HEL-863: bind tell-action polarity, state and object to the same promise claim - per manager independent review of 6859696: (1) clause_affirms_promise now enforces the full supported grammar in one pass - promise verb with non-negated prefix -> tell action whose connecting span is negation-free (答应不告诉我面试结果 is a promise NOT to tell, rejected) -> optional closed-class receiver pronoun -> correct object immediately following (告诉我体检结果而面试结果是另一件事 mis-binds the promise object, rejected); (2) clause_foreign_object structural class: a clause mentioning any other ...结果 object (体检结果) cannot support promise OR state affirmation for the interview promise - 体检结果现在已经告诉我了 no longer proves the interview promise closed; bare anaphor 结果已收到 and 面试结果已经告诉我了 still bind correctly; conservative fail for unparsable extra objects/predicates/negations, no per-sentence blacklists, no new model
Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
co-authored by
multica-agent
parent
6859696075
commit
fcda71e6a2
+126
-38
@@ -42,6 +42,19 @@ HEL-862 承诺声明绑定(总管对 a5ef23f 独立复核剩余缺口,限定
|
||||
- 已结/未结状态短语同样小句内肯定极性判定;固定合成语料领域内的
|
||||
受支持表达之外一律保守判失败,不外推、不新增模型。
|
||||
|
||||
HEL-863 动作极性与状态对象绑定(总管对 6859696 独立复核三组反例):
|
||||
- 告知动作极性同时核对:承诺动词与告知动作之间的间隔内含否定语素
|
||||
("答应周五晚不告诉我面试结果")→ 告知动作未被肯定 → 该小句不能证明
|
||||
"承诺告知"这一项声明,不再只查承诺动词前缀;
|
||||
- 对象紧随告知动作(中间仅允许接收者代词我/你等封闭类):"告诉我体检结果
|
||||
而面试结果是另一件事"中承诺对象错挂为体检结果,面试结果在告知动作之后
|
||||
更远处出现不再凑数;
|
||||
- 状态绑定回同一目标声明:状态短语所在小句若提及正确对象之外的结果对象
|
||||
("体检结果现在已经告诉我了"),该状态断言属于别的对象,不能证明面试
|
||||
承诺已结/未结;裸回指"结果已收到"与"面试结果已经告诉我了"仍可证明;
|
||||
- 同样限定受支持表达的完整语法,不靠整句黑名单或逐句禁词;不能按该语法
|
||||
解析的额外对象/谓语/否定一律保守判失败。
|
||||
|
||||
对照设计(批准范围第5条):
|
||||
现状路径 = docs/reuse-hel847/probe_recall.py 的生产语义重放(总管已核对的 v2 基线,
|
||||
词面 FTS trigram 检索 + fixture gist + 带门回读),不读写真实库;
|
||||
@@ -676,6 +689,12 @@ PROMISE_VERB_ZH = ["答应", "承诺", "许诺"]
|
||||
PROMISE_VERB_EN = ["promis", "pledge", "commit"]
|
||||
TELL_ACTION_ZH = ["告诉", "告知"]
|
||||
TELL_ACTION_EN = ["tell", "inform", "notify"]
|
||||
# HEL-863 告知动作后的接收者代词(封闭类):对象必须紧随告知动作(至多隔一个
|
||||
# 接收者代词),"告诉我面试结果"成立、"告诉我体检结果而面试结果是另一件事"
|
||||
# 的承诺对象错挂不成立。
|
||||
PROMISE_RECEIVER_ZH = ["我", "你", "他", "她", "它", "咱们", "我们", "你们",
|
||||
"他们", "她们", "它们"]
|
||||
PROMISE_RECEIVER_EN = ["me", "you", "us", "him", "her", "them"]
|
||||
# 否定语素(封闭语法类,非逐句黑名单):出现在承诺动词/状态短语之前的同小句
|
||||
# 前缀内,即该断言未被肯定——"未曾答应/从未答应/并未答应/并非已经告诉我"
|
||||
# 由结构拒绝,不靠不断枚举整句禁词。
|
||||
@@ -722,45 +741,105 @@ def _prefix_negated(clause: str, idx: int) -> bool:
|
||||
m in low for m in NEGATION_MORPHS_EN)
|
||||
|
||||
|
||||
def clause_affirms_promise(clause: str) -> bool:
|
||||
"""单小句内肯定承诺断言:承诺动词(前缀无否定)→ 告知动作 → 正确对象。
|
||||
# HEL-863 对象一致性(结构类,非逐句黑名单):正确对象是"面试结果";同一小句
|
||||
# 内出现其他"…结果"对象提及(如体检结果),该小句的断言(承诺或状态)就绑定
|
||||
# 在别的对象上,不能用来证明面试承诺的任何一部分。裸"结果"(回指,无修饰前
|
||||
# 缀)与"面试结果"本身不算外来对象。
|
||||
_FOREIGN_OBJECT_ZH = "结果"
|
||||
_FOREIGN_OBJECT_EN = "result"
|
||||
_CORRECT_OBJECT_ZH = PROMISE_OBJECT_ZH[0] # 面试结果
|
||||
_CORRECT_OBJECT_EN = "interview result"
|
||||
|
||||
对象必须落在承诺动词之后的告知动作之后(告诉我面试结果),把正确对象
|
||||
绑定进同一项承诺声明;对象在别的小句出现("面试结果是另一件事")不凑数。
|
||||
|
||||
def clause_foreign_object(clause: str) -> bool:
|
||||
"""小句是否提及正确对象之外的结果对象(体检结果等"…结果"形态提及)。"""
|
||||
for m in re.finditer(_FOREIGN_OBJECT_ZH, clause):
|
||||
k = m.start()
|
||||
if k >= len(_CORRECT_OBJECT_ZH) - len(_FOREIGN_OBJECT_ZH) and (
|
||||
clause[k - (len(_CORRECT_OBJECT_ZH) - len(_FOREIGN_OBJECT_ZH)):
|
||||
k + len(_FOREIGN_OBJECT_ZH)] == _CORRECT_OBJECT_ZH):
|
||||
continue
|
||||
if k == 0: # 裸回指"结果"(句首、无修饰前缀)
|
||||
continue
|
||||
return True
|
||||
low = clause.lower()
|
||||
_off_en = len(_CORRECT_OBJECT_EN) - len(_FOREIGN_OBJECT_EN)
|
||||
for m in re.finditer(_FOREIGN_OBJECT_EN, low):
|
||||
k = m.start()
|
||||
if k >= _off_en and low[k - _off_en:
|
||||
k + len(_FOREIGN_OBJECT_EN)] == _CORRECT_OBJECT_EN:
|
||||
continue
|
||||
if k == 0:
|
||||
continue
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def clause_affirms_promise(clause: str) -> bool:
|
||||
"""单小句内肯定承诺断言(HEL-863 完整受支持语法,一次核对对象与两种动作
|
||||
极性):
|
||||
|
||||
承诺动词(前缀无否定语素)→ 告知动作(承诺动词至告知动作的间隔内无否定
|
||||
语素,即告知动作同样被肯定)→ 可选接收者代词 → 正确对象(面试结果)紧随
|
||||
其后;且该小句不提及其他"…结果"对象。"答应周五晚不告诉我面试结果"是否定
|
||||
告知(另一种承诺,非承诺告知),"告诉我体检结果而面试结果是另一件事"承诺
|
||||
对象错挂,均不能证明"承诺告知面试结果"这一项声明。
|
||||
"""
|
||||
if clause_foreign_object(clause):
|
||||
return False
|
||||
for verb in PROMISE_VERB_ZH:
|
||||
start = 0
|
||||
while (i := clause.find(verb, start)) >= 0:
|
||||
if not _prefix_negated(clause, i):
|
||||
after = clause[i + len(verb):]
|
||||
after_verb = clause[i + len(verb):]
|
||||
for tell in TELL_ACTION_ZH:
|
||||
j = after.find(tell)
|
||||
if j >= 0 and any(
|
||||
obj in after[j + len(tell):]
|
||||
for obj in PROMISE_OBJECT_ZH):
|
||||
return True
|
||||
t = 0
|
||||
while (j := after_verb.find(tell, t)) >= 0:
|
||||
if not _prefix_negated(after_verb, j):
|
||||
after_tell = after_verb[j + len(tell):].lstrip()
|
||||
for recv in PROMISE_RECEIVER_ZH:
|
||||
if after_tell.startswith(recv):
|
||||
after_tell = after_tell[len(recv):].lstrip()
|
||||
break
|
||||
if any(after_tell.startswith(obj)
|
||||
for obj in PROMISE_OBJECT_ZH):
|
||||
return True
|
||||
t = j + 1
|
||||
start = i + 1
|
||||
low = clause.lower()
|
||||
for verb in PROMISE_VERB_EN:
|
||||
start = 0
|
||||
while (i := low.find(verb, start)) >= 0:
|
||||
if not _prefix_negated(clause, i):
|
||||
after = low[i + len(verb):]
|
||||
if not _prefix_negated(low, i):
|
||||
after_verb = low[i + len(verb):]
|
||||
for tell in TELL_ACTION_EN:
|
||||
j = after.find(tell)
|
||||
if j >= 0 and any(
|
||||
obj in after[j:] for obj in PROMISE_OBJECT_EN):
|
||||
return True
|
||||
t = 0
|
||||
while (j := after_verb.find(tell + " ", t)) >= 0:
|
||||
if not _prefix_negated(after_verb, j):
|
||||
after_tell = after_verb[j + len(tell):].lstrip()
|
||||
for recv in PROMISE_RECEIVER_EN:
|
||||
if after_tell.startswith(recv):
|
||||
after_tell = after_tell[len(recv):].lstrip()
|
||||
break
|
||||
for obj in PROMISE_OBJECT_EN:
|
||||
if after_tell.startswith(obj):
|
||||
return True
|
||||
t = j + 1
|
||||
start = i + 1
|
||||
return False
|
||||
|
||||
|
||||
def clause_affirms_state(clause: str, closed: bool) -> bool:
|
||||
"""单小句内肯定已结/未结状态:状态短语(预声明等价集)前缀无否定语素。
|
||||
"""单小句内肯定已结/未结状态且绑定回正确对象(HEL-863):
|
||||
|
||||
状态短语(预声明等价集)前缀无否定语素;且该小句不提及其他"…结果"对象
|
||||
——"体检结果现在已经告诉我了"的状态断言属于体检结果,不能证明面试承诺
|
||||
已结;裸回指"结果已收到"与"面试结果已经告诉我了"绑定正确对象可证明。
|
||||
"但并非已经告诉我"含已结短语但前缀被否定 → 未肯定;"还没有告诉我"/
|
||||
"尚未收到"是未结短语的肯定形态(短语本身表达未结,非否定已结)。
|
||||
"""
|
||||
if clause_foreign_object(clause):
|
||||
return False
|
||||
terms_zh = PROMISE_CLOSED_ZH if closed else PROMISE_OPEN_ZH
|
||||
terms_en = PROMISE_CLOSED_EN if closed else PROMISE_OPEN_EN
|
||||
low = clause.lower()
|
||||
@@ -785,13 +864,16 @@ def affirms_promise_state(text: str, *, closed: bool) -> bool:
|
||||
|
||||
|
||||
def affirms_promise_claim(text: str) -> bool:
|
||||
"""肯定陈述固定语料中的承诺(HEL-862 小句结构化判据):
|
||||
"""肯定陈述固定语料中的承诺(HEL-862/863 小句结构化判据):
|
||||
|
||||
存在单一小句按"承诺动词(前缀无否定语素)→ 告知动作 → 正确对象(面试
|
||||
结果)"依次绑定,且全文无不确定表述。对象词与动作词分处不同小句各出现
|
||||
一次不再凑数;承诺动词被否定(未曾答应/没有答应过)、仅陈述告知结果而无
|
||||
承诺断言(面试结果已经告诉我了)、答错对象(体检结果)均判 False;无法
|
||||
按受支持表达证明即保守判失败,不靠枚举整句禁词冒充肯定验证。
|
||||
存在单一小句按"承诺动词(前缀无否定语素)→ 告知动作(间隔内无否定语素,
|
||||
告知动作同样被肯定)→ 可选接收者代词 → 正确对象(面试结果)紧随其后"
|
||||
绑定,小句内无其他"…结果"对象提及,且全文无不确定表述。对象词与动作词
|
||||
分处不同小句各出现一次不再凑数;承诺动词被否定(未曾答应/没有答应过)、
|
||||
告知动作被否定(答应不告诉)、对象错挂(告诉我体检结果而面试结果是另一
|
||||
件事)、仅陈述告知结果而无承诺断言(面试结果已经告诉我了)、答错对象
|
||||
(体检结果)均判 False;无法按受支持表达证明即保守判失败,不靠枚举整句
|
||||
禁词冒充肯定验证。
|
||||
"""
|
||||
return bool(any(clause_affirms_promise(c) for c in iter_clauses(text))
|
||||
and not has_any(text, PROMISE_UNCERTAIN_ZH, PROMISE_UNCERTAIN_EN))
|
||||
@@ -895,16 +977,19 @@ def s3_result(*, b_hits3: list, b_hits3b: list, m_hits3: list, m_hits3b: list,
|
||||
|
||||
|
||||
def s6_result(orch: dict, a_resolved: dict, a_unresolved: dict) -> dict:
|
||||
"""S6 判据(HEL-862:小句结构化绑定承诺对象/动作/极性与状态):编排层
|
||||
(已知id置 resolved 留痕 + 未结对照块自洽)+ 状态进上下文层(正确对象的
|
||||
resolved 状态实际写入带门装配块)+ 模型使用层(两侧请求成功;已结侧必须
|
||||
在单一小句内按"承诺动词(前缀无否定)→告知动作→面试结果"肯定陈述同一项
|
||||
承诺,且状态短语(已经告诉我等)所在小句前缀无否定语素并肯定为已告知;
|
||||
未结侧同一绑定且肯定为尚未告知——对象与动作分处不同小句各出现一次
|
||||
(体检承诺+面试另一件事)、承诺被否定(未曾答应)、状态被否定(并非已经
|
||||
告诉我)、仅陈述告知而无承诺断言(面试结果已经告诉我了)、"不知道是否"、
|
||||
两侧同答不区分状态、已结侧答未结、未结侧同义宣称已收到均判失败,不以字面
|
||||
提到'告诉/答应'冒充状态正确;无法按受支持表达证明即保守判失败)。"""
|
||||
"""S6 判据(HEL-863:小句结构化绑定承诺对象/两种动作极性/状态与目标声明):
|
||||
编排层(已知id置 resolved 留痕 + 未结对照块自洽)+ 状态进上下文层(正确对象
|
||||
的 resolved 状态实际写入带门装配块)+ 模型使用层(两侧请求成功;已结侧必须
|
||||
在单一小句内按"承诺动词(前缀无否定)→告知动作(间隔内无否定)→(可选
|
||||
接收者)→面试结果紧随其后"肯定陈述同一项承诺,小句内无其他"…结果"对象,
|
||||
且状态短语(已经告诉我等)所在小句前缀无否定语素、同样不提及其他"…结果"
|
||||
对象并肯定为已告知;未结侧同一绑定且肯定为尚未告知——对象与动作分处不同
|
||||
小句各出现一次(体检承诺+面试另一件事)、告知动作被否定(答应不告诉)、
|
||||
状态属于别的对象(体检结果已经告诉我了)、承诺被否定(未曾答应)、状态被
|
||||
否定(并非已经告诉我)、仅陈述告知而无承诺断言(面试结果已经告诉我了)、
|
||||
"不知道是否"、两侧同答不区分状态、已结侧答未结、未结侧同义宣称已收到均判
|
||||
失败,不以字面提到'告诉/答应'冒充状态正确;无法按受支持表达证明即保守判
|
||||
失败)。"""
|
||||
ans_r = (a_resolved or {}).get("answer") or ""
|
||||
ans_u = (a_unresolved or {}).get("answer") or ""
|
||||
ok_r = bool((a_resolved or {}).get("ok"))
|
||||
@@ -945,11 +1030,14 @@ def s6_result(orch: dict, a_resolved: dict, a_unresolved: dict) -> dict:
|
||||
"expect": "已知id编排置 resolved 留痕且未结对照块剔除矛盾条目后自洽;"
|
||||
"正确对象的状态实际写入带门装配块;模型沿状态区分且必须"
|
||||
"肯定验证同一项声明:已结侧单一小句内'承诺动词(未被否定)→"
|
||||
"告知动作→面试结果'绑定成立且已结状态短语未被否定并肯定为"
|
||||
"已告知,未结侧同一绑定且肯定为尚未告知;对象与动作串错/"
|
||||
"未曾答应/并非已经告诉我/仅陈述告知无承诺/不知道/是否/"
|
||||
"两侧同答/已结侧答未结/未结侧宣称已收到均判失败(无法按"
|
||||
"受支持表达证明即保守判失败,不以字面提到'告诉/答应'冒充)"}
|
||||
"告知动作(间隔内无否定、同样被肯定)→(可选接收者)→面试"
|
||||
"结果紧随其后'绑定成立,小句内无其他'…结果'对象,已结状态"
|
||||
"短语所在小句前缀无否定、不提及其他'…结果'对象并肯定为已"
|
||||
"告知;未结侧同一绑定且肯定为尚未告知;对象与动作串错/答应"
|
||||
"不告诉/状态属于体检结果等其他对象/未曾答应/并非已经告诉我/"
|
||||
"仅陈述告知无承诺/不知道/是否/两侧同答/已结侧答未结/未结侧"
|
||||
"宣称已收到均判失败(无法按受支持表达证明即保守判失败,不以"
|
||||
"字面提到'告诉/答应'冒充)"}
|
||||
|
||||
|
||||
# HEL-860:无门 assemble_block 出口已删除——全部问答最终装配一律经
|
||||
|
||||
@@ -0,0 +1,279 @@
|
||||
#!/usr/bin/env python3
|
||||
"""HEL-863 负例复现脚本:总管独立复核《HEL-862-独立复核.md》S6 三组新增反例。
|
||||
|
||||
零真实模型、零网络(socket 硬拒绝 + HF 离线)、零本地嵌入:S6 判据为纯函数
|
||||
代入(真实 s6_result 构造器同一入口);既有判据不回归部分直接复用 audit861
|
||||
的两组纯函数节(S6 全套正负例 + S2/S3 总判据)与 audit862 的四组复核用例。
|
||||
同一脚本在修复前(6859696)与修复后代码上均可运行:
|
||||
|
||||
修复前(--phase prefix,在 6859696 工作树上运行)→ 总管复核三组新增反例
|
||||
(否定告知动作仍过 / 状态属于其他对象仍过 / 同小句对象串错仍过)全部
|
||||
pass=true,正确面试承诺已结/未结正例仍 pass=true;HEL-862 四组反例在
|
||||
6859696 已判失败(该修复是本卡起点,不回归)
|
||||
修复后(默认 postfix)→ 同一真实 s6_result 三组全部判失败,正确正例、
|
||||
audit861 既有正例(已结/未结/未结同义"还没有收到"/audit860/rework 形态)
|
||||
继续通过;audit862 四组、audit861 既有负例与 S2/S3 总判据不回归
|
||||
|
||||
补正口径(固定合成语料领域内,限定受支持表达的完整语法):一次核对同一项
|
||||
声明的正确对象、两种动作极性(承诺动词与告知动作均须肯定)、目标状态绑定
|
||||
(状态短语所在小句不得提及其他"…结果"对象)与冲突陈述——单一小句内"承诺
|
||||
动词(前缀无否定语素)→告知动作(间隔内无否定语素)→可选接收者代词→面试
|
||||
结果紧随其后";不靠逐句禁词或整句黑名单,无法证明则保守判失败。不新增
|
||||
模型、不做通用语义引擎。
|
||||
|
||||
退出码真实依赖正负例:除全部用例满足预期外,还对每个用例做翻转自检
|
||||
(flip 任一用例的实际结果,用例总判必翻转);任一翻转不改变结论则退出 1。
|
||||
|
||||
用法:python -m spike.hel857.audit863 [--phase prefix|postfix] [输出json路径]
|
||||
退出码:prefix=0 当且仅当三组新增反例全部复现(pass=true)且其余用例维持
|
||||
预期;postfix=0 当且仅当三组反例全部判失败、正例保留、既有判据不回归、
|
||||
权威账与既有证据只读未动、翻转自检全部通过。权威嵌入账只读快照核对
|
||||
(不补种不回退不双扣)。
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import socket
|
||||
import sqlite3
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
REPO = Path(__file__).resolve().parents[2]
|
||||
LEDGER = Path("/home/xiaomo/multica_workspaces/hello-af23464d222e/"
|
||||
"hel-859-1f079e50f551/workdir/audit-data/embed_ledger.sqlite3")
|
||||
os.environ["HEL857_EMBED_LEDGER"] = str(LEDGER)
|
||||
os.environ.setdefault("HF_HUB_OFFLINE", "1")
|
||||
os.environ.setdefault("TRANSFORMERS_OFFLINE", "1")
|
||||
|
||||
|
||||
def deny_network(*a, **k):
|
||||
raise RuntimeError("audit863 blocks network")
|
||||
|
||||
|
||||
socket.socket.connect = deny_network
|
||||
socket.socket.connect_ex = deny_network
|
||||
|
||||
from . import ab_scenarios as A
|
||||
from .audit861 import section_s2_s3_no_regression, section_s6_affirmative
|
||||
from .audit862 import (HELD_S2S3_EXPECT, HELD_S6_EXPECT, ORCH6,
|
||||
REVIEW_CASES, REVIEW_NEGATIVE_NAMES)
|
||||
|
||||
OUT = {"real_network_allowed": False, "real_model_requests": 0,
|
||||
"local_embeddings": 0}
|
||||
PHASE = "postfix"
|
||||
|
||||
|
||||
def ledger_snapshot():
|
||||
with sqlite3.connect(f"file:{LEDGER}?mode=ro", uri=True) as c:
|
||||
return {"counts": dict(c.execute("select kind,sum(items) from embed_ops "
|
||||
"group by kind")),
|
||||
"rows": c.execute("select count(*) from embed_ops").fetchone()[0]}
|
||||
|
||||
|
||||
PRESERVED_EVIDENCE = [
|
||||
"ab_scenarios_run1.json", "ab_scenarios.json", "rework858_offline.json",
|
||||
"rework859_offline.json", "rework860_offline.json", "audit859_prefix.json",
|
||||
"audit859_postfix.json", "audit859_reuse860.json", "audit860_prefix.json",
|
||||
"audit860_postfix.json", "audit861_prefix.json", "audit861_postfix.json",
|
||||
"audit861_reuse862.json", "audit862_prefix.json", "audit862_postfix.json"]
|
||||
|
||||
|
||||
def evidence_hashes():
|
||||
res = {}
|
||||
for name in PRESERVED_EVIDENCE:
|
||||
p = REPO / "spike/hel857/results" / name
|
||||
res[name] = (hashlib.sha256(p.read_bytes()).hexdigest()
|
||||
if p.exists() else "missing")
|
||||
return res
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 总管《HEL-862-独立复核.md》三组新增反例(已结侧原文逐字;未结侧均为正确
|
||||
# 未结侧),同一真实 s6_result 入口。
|
||||
def s6_answer(s):
|
||||
return {"answer": s, "ok": True}
|
||||
|
||||
|
||||
ADDITIONAL_GOOD_OPEN = "你答应周五晚告诉我面试结果,还没有告诉我。"
|
||||
ADDITIONAL_NEGATIVE_NAMES = (
|
||||
"negated_tell_action", "state_bound_to_medical_result",
|
||||
"object_bound_to_other_action_same_clause")
|
||||
ADDITIONAL_CASES = {
|
||||
"negated_tell_action":
|
||||
"你答应周五晚不告诉我面试结果,现在已经告诉我了。",
|
||||
"state_bound_to_medical_result":
|
||||
"你答应周五晚告诉我面试结果,体检结果现在已经告诉我了。",
|
||||
"object_bound_to_other_action_same_clause":
|
||||
"你答应周五晚告诉我体检结果而面试结果是另一件事,现在已经告诉我了。",
|
||||
}
|
||||
|
||||
|
||||
def additional_expected(name: str) -> bool:
|
||||
"""三组新增反例:prefix 期预期 pass=true(复现),postfix 期预期判失败。"""
|
||||
if name in ADDITIONAL_NEGATIVE_NAMES:
|
||||
return PHASE == "prefix"
|
||||
return True
|
||||
|
||||
|
||||
def section_additional_three() -> dict:
|
||||
out = {}
|
||||
for name, closed in ADDITIONAL_CASES.items():
|
||||
result = A.s6_result(ORCH6, s6_answer(closed), s6_answer(
|
||||
ADDITIONAL_GOOD_OPEN))
|
||||
mu = result["layers"]["model_use"]
|
||||
out[name] = {
|
||||
"resolved_input": closed, "open_input": ADDITIONAL_GOOD_OPEN,
|
||||
"expected_pass": additional_expected(name),
|
||||
"actual_pass": result["pass"],
|
||||
"layers": {
|
||||
"resolved_states_promise": mu["resolved_states_promise"],
|
||||
"resolved_affirms_closed": mu["resolved_affirms_closed"],
|
||||
"unresolved_states_promise": mu["unresolved_states_promise"],
|
||||
"unresolved_affirms_open": mu["unresolved_affirms_open"],
|
||||
},
|
||||
"result": result,
|
||||
}
|
||||
return out
|
||||
|
||||
|
||||
def section_review_four_groups() -> dict:
|
||||
"""HEL-862 四组复核用例不回归(6859696 起点已判失败,预期极性固定)。"""
|
||||
out = {}
|
||||
for name, (closed, opened) in REVIEW_CASES.items():
|
||||
result = A.s6_result(ORCH6, s6_answer(closed), s6_answer(opened))
|
||||
out[name] = {
|
||||
"resolved_input": closed, "open_input": opened,
|
||||
"expected_pass": name not in REVIEW_NEGATIVE_NAMES,
|
||||
"actual_pass": result["pass"],
|
||||
"result": result,
|
||||
}
|
||||
return out
|
||||
|
||||
|
||||
def _case_table(additional: dict, review: dict, held_s6: dict,
|
||||
held_s2s3: dict) -> dict:
|
||||
"""全部用例的实际 pass 与预期极性(退出码公式输入)。"""
|
||||
table: dict[str, bool] = {}
|
||||
expect: dict[str, bool] = {}
|
||||
for name, c in additional.items():
|
||||
key = f"additional.{name}"
|
||||
table[key] = bool(c["actual_pass"])
|
||||
expect[key] = bool(c["expected_pass"])
|
||||
for name, c in review.items():
|
||||
key = f"review.{name}"
|
||||
table[key] = bool(c["actual_pass"])
|
||||
expect[key] = bool(c["expected_pass"])
|
||||
for name, res in held_s6.items():
|
||||
key = f"held_s6.{name}"
|
||||
table[key] = bool(res["pass"])
|
||||
expect[key] = HELD_S6_EXPECT[name]
|
||||
for name, res in held_s2s3.items():
|
||||
key = f"held_s2s3.{name}"
|
||||
table[key] = bool(res["pass"])
|
||||
expect[key] = HELD_S2S3_EXPECT[name]
|
||||
return table, expect
|
||||
|
||||
|
||||
def _all_cases_match(table: dict, expect: dict) -> bool:
|
||||
return all(table[k] == expect[k] for k in expect)
|
||||
|
||||
|
||||
def _flip_dependence(table: dict, expect: dict) -> dict:
|
||||
"""翻转自检:任一用例实际值翻转必须改变用例总判(退出码真实依赖正负例)。"""
|
||||
base = _all_cases_match(table, expect)
|
||||
flips = {}
|
||||
for k in expect:
|
||||
flipped = dict(table)
|
||||
flipped[k] = not flipped[k]
|
||||
flips[k] = _all_cases_match(flipped, expect) != base
|
||||
return {"base_all_match": base, "every_flip_changes_verdict": all(
|
||||
flips.values()), "per_case": flips}
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
def main(argv: list[str]) -> dict:
|
||||
global PHASE
|
||||
argv = list(argv)
|
||||
rest: list[str] = []
|
||||
PHASE = "postfix"
|
||||
it = iter(argv[1:])
|
||||
for tok in it:
|
||||
if tok == "--phase":
|
||||
PHASE = next(it, "postfix")
|
||||
elif tok.startswith("--phase="):
|
||||
PHASE = tok.split("=", 1)[1]
|
||||
elif not tok.startswith("--"):
|
||||
rest.append(tok)
|
||||
args = rest
|
||||
OUT["phase"] = PHASE
|
||||
|
||||
commit = subprocess.run(["git", "rev-parse", "--short", "HEAD"],
|
||||
capture_output=True, text=True,
|
||||
cwd=str(REPO)).stdout.strip()
|
||||
OUT["commit"] = commit
|
||||
OUT["ledger_before"] = ledger_snapshot()
|
||||
OUT["preserved_evidence_before"] = evidence_hashes()
|
||||
|
||||
OUT["additional_three_groups"] = section_additional_three()
|
||||
OUT["review_four_groups"] = section_review_four_groups()
|
||||
held_s6 = section_s6_affirmative()
|
||||
held_s2s3 = section_s2_s3_no_regression()
|
||||
OUT["held_s6_affirmative"] = held_s6
|
||||
OUT["held_s2_s3_no_regression"] = held_s2s3
|
||||
|
||||
table, expect = _case_table(OUT["additional_three_groups"],
|
||||
OUT["review_four_groups"], held_s6, held_s2s3)
|
||||
OUT["case_expect"] = {k: bool(v) for k, v in expect.items()}
|
||||
OUT["case_actual"] = {k: bool(v) for k, v in table.items()}
|
||||
OUT["all_cases_match_expectation"] = _all_cases_match(table, expect)
|
||||
OUT["flip_dependence"] = _flip_dependence(table, expect)
|
||||
|
||||
OUT["ledger_after"] = ledger_snapshot()
|
||||
OUT["ledger_unchanged"] = OUT["ledger_before"] == OUT["ledger_after"]
|
||||
OUT["preserved_evidence_after"] = evidence_hashes()
|
||||
OUT["preserved_evidence_unchanged"] = (
|
||||
OUT["preserved_evidence_before"] == OUT["preserved_evidence_after"])
|
||||
|
||||
a3 = OUT["additional_three_groups"]
|
||||
# ---- prefix:三组新增反例全部复现(pass=true)----
|
||||
three_reproduced = all(a3[n]["actual_pass"]
|
||||
for n in ADDITIONAL_NEGATIVE_NAMES)
|
||||
# ---- postfix:三组新增反例全部判失败 ----
|
||||
three_rejected = not any(a3[n]["actual_pass"]
|
||||
for n in ADDITIONAL_NEGATIVE_NAMES)
|
||||
OUT["summary"] = {
|
||||
"phase": PHASE,
|
||||
"three_groups_reproduced": three_reproduced,
|
||||
"three_groups_rejected": three_rejected,
|
||||
"all_cases_match_expectation": OUT["all_cases_match_expectation"],
|
||||
"flip_dependence_holds": OUT["flip_dependence"][
|
||||
"every_flip_changes_verdict"],
|
||||
"ledger_unchanged": OUT["ledger_unchanged"],
|
||||
"preserved_evidence_unchanged": OUT["preserved_evidence_unchanged"],
|
||||
"pass": (three_reproduced and PHASE == "prefix"
|
||||
and OUT["all_cases_match_expectation"]
|
||||
and OUT["flip_dependence"]["every_flip_changes_verdict"]
|
||||
and OUT["ledger_unchanged"]
|
||||
and OUT["preserved_evidence_unchanged"])
|
||||
or (three_rejected and PHASE == "postfix"
|
||||
and OUT["all_cases_match_expectation"]
|
||||
and OUT["flip_dependence"]["every_flip_changes_verdict"]
|
||||
and OUT["ledger_unchanged"]
|
||||
and OUT["preserved_evidence_unchanged"]),
|
||||
}
|
||||
out_path = Path(args[0]) if args else (
|
||||
REPO / "spike/hel857/results/"
|
||||
f"audit863_{'prefix' if PHASE == 'prefix' else 'postfix'}.json")
|
||||
out_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
out_path.write_text(json.dumps(OUT, ensure_ascii=False, indent=2),
|
||||
encoding="utf-8")
|
||||
print(json.dumps(OUT["summary"] | {"out": str(out_path)},
|
||||
ensure_ascii=False))
|
||||
return OUT
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(0 if main(sys.argv)["summary"]["pass"] else 1)
|
||||
Reference in New Issue
Block a user