修复: 闭合正文候选丢弃与交接合同
This commit is contained in:
parent
efb2b3e781
commit
de0f870b96
@ -299,11 +299,27 @@ def check_writer_acceptance(
|
|||||||
if confirmed is not True:
|
if confirmed is not True:
|
||||||
raise AcceptanceError("CONFIRMATION_REQUIRED", f"{decision} 必须由用户明确确认")
|
raise AcceptanceError("CONFIRMATION_REQUIRED", f"{decision} 必须由用户明确确认")
|
||||||
if decision == "discard":
|
if decision == "discard":
|
||||||
|
try:
|
||||||
|
normalized_context = validate_writer_context(context)
|
||||||
|
except ContractError as exc:
|
||||||
|
raise AcceptanceError("CONTEXT_CONTRACT_INVALID", str(exc)) from exc
|
||||||
|
try:
|
||||||
|
normalized_candidate = validate_writer_output(candidate)
|
||||||
|
except ContractError as exc:
|
||||||
|
raise AcceptanceError("CANDIDATE_CONTRACT_INVALID", str(exc)) from exc
|
||||||
|
_validate_context_binding(normalized_context, normalized_candidate)
|
||||||
return {
|
return {
|
||||||
"schemaVersion": "writer-acceptance-result-v1",
|
"schemaVersion": "writer-acceptance-result-v1",
|
||||||
"status": "DISCARD_INTENT_READY",
|
"status": "DISCARD_INTENT_READY",
|
||||||
"decision": decision,
|
"decision": decision,
|
||||||
"commandIntent": None,
|
"runId": normalized_candidate["runId"],
|
||||||
|
"candidateVersion": normalized_candidate["candidateVersion"],
|
||||||
|
"candidateSha256": normalized_candidate["candidateSha256"],
|
||||||
|
"commandIntent": {
|
||||||
|
"command": "close_shadow_candidate",
|
||||||
|
"expectedCandidateVersion": normalized_candidate["candidateVersion"],
|
||||||
|
"expectedCandidateSha256": normalized_candidate["candidateSha256"],
|
||||||
|
},
|
||||||
"canonicalMutationPerformed": False,
|
"canonicalMutationPerformed": False,
|
||||||
}
|
}
|
||||||
shadow = check_shadow_ready(
|
shadow = check_shadow_ready(
|
||||||
|
|||||||
@ -269,7 +269,7 @@ def _terminal_result(
|
|||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
"""生成可原子落盘的稳定终态结果。"""
|
"""生成可原子落盘的稳定终态结果。"""
|
||||||
|
|
||||||
return {
|
result = {
|
||||||
"schemaVersion": "writer-pipeline-result-v1",
|
"schemaVersion": "writer-pipeline-result-v1",
|
||||||
"runId": run_id,
|
"runId": run_id,
|
||||||
"status": status,
|
"status": status,
|
||||||
@ -281,6 +281,10 @@ def _terminal_result(
|
|||||||
"rewriteCount": rewrite_count,
|
"rewriteCount": rewrite_count,
|
||||||
"trace": [dict(item) for item in trace],
|
"trace": [dict(item) for item in trace],
|
||||||
}
|
}
|
||||||
|
# 只有通过全部合同和审查门的终态才暴露可进入 Shadow 的最终候选。
|
||||||
|
if status == "PASSED" and candidate is not None:
|
||||||
|
result["candidateArtifact"] = dict(candidate)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
def _publish_if_requested(
|
def _publish_if_requested(
|
||||||
@ -625,6 +629,9 @@ def run_writer_pipeline(
|
|||||||
"mechanicalPassed": mechanical_report.get("passed", False),
|
"mechanicalPassed": mechanical_report.get("passed", False),
|
||||||
"semanticStatus": semantic_report.get("status") if semantic_report else "not_run",
|
"semanticStatus": semantic_report.get("status") if semantic_report else "not_run",
|
||||||
"failureCodes": [item.get("code") for item in candidate_failures],
|
"failureCodes": [item.get("code") for item in candidate_failures],
|
||||||
|
# 保留接受层审计所需的原始报告;摘要字段仍供稳定绑定检查使用。
|
||||||
|
"mechanicalReport": dict(mechanical_report),
|
||||||
|
"semanticReport": dict(semantic_report) if semantic_report else None,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
if not candidate_failures:
|
if not candidate_failures:
|
||||||
|
|||||||
@ -95,6 +95,7 @@ class RunWriterPipelineTest(unittest.TestCase):
|
|||||||
self.assertEqual(store.latest(context["runId"]).state, "REJECTED")
|
self.assertEqual(store.latest(context["runId"]).state, "REJECTED")
|
||||||
self.assertEqual(raised.exception.result["status"], "REJECTED")
|
self.assertEqual(raised.exception.result["status"], "REJECTED")
|
||||||
self.assertEqual(raised.exception.result["failureCode"], expected_code)
|
self.assertEqual(raised.exception.result["failureCode"], expected_code)
|
||||||
|
self.assertNotIn("candidateArtifact", raised.exception.result)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
json.loads(result_path.read_text(encoding="utf-8")),
|
json.loads(result_path.read_text(encoding="utf-8")),
|
||||||
raised.exception.result,
|
raised.exception.result,
|
||||||
@ -442,6 +443,11 @@ class RunWriterPipelineTest(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(result["status"], "PASSED")
|
self.assertEqual(result["status"], "PASSED")
|
||||||
|
self.assertEqual(result["candidateArtifact"]["candidateVersion"], 2)
|
||||||
|
self.assertEqual(
|
||||||
|
result["candidateArtifact"]["candidateSha256"],
|
||||||
|
result["candidateSha256"],
|
||||||
|
)
|
||||||
self.assertEqual(result["evidenceRequestCount"], 1)
|
self.assertEqual(result["evidenceRequestCount"], 1)
|
||||||
self.assertEqual(result["rewriteCount"], 1)
|
self.assertEqual(result["rewriteCount"], 1)
|
||||||
self.assertEqual(calls, [(1, 1), (2, 2)])
|
self.assertEqual(calls, [(1, 1), (2, 2)])
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user