From de0f870b96aa3d3c44542bb3953b3f4eab54d440 Mon Sep 17 00:00:00 2001 From: zizi Date: Mon, 20 Jul 2026 20:59:41 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D:=20=E9=97=AD=E5=90=88?= =?UTF-8?q?=E6=AD=A3=E6=96=87=E5=80=99=E9=80=89=E4=B8=A2=E5=BC=83=E4=B8=8E?= =?UTF-8?q?=E4=BA=A4=E6=8E=A5=E5=90=88=E5=90=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../confirm/scripts/check_writer_acceptance.py | 18 +++++++++++++++++- .../scripts/run_writer_pipeline.py | 9 ++++++++- .../scripts/test_run_writer_pipeline.py | 6 ++++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/.claude/skills/confirm/scripts/check_writer_acceptance.py b/.claude/skills/confirm/scripts/check_writer_acceptance.py index 3250fe7..09bdff8 100644 --- a/.claude/skills/confirm/scripts/check_writer_acceptance.py +++ b/.claude/skills/confirm/scripts/check_writer_acceptance.py @@ -299,11 +299,27 @@ def check_writer_acceptance( if confirmed is not True: raise AcceptanceError("CONFIRMATION_REQUIRED", f"{decision} 必须由用户明确确认") 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 { "schemaVersion": "writer-acceptance-result-v1", "status": "DISCARD_INTENT_READY", "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, } shadow = check_shadow_ready( diff --git a/.claude/skills/continuation/scripts/run_writer_pipeline.py b/.claude/skills/continuation/scripts/run_writer_pipeline.py index 517f8c9..1604c89 100644 --- a/.claude/skills/continuation/scripts/run_writer_pipeline.py +++ b/.claude/skills/continuation/scripts/run_writer_pipeline.py @@ -269,7 +269,7 @@ def _terminal_result( ) -> dict[str, Any]: """生成可原子落盘的稳定终态结果。""" - return { + result = { "schemaVersion": "writer-pipeline-result-v1", "runId": run_id, "status": status, @@ -281,6 +281,10 @@ def _terminal_result( "rewriteCount": rewrite_count, "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( @@ -625,6 +629,9 @@ def run_writer_pipeline( "mechanicalPassed": mechanical_report.get("passed", False), "semanticStatus": semantic_report.get("status") if semantic_report else "not_run", "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: diff --git a/.claude/skills/continuation/scripts/test_run_writer_pipeline.py b/.claude/skills/continuation/scripts/test_run_writer_pipeline.py index 1b27260..14b7304 100644 --- a/.claude/skills/continuation/scripts/test_run_writer_pipeline.py +++ b/.claude/skills/continuation/scripts/test_run_writer_pipeline.py @@ -95,6 +95,7 @@ class RunWriterPipelineTest(unittest.TestCase): self.assertEqual(store.latest(context["runId"]).state, "REJECTED") self.assertEqual(raised.exception.result["status"], "REJECTED") self.assertEqual(raised.exception.result["failureCode"], expected_code) + self.assertNotIn("candidateArtifact", raised.exception.result) self.assertEqual( json.loads(result_path.read_text(encoding="utf-8")), raised.exception.result, @@ -442,6 +443,11 @@ class RunWriterPipelineTest(unittest.TestCase): ) 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["rewriteCount"], 1) self.assertEqual(calls, [(1, 1), (2, 2)])