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:
zonggong
2026-10-26 02:03:12 +08:00
co-authored by multica-agent
parent 6859696075
commit fcda71e6a2
2 changed files with 405 additions and 38 deletions
+126 -38
View File
@@ -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 出口已删除——全部问答最终装配一律经
+279
View File
@@ -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)