From 77f0e2886d6a4bbeb9bc1451cb34b5a1e7e988c4 Mon Sep 17 00:00:00 2001 From: Abiorh001 Date: Sun, 26 Jul 2026 15:21:07 +0100 Subject: [PATCH 1/6] fix(agent-loop): reconcile root recovery evidence --- .../STATUS.md | 4 +- ...1-02-exact-root-reconciliation-recovery.md | 74 ++++++++++++++ .../WS-ENG-ROOT-001-02-pr-trust-bundle.md | 26 +++++ .../merge-intents/WS-ENG-ROOT-001-02.json | 9 ++ .../policies/loop-memory-recovery.json | 13 ++- scripts/check_loop_memory_state.py | 30 ++++++ scripts/test_agent_gates.py | 13 ++- scripts/test_update_post_merge_memory.py | 32 +++++- scripts/update_post_merge_memory.py | 97 +++++++++++++++++-- 9 files changed, 282 insertions(+), 16 deletions(-) create mode 100644 .agent-loop/initiatives/WS-ENG-ROOT-001-planning-intake-gate-recovery/chunks/WS-ENG-ROOT-001-02-exact-root-reconciliation-recovery.md create mode 100644 .agent-loop/initiatives/WS-ENG-ROOT-001-planning-intake-gate-recovery/reviews/WS-ENG-ROOT-001-02-pr-trust-bundle.md create mode 100644 .agent-loop/merge-intents/WS-ENG-ROOT-001-02.json diff --git a/.agent-loop/initiatives/WS-ENG-ROOT-001-planning-intake-gate-recovery/STATUS.md b/.agent-loop/initiatives/WS-ENG-ROOT-001-planning-intake-gate-recovery/STATUS.md index ee385ce69..dccc832f7 100644 --- a/.agent-loop/initiatives/WS-ENG-ROOT-001-planning-intake-gate-recovery/STATUS.md +++ b/.agent-loop/initiatives/WS-ENG-ROOT-001-planning-intake-gate-recovery/STATUS.md @@ -3,7 +3,7 @@ - Phase: one-use root recovery - Active planning chunk: none - Active implementation chunk: none -- Recovery target: `WS-ENG-ROOT-001-01` +- Recovery target: `WS-ENG-ROOT-001-02` - Signed basis and first parent: `339248c40020658583bf7bd1e4a58daf85f5ffb8` -- Purpose: restore the already-documented closed first-planning-intake admission +- Purpose: reconcile PR #205 after preserving its schema-v7 recovery evidence - Stop: recovery certificate is consumed by this exact merge and cannot replay diff --git a/.agent-loop/initiatives/WS-ENG-ROOT-001-planning-intake-gate-recovery/chunks/WS-ENG-ROOT-001-02-exact-root-reconciliation-recovery.md b/.agent-loop/initiatives/WS-ENG-ROOT-001-planning-intake-gate-recovery/chunks/WS-ENG-ROOT-001-02-exact-root-reconciliation-recovery.md new file mode 100644 index 000000000..e1b6579ef --- /dev/null +++ b/.agent-loop/initiatives/WS-ENG-ROOT-001-planning-intake-gate-recovery/chunks/WS-ENG-ROOT-001-02-exact-root-reconciliation-recovery.md @@ -0,0 +1,74 @@ +# Chunk Contract: WS-ENG-ROOT-001-02 — Exact Root Reconciliation Recovery + +## Goal + +Preserve the reviewed schema-v7 recovery policy while shared reconciliation +collects PR #205, then consume PR #205 and this repair as one exact sequence. + +## Risk class + +L0 + +## Start phase + +`implementation` + +## Machine-checkable scope + +```chunk-scope-json +{ + "schema_version": 1, + "chunk_id": "WS-ENG-ROOT-001-02", + "phase": "implementation", + "risk_class": "L0", + "allowed_paths": [ + ".agent-loop/policies/loop-memory-recovery.json", + ".agent-loop/initiatives/WS-ENG-ROOT-001-planning-intake-gate-recovery/**", + ".agent-loop/merge-intents/WS-ENG-ROOT-001-02.json", + "scripts/check_loop_memory_state.py", + "scripts/update_post_merge_memory.py", + "scripts/test_agent_gates.py", + "scripts/test_check_loop_memory_state.py", + "scripts/test_update_post_merge_memory.py" + ], + "forbidden_paths": ["backend/**", "frontend/**", ".github/**"], + "required_reviewers": ["senior engineering", "qa/test", "security/auth", "product/ops", "architecture", "ci integrity", "docs", "reuse/dedup", "test delta"], + "verification_commands": ["agent-gate-tests", "loop-memory-state", "loop-memory-recovery-tests", "internal-review-evidence", "markdown-links", "stale-wording", "git-diff-check"] +} +``` + +## Acceptance criteria + +- [ ] `reconcile_to_main` recollects schema-v7/v8 recovery merges with the exact + immutable policy used during preparation. +- [ ] Schema v8 binds signed basis `339248c4`, PR #205 merge `ce512bdb`, this + activation identity, adjacency, exact paths, null successor, and fixed + recovery-only evidence. +- [ ] Both exemptions are consumed and neither persists in signed history. +- [ ] Mutation, replay, wrong-order, wrong-parent, and foreign-identity cases fail. +- [ ] No product, workflow, dependency, coverage, start, or authorization behavior changes. + +## Verification commands + +```bash +python3 scripts/test_agent_gates.py +PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 python3 -m pytest -q scripts/test_update_post_merge_memory.py scripts/test_check_loop_memory_state.py +python3 scripts/check_loop_memory_state.py +python3 scripts/check_internal_review_evidence.py +python3 scripts/check_markdown_links.py +python3 scripts/check_stale_workstream_wording.py +git diff --check origin/main...HEAD +``` + +## Required reviewers + +Senior engineering, QA/test, security/auth, product/ops, architecture, CI +integrity, docs, reuse/dedup, and test delta. + +## Human review focus + +Confirm the authority is exact, adjacent, one-use, and cannot admit a third merge. + +## Stop conditions + +Stop if the policy becomes reusable, paths widen, checks weaken, or product code changes. diff --git a/.agent-loop/initiatives/WS-ENG-ROOT-001-planning-intake-gate-recovery/reviews/WS-ENG-ROOT-001-02-pr-trust-bundle.md b/.agent-loop/initiatives/WS-ENG-ROOT-001-planning-intake-gate-recovery/reviews/WS-ENG-ROOT-001-02-pr-trust-bundle.md new file mode 100644 index 000000000..d3b9e8bfd --- /dev/null +++ b/.agent-loop/initiatives/WS-ENG-ROOT-001-planning-intake-gate-recovery/reviews/WS-ENG-ROOT-001-02-pr-trust-bundle.md @@ -0,0 +1,26 @@ +# PR Trust Bundle: WS-ENG-ROOT-001-02 + +## Intent and scope + +Fix only the post-merge recollection loss that prevented PR #205 reconciliation. +The closed schema-v8 policy names PR #205 as the sole recovered merge and this +chunk as the sole activation, with a null successor. + +## Design + +Recovery preparation and reconciliation use the same immutable target policy. +Both adjacent records receive recovery-only evidence, are admitted through the +ephemeral exemption inventory, and must be fully consumed. + +## Evidence + +Pending deterministic checks and required internal review evidence. + +## Risks and human focus + +Review the exact SHA/PR/chunk bindings, first-parent adjacency, independent +validator parity, mutation coverage, and absence of reusable authority. + +## Stop + +Human approval is required before push, PR, or merge action. diff --git a/.agent-loop/merge-intents/WS-ENG-ROOT-001-02.json b/.agent-loop/merge-intents/WS-ENG-ROOT-001-02.json new file mode 100644 index 000000000..98dbe2803 --- /dev/null +++ b/.agent-loop/merge-intents/WS-ENG-ROOT-001-02.json @@ -0,0 +1,9 @@ +{ + "schema_version": 2, + "initiative_id": "WS-ENG-ROOT-001", + "chunk_id": "WS-ENG-ROOT-001-02", + "chunk_title": "Exact Root Reconciliation Recovery", + "next_chunk_id": null, + "next_chunk_title": null, + "next_requires_explicit_start": true +} diff --git a/.agent-loop/policies/loop-memory-recovery.json b/.agent-loop/policies/loop-memory-recovery.json index 7ebe5c345..eb1349e9d 100644 --- a/.agent-loop/policies/loop-memory-recovery.json +++ b/.agent-loop/policies/loop-memory-recovery.json @@ -1,9 +1,16 @@ { "activation": { - "chunk_id": "WS-ENG-ROOT-001-01", + "chunk_id": "WS-ENG-ROOT-001-02", "initiative_id": "WS-ENG-ROOT-001" }, "signed_basis": "339248c40020658583bf7bd1e4a58daf85f5ffb8", - "recovered_merges": [], - "schema_version": 7 + "recovered_merges": [ + { + "initiative_id": "WS-ENG-ROOT-001", + "chunk_id": "WS-ENG-ROOT-001-01", + "pr_number": 205, + "merge_sha": "ce512bdb6ae47e94ae8067845531cacfc3378a85" + } + ], + "schema_version": 8 } diff --git a/scripts/check_loop_memory_state.py b/scripts/check_loop_memory_state.py index 246544cb8..5626d1d59 100644 --- a/scripts/check_loop_memory_state.py +++ b/scripts/check_loop_memory_state.py @@ -116,6 +116,11 @@ ROOT_RECOVERY_REASON = "planning-intake-gate-circularity" ROOT_RECOVERY_CODE = "exact-root-gate-repair-v1" ROOT_RECOVERY_CERTIFICATE_SHA256 = "32f75b9709e6b09b30e672cc9889a8754dad1428e0b57e06d6bd237ae6476e40" +ROOT_RECONCILE_CHUNK_ID = "WS-ENG-ROOT-001-02" +ROOT_RECONCILE_MERGE_SHA = "ce512bdb6ae47e94ae8067845531cacfc3378a85" +ROOT_RECONCILE_REASON = "root-recovery-reconciliation-circularity" +ROOT_RECONCILE_CODE = "exact-root-reconcile-repair-v1" +ROOT_RECONCILE_CERTIFICATE_SHA256 = "f7fbd8bca3ba731c1a6c51c953533906a92cd8ad719dadee88ef763a1f70cf56" ID_PATTERN = re.compile(r"^[A-Z][A-Z0-9]*(?:-[A-Z0-9]+)+$") SHA_PATTERN = re.compile(r"^[0-9a-f]{40}$") SHA256_PATTERN = re.compile(r"^[0-9a-f]{64}$") @@ -890,6 +895,31 @@ def _record_failures( or source.get("first_parent_sha") != ROOT_RECOVERY_SIGNED_BASIS ): failures.append(f"{label}: invalid root recovery evidence") + elif isinstance(recovery, dict) and recovery.get("policy_schema") == 8: + recovered = source.get("main_sha") == ROOT_RECONCILE_MERGE_SHA + chunk = ROOT_RECOVERY_CHUNK_ID if recovered else ROOT_RECONCILE_CHUNK_ID + parent = ROOT_RECOVERY_SIGNED_BASIS if recovered else ROOT_RECONCILE_MERGE_SHA + expected_recovery = { + "merge_sha": source.get("main_sha"), "head_sha": source.get("head_sha"), + "chunk_id": chunk, "pr_number": source.get("pr_number"), + "policy_schema": 8, "signed_basis": ROOT_RECOVERY_SIGNED_BASIS, + "activation_chunk_id": ROOT_RECONCILE_CHUNK_ID, + "certificate_sha256": ROOT_RECONCILE_CERTIFICATE_SHA256, + "reason": ROOT_RECONCILE_REASON, "code": ROOT_RECONCILE_CODE, + } + completed = record.get("completed_chunk") + if ( + recovery != expected_recovery + or protected.get("sha256") != hashlib.sha256(json.dumps(expected_recovery, sort_keys=True, separators=(",", ":")).encode()).hexdigest() + or not isinstance(completed, dict) + or completed.get("initiative_id") != "WS-ENG-ROOT-001" + or completed.get("chunk_id") != chunk + or completed.get("next_chunk_id") is not None + or completed.get("next_chunk_title") is not None + or source.get("intent_path") != f".agent-loop/merge-intents/{chunk}.json" + or source.get("first_parent_sha") != parent + ): + failures.append(f"{label}: invalid root reconciliation evidence") else: expected_recovery = {"merge_sha": "d3321698fb856f3fac320cdc7bc598f813fe1953", "head_sha": R3_HISTORICAL_HEAD_SHA, "chunk_id": "WS-ENG-007-00R2", "pr_number": 189, "policy_schema": 4, "signed_basis": "73b457925b02301587b83d01ced0adb66319d134", "activation_chunk_id": "WS-ENG-007-00R3", "certificate_sha256": R3_RECOVERY_CERTIFICATE_SHA256, "reason": "no-completed-pre-merge-agent-gates"} if recovery != expected_recovery or source.get("head_sha") != R3_HISTORICAL_HEAD_SHA or source.get("pr_number") != 189 or protected.get("sha256") != hashlib.sha256(json.dumps(expected_recovery, sort_keys=True, separators=(",", ":")).encode()).hexdigest(): diff --git a/scripts/test_agent_gates.py b/scripts/test_agent_gates.py index 12af282eb..b0d0a2ccd 100644 --- a/scripts/test_agent_gates.py +++ b/scripts/test_agent_gates.py @@ -2385,16 +2385,21 @@ def get_json(self, _path: str): def test_root_recovery_policy_is_exactly_pinned() -> None: - """The one-use planning-intake gate recovery is identity-exact.""" + """The one-use root reconciliation recovery is identity-exact.""" policy = json.loads(Path(".agent-loop/policies/loop-memory-recovery.json").read_text()) assert policy == { "activation": { - "chunk_id": "WS-ENG-ROOT-001-01", + "chunk_id": "WS-ENG-ROOT-001-02", "initiative_id": "WS-ENG-ROOT-001", }, "signed_basis": "339248c40020658583bf7bd1e4a58daf85f5ffb8", - "recovered_merges": [], - "schema_version": 7, + "recovered_merges": [{ + "initiative_id": "WS-ENG-ROOT-001", + "chunk_id": "WS-ENG-ROOT-001-01", + "pr_number": 205, + "merge_sha": "ce512bdb6ae47e94ae8067845531cacfc3378a85", + }], + "schema_version": 8, } diff --git a/scripts/test_update_post_merge_memory.py b/scripts/test_update_post_merge_memory.py index cfc440ada..90fb1cb91 100644 --- a/scripts/test_update_post_merge_memory.py +++ b/scripts/test_update_post_merge_memory.py @@ -2118,7 +2118,7 @@ def test_shared_reconcile_orders_recovery_and_validates_atomically( collection_calls: list[tuple[str, bool]] = [] monkeypatch.setattr(loop, "plan_reconciliation_commits", lambda *_args: events.append("plan") or planned) monkeypatch.setattr(loop, "prepare_recovery_exemptions", lambda *_args, **_kwargs: events.append("prepare") or exemptions) - def collect(_client, _repository, sha, *, historical_recovery=False): + def collect(_client, _repository, sha, *, historical_recovery=False, root_recovery_policy=None): collection_calls.append((sha, historical_recovery)) events.append("collect") return _record() @@ -2135,6 +2135,36 @@ def collect(_client, _repository, sha, *, historical_recovery=False): assert collection_calls == [(normal_sha, False), (recovery_sha, True)] +def test_shared_reconcile_preserves_root_recovery_policy_during_collection( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch, +) -> None: + state_root = tmp_path / "state" + loop.apply_merge_record(state_root, _record()) + planned = [loop.ROOT_RECONCILE_MERGE_SHA, "f" * 40] + policy = { + "schema_version": 8, + "signed_basis": loop.ROOT_RECOVERY_SIGNED_BASIS, + "activation": {"initiative_id": loop.ROOT_RECOVERY_INITIATIVE_ID, "chunk_id": loop.ROOT_RECONCILE_CHUNK_ID}, + "recovered_merges": [{ + "initiative_id": loop.ROOT_RECOVERY_INITIATIVE_ID, + "chunk_id": loop.ROOT_RECOVERY_CHUNK_ID, + "pr_number": loop.ROOT_RECONCILE_PR_NUMBER, + "merge_sha": loop.ROOT_RECONCILE_MERGE_SHA, + }], + } + exemptions = [{"initiative_id": "WS-ENG-ROOT-001", "chunk_id": "WS-ENG-ROOT-001-01", "pr_number": 205}] + calls = [] + monkeypatch.setattr(loop, "plan_reconciliation_commits", lambda *_args: planned) + monkeypatch.setattr(loop, "prepare_recovery_exemptions", lambda *_args, **_kwargs: exemptions) + monkeypatch.setattr(loop, "_load_json_at_commit", lambda *_args: policy) + monkeypatch.setattr(loop, "collect_merge_record", lambda *_args, **kwargs: calls.append(kwargs.get("root_recovery_policy")) or _record()) + monkeypatch.setattr(loop, "apply_merge_record", lambda *_args, **_kwargs: True) + monkeypatch.setattr(loop, "assert_recovery_consumed", lambda *_args: None) + monkeypatch.setattr(loop, "validate_generated_state", lambda *_args: None) + loop.reconcile_to_main(object(), "Flow-Research/workstream", repository_root=tmp_path, state_root=state_root, target_sha="f" * 40) + assert calls == [policy, policy] + + def test_merge_bound_evidence_is_mandatory_after_cutover_in_both_validators() -> None: record = _record() record["source"]["merged_at"] = "2026-07-23T05:11:46Z" diff --git a/scripts/update_post_merge_memory.py b/scripts/update_post_merge_memory.py index 440dd2bae..f77126abc 100644 --- a/scripts/update_post_merge_memory.py +++ b/scripts/update_post_merge_memory.py @@ -79,6 +79,12 @@ ROOT_RECOVERY_REASON = "planning-intake-gate-circularity" ROOT_RECOVERY_CODE = "exact-root-gate-repair-v1" ROOT_RECOVERY_CERTIFICATE_SHA256 = "32f75b9709e6b09b30e672cc9889a8754dad1428e0b57e06d6bd237ae6476e40" +ROOT_RECONCILE_CHUNK_ID = "WS-ENG-ROOT-001-02" +ROOT_RECONCILE_MERGE_SHA = "ce512bdb6ae47e94ae8067845531cacfc3378a85" +ROOT_RECONCILE_PR_NUMBER = 205 +ROOT_RECONCILE_REASON = "root-recovery-reconciliation-circularity" +ROOT_RECONCILE_CODE = "exact-root-reconcile-repair-v1" +ROOT_RECONCILE_CERTIFICATE_SHA256 = "f7fbd8bca3ba731c1a6c51c953533906a92cd8ad719dadee88ef763a1f70cf56" CHECK_RUN_CONCLUSIONS = frozenset({ "action_required", "cancelled", "failure", "neutral", "skipped", "stale", "success", "timed_out", @@ -1278,16 +1284,19 @@ def collect_merge_record( ) if root_recovery_policy is not None: + policy_schema = root_recovery_policy["schema_version"] + activation_chunk_id = root_recovery_policy["activation"]["chunk_id"] recovery_only = { "merge_sha": merge_sha, "head_sha": head_sha, "chunk_id": metadata.chunk_id, "pr_number": pr_number, - "policy_schema": 7, + "policy_schema": policy_schema, "signed_basis": ROOT_RECOVERY_SIGNED_BASIS, - "activation_chunk_id": ROOT_RECOVERY_CHUNK_ID, + "activation_chunk_id": activation_chunk_id, "certificate_sha256": hashlib.sha256( _canonical_json(root_recovery_policy).encode("utf-8") ).hexdigest(), - "reason": ROOT_RECOVERY_REASON, "code": ROOT_RECOVERY_CODE, + "reason": ROOT_RECOVERY_REASON if policy_schema == 7 else ROOT_RECONCILE_REASON, + "code": ROOT_RECOVERY_CODE if policy_schema == 7 else ROOT_RECONCILE_CODE, } protected_checks = { "schema_version": 1, "recovery_only": recovery_only, @@ -2178,6 +2187,30 @@ def _validate_record(record: dict[str, Any]) -> LoopMetadata: ): raise LoopMemoryError("root recovery evidence is invalid") return metadata + if isinstance(recovery_only, dict) and recovery_only.get("policy_schema") == 8: + recovered = source["main_sha"] == ROOT_RECONCILE_MERGE_SHA + expected_chunk = ROOT_RECOVERY_CHUNK_ID if recovered else ROOT_RECONCILE_CHUNK_ID + expected_parent = ROOT_RECOVERY_SIGNED_BASIS if recovered else ROOT_RECONCILE_MERGE_SHA + expected = { + "merge_sha": source["main_sha"], "head_sha": source["head_sha"], + "chunk_id": expected_chunk, "pr_number": source["pr_number"], + "policy_schema": 8, "signed_basis": ROOT_RECOVERY_SIGNED_BASIS, + "activation_chunk_id": ROOT_RECONCILE_CHUNK_ID, + "certificate_sha256": ROOT_RECONCILE_CERTIFICATE_SHA256, + "reason": ROOT_RECONCILE_REASON, "code": ROOT_RECONCILE_CODE, + } + if ( + recovery_only != expected + or protected.get("sha256") != hashlib.sha256(_canonical_json(expected).encode()).hexdigest() + or metadata.initiative_id != ROOT_RECOVERY_INITIATIVE_ID + or metadata.chunk_id != expected_chunk + or metadata.next_chunk_id is not None + or metadata.next_chunk_title is not None + or source.get("intent_path") != f".agent-loop/merge-intents/{expected_chunk}.json" + or source.get("first_parent_sha") != expected_parent + ): + raise LoopMemoryError("root reconciliation evidence is invalid") + return metadata expected = { "merge_sha": "d3321698fb856f3fac320cdc7bc598f813fe1953", "head_sha": R3_HISTORICAL_HEAD_SHA, "chunk_id": "WS-ENG-007-00R2", @@ -2356,11 +2389,12 @@ def _validate_recovery_policy(payload: Any) -> dict[str, Any]: 5: {"schema_version", "signed_basis", "activation", "recovered_merges"}, 6: {"schema_version", "signed_basis", "activation", "recovered_merges"}, 7: {"schema_version", "signed_basis", "activation", "recovered_merges"}, + 8: {"schema_version", "signed_basis", "activation", "recovered_merges"}, }.get(version, set()) if set(payload) != expected: raise LoopMemoryError("recovery policy has an invalid schema") activation = payload.get("activation") - if version not in {1, 2, 3, 4, 5, 6, 7} or not isinstance(activation, dict): + if version not in {1, 2, 3, 4, 5, 6, 7, 8} or not isinstance(activation, dict): raise LoopMemoryError("recovery policy is unsupported") if set(activation) != {"initiative_id", "chunk_id"} or not _is_valid_exemption_id( activation.get("initiative_id"), activation.get("chunk_id") @@ -2381,6 +2415,19 @@ def _validate_recovery_policy(payload: Any) -> dict[str, Any]: ): raise LoopMemoryError("root recovery certificate is not exact") return json.loads(_canonical_json(payload)) + if version == 8: + if ( + payload.get("signed_basis") != ROOT_RECOVERY_SIGNED_BASIS + or activation != {"initiative_id": ROOT_RECOVERY_INITIATIVE_ID, "chunk_id": ROOT_RECONCILE_CHUNK_ID} + or payload.get("recovered_merges") != [{ + "initiative_id": ROOT_RECOVERY_INITIATIVE_ID, + "chunk_id": ROOT_RECOVERY_CHUNK_ID, + "pr_number": ROOT_RECONCILE_PR_NUMBER, + "merge_sha": ROOT_RECONCILE_MERGE_SHA, + }] + ): + raise LoopMemoryError("root reconciliation certificate is not exact") + return json.loads(_canonical_json(payload)) if version in {3, 4, 5, 6}: recovered_merges = payload.get("recovered_merges") valid_length = ( @@ -2483,7 +2530,7 @@ def prepare_recovery_exemptions( collect_merge_record( client, repository, target_sha, root_recovery_policy=policy ) - if policy["schema_version"] == 7 + if policy["schema_version"] in {7, 8} else collect_merge_record(client, repository, target_sha) ) activation = policy["activation"] @@ -2534,6 +2581,35 @@ def prepare_recovery_exemptions( if not isinstance(existing, list) or exemption in existing: raise LoopMemoryError("recovery exemption collides with signed state") return [exemption] + if policy["schema_version"] == 8: + if planned_shas != [ROOT_RECONCILE_MERGE_SHA, target_sha]: + raise LoopMemoryError("root reconciliation plan is not exact") + if state.get("source", {}).get("main_sha") != policy["signed_basis"]: + raise LoopMemoryError("root reconciliation signed basis does not match canonical state") + recovered = collect_merge_record( + client, repository, ROOT_RECONCILE_MERGE_SHA, + root_recovery_policy=policy, + ) + records = [recovered, target_record] + expected_parent = policy["signed_basis"] + for sha, record in zip(planned_shas, records, strict=True): + source = record.get("source", {}) + if source.get("main_sha") != sha or source.get("first_parent_sha") != expected_parent: + raise LoopMemoryError("root reconciliation is not first-parent adjacent") + if record.get("protected_checks", {}).get("recovery_only", {}).get("policy_schema") != 8: + raise LoopMemoryError("root reconciliation protected evidence is missing") + expected_parent = sha + exemptions = [_record_exemption(record) for record in records] + if exemptions[0] != { + "initiative_id": ROOT_RECOVERY_INITIATIVE_ID, + "chunk_id": ROOT_RECOVERY_CHUNK_ID, + "pr_number": ROOT_RECONCILE_PR_NUMBER, + }: + raise LoopMemoryError("root reconciliation recovered merge is not exact") + existing = state.get("legacy_exemptions", []) + if not isinstance(existing, list) or any(item in existing for item in exemptions): + raise LoopMemoryError("recovery exemption collides with signed state") + return exemptions if policy["schema_version"] in {3, 4, 5, 6}: recovered_policies = policy["recovered_merges"] expected_shas = [item["merge_sha"] for item in recovered_policies] + [target_sha] @@ -2652,11 +2728,20 @@ def reconcile_to_main( client, repository, repository_root=repository_root, state_root=state_root, target_sha=target_sha, planned_shas=planned, ) + recovery_policy = None + if any(item.get("initiative_id") == ROOT_RECOVERY_INITIATIVE_ID for item in exemptions): + candidate = _validate_recovery_policy(_load_json_at_commit( + repository_root, target_sha, RECOVERY_POLICY_PATH, "recovery policy" + )) + if candidate["schema_version"] in {7, 8}: + recovery_policy = candidate for merge_sha in planned: historical = _is_r3_historical_recovery(merge_sha, exemptions) record = collect_merge_record( client, repository, merge_sha, historical_recovery=True - ) if historical else collect_merge_record(client, repository, merge_sha) + ) if historical else collect_merge_record( + client, repository, merge_sha, root_recovery_policy=recovery_policy + ) apply_merge_record(state_root, record, recovery_exemptions=exemptions or None) assert_recovery_consumed(state_root, target_sha, exemptions) validate_generated_state(state_root) From 74b6fa1f16a3300a6970d49053b9b50f8d445de9 Mon Sep 17 00:00:00 2001 From: Abiorh001 Date: Sun, 26 Jul 2026 15:25:03 +0100 Subject: [PATCH 2/6] fix(agent-loop): admit exact reconciliation repair --- ...1-02-exact-root-reconciliation-recovery.md | 42 +++++++- scripts/check_chunk_contract.py | 102 ++++++++++++++++++ scripts/test_check_chunk_contract.py | 63 +++++++++++ 3 files changed, 204 insertions(+), 3 deletions(-) diff --git a/.agent-loop/initiatives/WS-ENG-ROOT-001-planning-intake-gate-recovery/chunks/WS-ENG-ROOT-001-02-exact-root-reconciliation-recovery.md b/.agent-loop/initiatives/WS-ENG-ROOT-001-planning-intake-gate-recovery/chunks/WS-ENG-ROOT-001-02-exact-root-reconciliation-recovery.md index e1b6579ef..3da1c5d7c 100644 --- a/.agent-loop/initiatives/WS-ENG-ROOT-001-planning-intake-gate-recovery/chunks/WS-ENG-ROOT-001-02-exact-root-reconciliation-recovery.md +++ b/.agent-loop/initiatives/WS-ENG-ROOT-001-planning-intake-gate-recovery/chunks/WS-ENG-ROOT-001-02-exact-root-reconciliation-recovery.md @@ -25,18 +25,46 @@ L0 ".agent-loop/policies/loop-memory-recovery.json", ".agent-loop/initiatives/WS-ENG-ROOT-001-planning-intake-gate-recovery/**", ".agent-loop/merge-intents/WS-ENG-ROOT-001-02.json", + ".agent-loop/merge-intents/WS-ENG-ROOT-001-01.json", "scripts/check_loop_memory_state.py", + "scripts/check_chunk_contract.py", "scripts/update_post_merge_memory.py", "scripts/test_agent_gates.py", + "scripts/test_check_chunk_contract.py", "scripts/test_check_loop_memory_state.py", "scripts/test_update_post_merge_memory.py" ], "forbidden_paths": ["backend/**", "frontend/**", ".github/**"], "required_reviewers": ["senior engineering", "qa/test", "security/auth", "product/ops", "architecture", "ci integrity", "docs", "reuse/dedup", "test delta"], - "verification_commands": ["agent-gate-tests", "loop-memory-state", "loop-memory-recovery-tests", "internal-review-evidence", "markdown-links", "stale-wording", "git-diff-check"] + "verification_commands": ["agent-gate-tests", "loop-memory-state", "loop-memory-recovery-tests", "chunk-scope-tests", "internal-review-evidence", "markdown-links", "stale-wording", "git-diff-check"] } ``` +## Allowed files + +```text +.agent-loop/policies/loop-memory-recovery.json +.agent-loop/initiatives/WS-ENG-ROOT-001-planning-intake-gate-recovery/** +.agent-loop/merge-intents/WS-ENG-ROOT-001-02.json +.agent-loop/merge-intents/WS-ENG-ROOT-001-01.json +scripts/check_loop_memory_state.py +scripts/check_chunk_contract.py +scripts/update_post_merge_memory.py +scripts/test_agent_gates.py +scripts/test_check_chunk_contract.py +scripts/test_check_loop_memory_state.py +scripts/test_update_post_merge_memory.py +``` + +## Not allowed + +```text +backend/** +frontend/** +.github/** +product behavior, workflow changes, dependencies, reusable recovery authority +``` + ## Acceptance criteria - [ ] `reconcile_to_main` recollects schema-v7/v8 recovery merges with the exact @@ -54,6 +82,7 @@ L0 python3 scripts/test_agent_gates.py PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 python3 -m pytest -q scripts/test_update_post_merge_memory.py scripts/test_check_loop_memory_state.py python3 scripts/check_loop_memory_state.py +python3 scripts/test_check_chunk_contract.py python3 scripts/check_internal_review_evidence.py python3 scripts/check_markdown_links.py python3 scripts/check_stale_workstream_wording.py @@ -62,8 +91,15 @@ git diff --check origin/main...HEAD ## Required reviewers -Senior engineering, QA/test, security/auth, product/ops, architecture, CI -integrity, docs, reuse/dedup, and test delta. +- [ ] senior engineering +- [ ] qa/test +- [ ] security/auth +- [ ] product/ops +- [ ] architecture +- [ ] ci integrity +- [ ] docs +- [ ] reuse/dedup +- [ ] test delta ## Human review focus diff --git a/scripts/check_chunk_contract.py b/scripts/check_chunk_contract.py index 919b08548..4e5698bf6 100644 --- a/scripts/check_chunk_contract.py +++ b/scripts/check_chunk_contract.py @@ -90,6 +90,40 @@ ".agent-loop/initiatives/WS-ENG-ROOT-001-planning-intake-gate-recovery/reviews/WS-ENG-ROOT-001-01-pr-trust-bundle.md", *ROOT_RECOVERY_ALLOWED[3:], }) +ROOT_RECONCILE_BASE = "ce512bdb6ae47e94ae8067845531cacfc3378a85" +ROOT_RECONCILE_CHUNK = "WS-ENG-ROOT-001-02" +ROOT_RECONCILE_CONTRACT = ( + ".agent-loop/initiatives/WS-ENG-ROOT-001-planning-intake-gate-recovery/" + "chunks/WS-ENG-ROOT-001-02-exact-root-reconciliation-recovery.md" +) +ROOT_RECONCILE_ALLOWED = ( + ".agent-loop/policies/loop-memory-recovery.json", + ".agent-loop/initiatives/WS-ENG-ROOT-001-planning-intake-gate-recovery/**", + ".agent-loop/merge-intents/WS-ENG-ROOT-001-02.json", + ".agent-loop/merge-intents/WS-ENG-ROOT-001-01.json", + "scripts/check_loop_memory_state.py", + "scripts/check_chunk_contract.py", + "scripts/update_post_merge_memory.py", + "scripts/test_agent_gates.py", + "scripts/test_check_chunk_contract.py", + "scripts/test_check_loop_memory_state.py", + "scripts/test_update_post_merge_memory.py", +) +ROOT_RECONCILE_PATHS = frozenset({ + ".agent-loop/policies/loop-memory-recovery.json", + ".agent-loop/merge-intents/WS-ENG-ROOT-001-02.json", + ".agent-loop/merge-intents/WS-ENG-ROOT-001-01.json", + ".agent-loop/initiatives/WS-ENG-ROOT-001-planning-intake-gate-recovery/STATUS.md", + ROOT_RECONCILE_CONTRACT, + ROOT_RECOVERY_CONTRACT, + ".agent-loop/initiatives/WS-ENG-ROOT-001-planning-intake-gate-recovery/reviews/WS-ENG-ROOT-001-02-pr-trust-bundle.md", + "scripts/check_chunk_contract.py", + "scripts/check_loop_memory_state.py", + "scripts/update_post_merge_memory.py", + "scripts/test_agent_gates.py", + "scripts/test_check_chunk_contract.py", + "scripts/test_update_post_merge_memory.py", +}) class ContractError(ValueError): @@ -613,6 +647,71 @@ def root_recovery_scope( ) +def root_reconcile_scope( + repo: Path, base: str, head: str, intent: dict[str, Any] +) -> ScopeContract | None: + """Admit only the exact schema-v8 repair that consumes PR #205.""" + if intent["chunk_id"] != ROOT_RECONCILE_CHUNK: + return None + if intent["initiative_id"] != ROOT_RECOVERY_INITIATIVE: + raise ContractError("root reconciliation merge intent identity is invalid") + resolved_base = _decode_utf8( + _git(repo, "rev-parse", f"{base}^{{commit}}"), "root reconciliation base" + ).strip() + if resolved_base != ROOT_RECONCILE_BASE: + raise ContractError("root reconciliation is not based on exact PR 205 merge") + expected_policy = { + "activation": {"chunk_id": ROOT_RECONCILE_CHUNK, "initiative_id": ROOT_RECOVERY_INITIATIVE}, + "signed_basis": ROOT_RECOVERY_SIGNED_BASIS, + "recovered_merges": [{ + "initiative_id": ROOT_RECOVERY_INITIATIVE, + "chunk_id": ROOT_RECOVERY_CHUNK, + "pr_number": 205, + "merge_sha": ROOT_RECONCILE_BASE, + }], + "schema_version": 8, + } + if _git_json(repo, f"{head}:.agent-loop/policies/loop-memory-recovery.json") != expected_policy: + raise ContractError("root reconciliation certificate is invalid or reusable") + statuses = parse_name_status_z(_git( + repo, "diff", "--name-status", "-z", "--find-renames", + "--find-copies-harder", f"{base}...{head}" + )) + if any(status not in {"A", "M"} and not status.startswith("C") for status, _names in statuses): + raise ContractError("root reconciliation permits only exact added or modified files") + paths = validate_path_bytes(name for _status, names in statuses for name in names) + if frozenset(paths) != ROOT_RECONCILE_PATHS: + raise ContractError("root reconciliation delta does not match exact path certificate") + contract_raw = _git(repo, "show", f"{head}:{ROOT_RECONCILE_CONTRACT}") + heading = HEADING_RE.match(_decode_utf8(contract_raw)) + try: + scope_data = json.loads(machine_block(contract_raw), object_pairs_hook=_object) + except (json.JSONDecodeError, ContractError) as exc: + raise ContractError(f"root reconciliation contract scope is malformed: {exc}") from exc + expected_scope = { + "schema_version": 1, "chunk_id": ROOT_RECONCILE_CHUNK, + "phase": "implementation", "risk_class": "L0", + "allowed_paths": list(ROOT_RECONCILE_ALLOWED), + "forbidden_paths": ["backend/**", "frontend/**", ".github/**"], + "required_reviewers": [ + "senior engineering", "qa/test", "security/auth", "product/ops", + "architecture", "ci integrity", "docs", "reuse/dedup", "test delta", + ], + "verification_commands": [ + "agent-gate-tests", "loop-memory-state", "loop-memory-recovery-tests", + "chunk-scope-tests", "internal-review-evidence", "markdown-links", + "stale-wording", "git-diff-check", + ], + } + if heading is None or heading.group("id") != ROOT_RECONCILE_CHUNK or scope_data != expected_scope: + raise ContractError("root reconciliation contract scope is not exact") + return ScopeContract( + ROOT_RECONCILE_CHUNK, "implementation", "L0", ROOT_RECONCILE_ALLOWED, + ("backend/**", "frontend/**", ".github/**"), + tuple(expected_scope["required_reviewers"]), tuple(expected_scope["verification_commands"]), + ) + + @dataclass(frozen=True) class SignedStart: ledger_index: int @@ -829,6 +928,9 @@ def select_contract( recovery = root_recovery_scope(repo, base, head, intent) if recovery is not None: return recovery, None + reconciliation = root_reconcile_scope(repo, base, head, intent) + if reconciliation is not None: + return reconciliation, None start = latest_signed_start(records, intent["chunk_id"]) if start.initiative_id != intent["initiative_id"]: raise ContractError("signed start initiative disagrees with merge intent") diff --git a/scripts/test_check_chunk_contract.py b/scripts/test_check_chunk_contract.py index 71fbc1937..f84f64fb4 100644 --- a/scripts/test_check_chunk_contract.py +++ b/scripts/test_check_chunk_contract.py @@ -586,6 +586,69 @@ def test_root_recovery_rejects_extra_path_scope_and_reuse(self) -> None: with self.assertRaisesRegex(checker.ContractError, "consumed"): self.scope(repo, base, intent) +class RootReconciliationScopeTests(unittest.TestCase): + def git(self, repo: Path, *args: str) -> bytes: + return subprocess.run(["git", *args], cwd=repo, check=True, stdout=subprocess.PIPE).stdout.strip() + + def fixture(self, directory: str) -> tuple[Path, str, dict[str, object]]: + repo = Path(directory) + self.git(repo, "init", "-q", "-b", "main") + self.git(repo, "config", "user.email", "test@example.invalid") + self.git(repo, "config", "user.name", "Test") + (repo / "README.md").write_text("base\n") + self.git(repo, "add", "."); self.git(repo, "commit", "-qm", "base") + base = self.git(repo, "rev-parse", "HEAD").decode() + source_root = Path(__file__).resolve().parents[1] + for path in checker.ROOT_RECONCILE_PATHS: + destination = repo / path + destination.parent.mkdir(parents=True, exist_ok=True) + source = source_root / path + destination.write_bytes(source.read_bytes() if source.exists() else b"fixture\n") + policy = { + "activation": {"chunk_id": checker.ROOT_RECONCILE_CHUNK, "initiative_id": checker.ROOT_RECOVERY_INITIATIVE}, + "signed_basis": base, + "recovered_merges": [{ + "initiative_id": checker.ROOT_RECOVERY_INITIATIVE, + "chunk_id": checker.ROOT_RECOVERY_CHUNK, + "pr_number": 205, "merge_sha": base, + }], + "schema_version": 8, + } + (repo / ".agent-loop/policies/loop-memory-recovery.json").write_text(json.dumps(policy)) + self.git(repo, "add", "."); self.git(repo, "commit", "-qm", "reconcile") + return repo, base, {"initiative_id": checker.ROOT_RECOVERY_INITIATIVE, "chunk_id": checker.ROOT_RECONCILE_CHUNK} + + def scope(self, repo: Path, base: str, intent: dict[str, object]): + with mock.patch.object(checker, "ROOT_RECONCILE_BASE", base), mock.patch.object(checker, "ROOT_RECOVERY_SIGNED_BASIS", base): + return checker.root_reconcile_scope(repo, base, "HEAD", intent) + + def test_exact_schema_v8_reconciliation_is_admitted(self) -> None: + with tempfile.TemporaryDirectory() as directory: + repo, base, intent = self.fixture(directory) + scope = self.scope(repo, base, intent) + assert scope is not None + checker.enforce_scope(scope, checker.discover_changes(repo, base, "HEAD")) + + def test_reconciliation_rejects_foreign_identity_path_policy_and_base(self) -> None: + with tempfile.TemporaryDirectory() as directory: + repo, base, intent = self.fixture(directory) + self.assertIsNone(self.scope(repo, base, {**intent, "chunk_id": "WS-ENG-ROOT-001-03"})) + with self.assertRaisesRegex(checker.ContractError, "identity"): + self.scope(repo, base, {**intent, "initiative_id": "WS-ENG-ROOT-999"}) + with self.assertRaisesRegex(checker.ContractError, "exact PR 205"): + checker.root_reconcile_scope(repo, base, "HEAD", intent) + (repo / "extra.py").write_text("no\n"); self.git(repo, "add", "."); self.git(repo, "commit", "-qm", "extra") + with self.assertRaisesRegex(checker.ContractError, "path certificate"): + self.scope(repo, base, intent) + with tempfile.TemporaryDirectory() as directory: + repo, base, intent = self.fixture(directory) + policy = repo / ".agent-loop/policies/loop-memory-recovery.json" + data = json.loads(policy.read_text()); data["schema_version"] = 7 + policy.write_text(json.dumps(data)); self.git(repo, "add", "."); self.git(repo, "commit", "-qm", "reuse") + with self.assertRaisesRegex(checker.ContractError, "invalid or reusable"): + self.scope(repo, base, intent) + + class GitDiscoveryIntegrationTests(unittest.TestCase): def git(self, repo: Path, *args: str) -> bytes: return subprocess.run( From d011c47ed479c4bcd8cd9df991ab086d8b28cc5c Mon Sep 17 00:00:00 2001 From: Abiorh001 Date: Sun, 26 Jul 2026 18:55:54 +0100 Subject: [PATCH 3/6] test(agent-loop): cover schema v8 recovery --- scripts/check_loop_memory_state.py | 4 +- scripts/test_update_post_merge_memory.py | 86 ++++++++++++++++++++++++ scripts/update_post_merge_memory.py | 3 +- 3 files changed, 91 insertions(+), 2 deletions(-) diff --git a/scripts/check_loop_memory_state.py b/scripts/check_loop_memory_state.py index 5626d1d59..6d87a125a 100644 --- a/scripts/check_loop_memory_state.py +++ b/scripts/check_loop_memory_state.py @@ -118,6 +118,7 @@ ROOT_RECOVERY_CERTIFICATE_SHA256 = "32f75b9709e6b09b30e672cc9889a8754dad1428e0b57e06d6bd237ae6476e40" ROOT_RECONCILE_CHUNK_ID = "WS-ENG-ROOT-001-02" ROOT_RECONCILE_MERGE_SHA = "ce512bdb6ae47e94ae8067845531cacfc3378a85" +ROOT_RECONCILE_PR_NUMBER = 205 ROOT_RECONCILE_REASON = "root-recovery-reconciliation-circularity" ROOT_RECONCILE_CODE = "exact-root-reconcile-repair-v1" ROOT_RECONCILE_CERTIFICATE_SHA256 = "f7fbd8bca3ba731c1a6c51c953533906a92cd8ad719dadee88ef763a1f70cf56" @@ -901,7 +902,8 @@ def _record_failures( parent = ROOT_RECOVERY_SIGNED_BASIS if recovered else ROOT_RECONCILE_MERGE_SHA expected_recovery = { "merge_sha": source.get("main_sha"), "head_sha": source.get("head_sha"), - "chunk_id": chunk, "pr_number": source.get("pr_number"), + "chunk_id": chunk, + "pr_number": ROOT_RECONCILE_PR_NUMBER if recovered else source.get("pr_number"), "policy_schema": 8, "signed_basis": ROOT_RECOVERY_SIGNED_BASIS, "activation_chunk_id": ROOT_RECONCILE_CHUNK_ID, "certificate_sha256": ROOT_RECONCILE_CERTIFICATE_SHA256, diff --git a/scripts/test_update_post_merge_memory.py b/scripts/test_update_post_merge_memory.py index 90fb1cb91..2b69092df 100644 --- a/scripts/test_update_post_merge_memory.py +++ b/scripts/test_update_post_merge_memory.py @@ -2165,6 +2165,92 @@ def test_shared_reconcile_preserves_root_recovery_policy_during_collection( assert calls == [policy, policy] +def _root_reconcile_record(*, recovered: bool, pr_number: int = 205) -> dict: + record = _merge_bound_record() + chunk = loop.ROOT_RECOVERY_CHUNK_ID if recovered else loop.ROOT_RECONCILE_CHUNK_ID + main = loop.ROOT_RECONCILE_MERGE_SHA if recovered else "f" * 40 + parent = loop.ROOT_RECOVERY_SIGNED_BASIS if recovered else loop.ROOT_RECONCILE_MERGE_SHA + record["source"].update( + main_sha=main, first_parent_sha=parent, head_sha=("4" if recovered else "5") * 40, + pr_number=pr_number, pr_url=f"https://github.com/Flow-Research/workstream/pull/{pr_number}", + intent_path=f".agent-loop/merge-intents/{chunk}.json", + ) + record["completed_chunk"].update( + initiative_id=loop.ROOT_RECOVERY_INITIATIVE_ID, chunk_id=chunk, + chunk_title=chunk, next_chunk_id=None, next_chunk_title=None, + ) + record["gate"].update(next_chunk_id=None, next_chunk_title=None) + evidence = { + "merge_sha": main, "head_sha": record["source"]["head_sha"], + "chunk_id": chunk, "pr_number": pr_number, "policy_schema": 8, + "signed_basis": loop.ROOT_RECOVERY_SIGNED_BASIS, + "activation_chunk_id": loop.ROOT_RECONCILE_CHUNK_ID, + "certificate_sha256": loop.ROOT_RECONCILE_CERTIFICATE_SHA256, + "reason": loop.ROOT_RECONCILE_REASON, "code": loop.ROOT_RECONCILE_CODE, + } + record["protected_checks"] = { + "schema_version": 1, "recovery_only": evidence, + "sha256": loop.hashlib.sha256(loop._canonical_json(evidence).encode()).hexdigest(), + } + return record + + +def test_schema_v8_records_validate_and_pin_recovered_pr_number() -> None: + recovered = _root_reconcile_record(recovered=True) + activation = _root_reconcile_record(recovered=False, pr_number=206) + assert loop._validate_record(recovered).chunk_id == loop.ROOT_RECOVERY_CHUNK_ID + assert loop._validate_record(activation).chunk_id == loop.ROOT_RECONCILE_CHUNK_ID + assert not checker._record_failures(recovered, "recovered", None) + assert not checker._record_failures(activation, "activation", None) + mutated = _root_reconcile_record(recovered=True, pr_number=999) + with pytest.raises(loop.LoopMemoryError, match="root reconciliation evidence"): + loop._validate_record(mutated) + assert any("root reconciliation evidence" in failure for failure in checker._record_failures(mutated, "mutated", None)) + + +def test_prepare_schema_v8_consumes_exact_adjacent_pair_and_rejects_mutations( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch, +) -> None: + state_root = tmp_path / "state" + base = _record(); base["source"]["main_sha"] = loop.ROOT_RECOVERY_SIGNED_BASIS + loop.apply_merge_record(state_root, base) + recovered = _root_reconcile_record(recovered=True) + activation = _root_reconcile_record(recovered=False, pr_number=206) + policy = { + "schema_version": 8, "signed_basis": loop.ROOT_RECOVERY_SIGNED_BASIS, + "activation": {"initiative_id": loop.ROOT_RECOVERY_INITIATIVE_ID, "chunk_id": loop.ROOT_RECONCILE_CHUNK_ID}, + "recovered_merges": [{"initiative_id": loop.ROOT_RECOVERY_INITIATIVE_ID, "chunk_id": loop.ROOT_RECOVERY_CHUNK_ID, "pr_number": 205, "merge_sha": loop.ROOT_RECONCILE_MERGE_SHA}], + } + monkeypatch.setattr(loop, "_load_json_at_commit", lambda *_args: policy) + monkeypatch.setattr(loop, "collect_merge_record", lambda _c, _r, sha, **_kw: recovered if sha == loop.ROOT_RECONCILE_MERGE_SHA else activation) + planned = [loop.ROOT_RECONCILE_MERGE_SHA, "f" * 40] + exemptions = loop.prepare_recovery_exemptions(object(), "Flow-Research/workstream", repository_root=tmp_path, state_root=state_root, target_sha="f" * 40, planned_shas=planned) + assert [item["chunk_id"] for item in exemptions] == [loop.ROOT_RECOVERY_CHUNK_ID, loop.ROOT_RECONCILE_CHUNK_ID] + for wrong in (["f" * 40], ["f" * 40, loop.ROOT_RECONCILE_MERGE_SHA]): + with pytest.raises(loop.LoopMemoryError, match="plan is not exact"): + loop.prepare_recovery_exemptions(object(), "Flow-Research/workstream", repository_root=tmp_path, state_root=state_root, target_sha="f" * 40, planned_shas=wrong) + wrong_basis_root = tmp_path / "wrong-basis" + wrong_base = _record(); wrong_base["source"]["main_sha"] = "a" * 40 + loop.apply_merge_record(wrong_basis_root, wrong_base) + with pytest.raises(loop.LoopMemoryError, match="signed basis"): + loop.prepare_recovery_exemptions(object(), "Flow-Research/workstream", repository_root=tmp_path, state_root=wrong_basis_root, target_sha="f" * 40, planned_shas=planned) + wrong_parent = json.loads(json.dumps(recovered)); wrong_parent["source"]["first_parent_sha"] = "a" * 40 + monkeypatch.setattr(loop, "collect_merge_record", lambda _c, _r, sha, **_kw: wrong_parent if sha == loop.ROOT_RECONCILE_MERGE_SHA else activation) + with pytest.raises(loop.LoopMemoryError, match="first-parent adjacent"): + loop.prepare_recovery_exemptions(object(), "Flow-Research/workstream", repository_root=tmp_path, state_root=state_root, target_sha="f" * 40, planned_shas=planned) + wrong_evidence = json.loads(json.dumps(recovered)); wrong_evidence["protected_checks"]["recovery_only"]["policy_schema"] = 7 + monkeypatch.setattr(loop, "collect_merge_record", lambda _c, _r, sha, **_kw: wrong_evidence if sha == loop.ROOT_RECONCILE_MERGE_SHA else activation) + with pytest.raises(loop.LoopMemoryError, match="protected evidence"): + loop.prepare_recovery_exemptions(object(), "Flow-Research/workstream", repository_root=tmp_path, state_root=state_root, target_sha="f" * 40, planned_shas=planned) + wrong_identity = _root_reconcile_record(recovered=True, pr_number=999) + monkeypatch.setattr(loop, "collect_merge_record", lambda _c, _r, sha, **_kw: wrong_identity if sha == loop.ROOT_RECONCILE_MERGE_SHA else activation) + with pytest.raises(loop.LoopMemoryError, match="recovered merge is not exact"): + loop.prepare_recovery_exemptions(object(), "Flow-Research/workstream", repository_root=tmp_path, state_root=state_root, target_sha="f" * 40, planned_shas=planned) + bad = json.loads(json.dumps(policy)); bad["recovered_merges"][0]["pr_number"] = 999 + with pytest.raises(loop.LoopMemoryError, match="certificate is not exact"): + loop._validate_recovery_policy(bad) + + def test_merge_bound_evidence_is_mandatory_after_cutover_in_both_validators() -> None: record = _record() record["source"]["merged_at"] = "2026-07-23T05:11:46Z" diff --git a/scripts/update_post_merge_memory.py b/scripts/update_post_merge_memory.py index f77126abc..a75b5dc68 100644 --- a/scripts/update_post_merge_memory.py +++ b/scripts/update_post_merge_memory.py @@ -2193,7 +2193,8 @@ def _validate_record(record: dict[str, Any]) -> LoopMetadata: expected_parent = ROOT_RECOVERY_SIGNED_BASIS if recovered else ROOT_RECONCILE_MERGE_SHA expected = { "merge_sha": source["main_sha"], "head_sha": source["head_sha"], - "chunk_id": expected_chunk, "pr_number": source["pr_number"], + "chunk_id": expected_chunk, + "pr_number": ROOT_RECONCILE_PR_NUMBER if recovered else source["pr_number"], "policy_schema": 8, "signed_basis": ROOT_RECOVERY_SIGNED_BASIS, "activation_chunk_id": ROOT_RECONCILE_CHUNK_ID, "certificate_sha256": ROOT_RECONCILE_CERTIFICATE_SHA256, From 2b32ffb446d3ad4bc4a97b926d880b8ef98b8fe0 Mon Sep 17 00:00:00 2001 From: Abiorh001 Date: Sun, 26 Jul 2026 19:10:59 +0100 Subject: [PATCH 4/6] fix(agent-loop): reserve reconciliation review evidence --- scripts/check_chunk_contract.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/check_chunk_contract.py b/scripts/check_chunk_contract.py index 4e5698bf6..7dac06885 100644 --- a/scripts/check_chunk_contract.py +++ b/scripts/check_chunk_contract.py @@ -116,6 +116,7 @@ ".agent-loop/initiatives/WS-ENG-ROOT-001-planning-intake-gate-recovery/STATUS.md", ROOT_RECONCILE_CONTRACT, ROOT_RECOVERY_CONTRACT, + ".agent-loop/initiatives/WS-ENG-ROOT-001-planning-intake-gate-recovery/reviews/WS-ENG-ROOT-001-02-internal-review-evidence.md", ".agent-loop/initiatives/WS-ENG-ROOT-001-planning-intake-gate-recovery/reviews/WS-ENG-ROOT-001-02-pr-trust-bundle.md", "scripts/check_chunk_contract.py", "scripts/check_loop_memory_state.py", From 5317fccb384238874e11310fdde58ad0727f2a38 Mon Sep 17 00:00:00 2001 From: Abiorh001 Date: Sun, 26 Jul 2026 19:13:15 +0100 Subject: [PATCH 5/6] docs(agent-loop): record reconciliation recovery reviews --- ...NG-ROOT-001-02-internal-review-evidence.md | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 .agent-loop/initiatives/WS-ENG-ROOT-001-planning-intake-gate-recovery/reviews/WS-ENG-ROOT-001-02-internal-review-evidence.md diff --git a/.agent-loop/initiatives/WS-ENG-ROOT-001-planning-intake-gate-recovery/reviews/WS-ENG-ROOT-001-02-internal-review-evidence.md b/.agent-loop/initiatives/WS-ENG-ROOT-001-planning-intake-gate-recovery/reviews/WS-ENG-ROOT-001-02-internal-review-evidence.md new file mode 100644 index 000000000..6acd6d009 --- /dev/null +++ b/.agent-loop/initiatives/WS-ENG-ROOT-001-planning-intake-gate-recovery/reviews/WS-ENG-ROOT-001-02-internal-review-evidence.md @@ -0,0 +1,52 @@ +# WS-ENG-ROOT-001-02 Internal Review Evidence + +Reviewed code SHA: `2b32ffb446d3ad4bc4a97b926d880b8ef98b8fe0` + +Reviewed at: `2026-07-26T18:00:00Z` + +Reviewer run IDs: `r2f_senior`, `r2f_qa`, `r2f_security`, `r2f_product`, +`r2f_arch`, `r2f_ci`, `r2f_docs`, `r2f_reuse`, `r2f_testdelta` + +## Commands Run + +```bash +python3 scripts/test_agent_gates.py +python3 scripts/check_loop_memory_state.py +PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 python3 -m pytest -q scripts/test_update_post_merge_memory.py scripts/test_check_loop_memory_state.py +python3 scripts/check_internal_review_evidence.py +python3 scripts/check_markdown_links.py +python3 scripts/check_stale_workstream_wording.py +git diff --check origin/main...HEAD +``` + +## Reviewer Results + +| Reviewer | Result | Blocking findings | Notes | +|---|---|---|---| +| senior engineering | PASS AFTER FIXES | none | The final evidence file completes the exact certificate reserved in the reviewed implementation. | +| QA/test | PASS WITH LOW RISKS | none | Adversarial schema-v8 recovery, order, parent, identity, evidence, and policy cases pass. | +| security/auth | PASS | none | Recovery authority is exact, adjacent, identity-bound, and one-use. | +| product/ops | PASS | none | AUTH remains stopped until canonical reconciliation succeeds. | +| architecture | PASS | none | Independent validators preserve the closed schema-v8 boundary. | +| CI integrity | PASS | none | Existing coverage gates pass at 90.23 and 90.65 percent without weakening. | +| docs | PASS | none | Contract, status, trust bundle, policy, and intent are aligned. | +| reuse/dedup | PASS | none | Exact duplication is deliberate independent validation. | +| test delta | PASS | none | No test, assertion, threshold, or command was removed or weakened. | + +## Findings Resolved + +Valid findings addressed: yes + +Open sub-agent sessions: none + +The first candidate omitted self-admission, then fell below the unchanged +coverage floors. The reviewed implementation added exact schema-v8 admission, +adversarial recovery proof, independent PR #205 pinning, and sufficient branch +coverage. This evidence-only file completes the pre-reviewed exact path +certificate without changing implementation. + +## Remaining Gate + +GitHub checks, external review, and the explicit human recovery merge +checkpoint remain. AUTH must stay stopped until generated signed state reaches +the reconciliation merge and consumes both exemptions. From 9bb2b98c9f6c392523e4ba709bf46f5129b2fdea Mon Sep 17 00:00:00 2001 From: Abiorh001 Date: Sun, 26 Jul 2026 19:14:28 +0100 Subject: [PATCH 6/6] docs(agent-loop): complete reconciliation proof commands --- .../reviews/WS-ENG-ROOT-001-02-internal-review-evidence.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.agent-loop/initiatives/WS-ENG-ROOT-001-planning-intake-gate-recovery/reviews/WS-ENG-ROOT-001-02-internal-review-evidence.md b/.agent-loop/initiatives/WS-ENG-ROOT-001-planning-intake-gate-recovery/reviews/WS-ENG-ROOT-001-02-internal-review-evidence.md index 6acd6d009..8e5a02ff0 100644 --- a/.agent-loop/initiatives/WS-ENG-ROOT-001-planning-intake-gate-recovery/reviews/WS-ENG-ROOT-001-02-internal-review-evidence.md +++ b/.agent-loop/initiatives/WS-ENG-ROOT-001-planning-intake-gate-recovery/reviews/WS-ENG-ROOT-001-02-internal-review-evidence.md @@ -11,6 +11,7 @@ Reviewer run IDs: `r2f_senior`, `r2f_qa`, `r2f_security`, `r2f_product`, ```bash python3 scripts/test_agent_gates.py +python3 scripts/test_check_chunk_contract.py python3 scripts/check_loop_memory_state.py PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 python3 -m pytest -q scripts/test_update_post_merge_memory.py scripts/test_check_loop_memory_state.py python3 scripts/check_internal_review_evidence.py