From 9a7ff34c7ec0bfd671c313cbf68cf4e6f42c8028 Mon Sep 17 00:00:00 2001 From: Abiorh001 Date: Thu, 23 Jul 2026 07:55:58 +0100 Subject: [PATCH 1/7] fix(loop-memory): canonicalize trusted check reruns --- .../CHUNK_MAP.md | 8 +- .../DECISIONS.md | 20 +++ .../DISCOVERY.md | 17 +++ .../INTENT.md | 31 ++++ .../PLAN.md | 25 +++ .../RISKS.md | 3 + .../STATUS.md | 16 +- ...S-ENG-007-00R2-canonical-check-recovery.md | 144 ++++++++++++++++++ .../merge-intents/WS-ENG-007-00R2.json | 9 ++ .../policies/loop-memory-recovery.json | 24 ++- docs/operations_post_merge_memory.md | 31 ++++ scripts/test_agent_gates.py | 120 +++++++++++++-- scripts/test_update_post_merge_memory.py | 134 ++++++++++++++++ scripts/update_post_merge_memory.py | 143 ++++++++++++++--- 14 files changed, 677 insertions(+), 48 deletions(-) create mode 100644 .agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/chunks/WS-ENG-007-00R2-canonical-check-recovery.md create mode 100644 .agent-loop/merge-intents/WS-ENG-007-00R2.json diff --git a/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/CHUNK_MAP.md b/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/CHUNK_MAP.md index 7c97d8d9..98629aa2 100644 --- a/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/CHUNK_MAP.md +++ b/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/CHUNK_MAP.md @@ -2,10 +2,12 @@ | Order | Chunk | Purpose | Risk | State | |---:|---|---|---:|---| -| 0 | `WS-ENG-007-00R1` | Repair planning-intake file/tree parity and recover PR #187 exactly once | L1/P0 | Emergency prerequisite review | -| 1 | `WS-ENG-007-01` | Add deterministic reviewed-patch identity and conservative base-delta review preservation | L1 | Blocked on 00R1 merge, signed reconciliation, and explicit start | +| 0 | `WS-ENG-007-00R1` | Repair planning-intake file/tree parity and recover PR #187 exactly once | L1/P0 | Merged; recovery superseded after rerun-cardinality failure | +| 0 | `WS-ENG-007-00R2` | Canonicalize repeated trusted check evidence and reconcile PRs #187 and #188 exactly once | L1/P0 | Proposed reliability recovery | +| 1 | `WS-ENG-007-01` | Add deterministic reviewed-patch identity and conservative base-delta review preservation | L1 | Blocked on 00R2 merge, successful three-merge reconciliation, and explicit start | | 2 | `WS-ENG-007-02` | Add structured reviewer-track and upstream-finding reconciliation | L1 | Blocked on 01 merge and explicit start | | 3 | `WS-ENG-007-03` | Add merge-group CI parity and queue-readiness proof | L1 | Blocked on 02 merge and explicit start | -Each chunk is one PR and stops after merge. Every successor requires a separate +Recovery chunks are exceptional ordered prerequisites; implementation chunks +remain one PR each and stop after merge. Every successor requires a separate explicit signed start. diff --git a/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/DECISIONS.md b/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/DECISIONS.md index fefa5540..b0bf20cc 100644 --- a/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/DECISIONS.md +++ b/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/DECISIONS.md @@ -62,3 +62,23 @@ Use the existing closed schema-v1 two-merge recovery mechanism, rebound to PR `WS-ENG-007-00R1`. Both ephemeral exemptions must be consumed before signing; no wildcard, manual state edit, force push, new secret, or persistent exemption is allowed. + +## D12 - Check reruns are evidence versions, not ambiguity + +For each protected check name, validate every returned same-name check against +the exact reviewed head and pinned GitHub Actions application. Reject the set if +any candidate is foreign, wrong-head, malformed, incomplete, or reuses an ID. +Order completed candidates by parsed `started_at` instant then positive numeric +check-run ID. The unique latest invocation determines the result. `completed_at` +must be a valid instant at or after start, but completion order never defines +recency. A later-started failure supersedes an older success even when the older +run completes last. Rerun count alone never fails reconciliation. + +## D13 - Recover the exact three-merge backlog once + +Schema v3 names an ordered `recovered_merges` list of one or two entries plus +one activation; production requires exactly two recovered entries. +For this repair it binds only PR #187 / `WS-ENG-007-PLAN`, PR #188 / +`WS-ENG-007-00R1`, and direct-next `WS-ENG-007-00R2`. The plan must be exactly +those adjacent first-parent merges. All exemptions are consumed before signing +and cannot appear in state, ledger, projections, or replay. diff --git a/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/DISCOVERY.md b/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/DISCOVERY.md index 3327edb8..adf0c44d 100644 --- a/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/DISCOVERY.md +++ b/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/DISCOVERY.md @@ -59,3 +59,20 @@ tracks. A PR never authors or narrows this graph. - Exact GitHub merge-queue repository-setting API behavior. - Hosted context behavior after queue enablement. These block administrative enablement, not repository-side parity planning. + +## Recovery reliability discovery — 2026-07-23 + +- PR #188 merged at `c65633f8f0991dbefe7b0635e053aab0df8f9af8`. +- Loop Memory run `29984940789` failed before publication with `planning intake + check agent-gates is missing or duplicated`. +- PR #187 head `34ddac158d8c8d5c96498de008dd43354205199f` + has two completed successful `agent-gates` runs from GitHub Actions app + `15368/github-actions` and one completed successful `test` run from the same + app. +- `_validate_protected_actions_checks()` requires exactly one match, while + `_check_evidence()` already treats check history as a latest-value stream. +- The current schema-v1 recovery certificate can name only one recovered merge + plus activation. Signed state is two merges behind, so the repair target is a + third adjacent merge and requires an exact ordered multi-recovered schema. +- The workflow already plans all unrecorded first-parent commits and publishes + only after reconciliation; no workflow edit is required. diff --git a/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/INTENT.md b/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/INTENT.md index b82b8f49..020818dd 100644 --- a/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/INTENT.md +++ b/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/INTENT.md @@ -79,3 +79,34 @@ forged manifests, and merge-group combined trees. Approve each implementation chunk and the later repository-setting change that enables GitHub merge queue. No queue or automatic merge setting changes in the planning intake. + +## Recovery reliability addendum — WS-ENG-007-00R2 + +### Problem being solved + +Signed reconciliation treats legitimate same-name GitHub Actions reruns as +ambiguity. PR #187 therefore cannot enter signed history even though both +`agent-gates` runs and its `test` run are trusted and successful. + +### Target behavior + +Rerun count is harmless. A deterministic latest trusted run controls each +protected result, later failures cannot be hidden by older successes, and the +exact three-merge backlog is reconciled atomically without reusable authority. + +### Boundaries preserved + +Required checks, branch protection, signing keys, permissions, human merge +approval, coverage floors, product behavior, and successor start gates do not +change. + +### Proof strategy + +Adversarial check histories, permutation invariance, exact production policy +pinning, ordered recovery consumption, replay rejection, and byte-identical +idempotency evidence. + +### Human decision + +The user's explicit `start` instruction approves planning and execution of this +bounded reliability repair only. Successor starts remain separately gated. diff --git a/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/PLAN.md b/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/PLAN.md index 0750a2bd..b8308a4d 100644 --- a/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/PLAN.md +++ b/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/PLAN.md @@ -181,3 +181,28 @@ always: Stop if preservation depends on mutable PR prose, inaccessible commits, an unbounded semantic judgment, weakened CI, or automatic human approval. + +## Recovery reliability plan — WS-ENG-007-00R2 + +1. Replace exact-cardinality protected-check validation with a deterministic + latest-run selector that validates all same-name candidates against exact + head, pinned app, structural fields, and parseable timestamps before sorting. +2. Reject the complete name-set if any candidate is malformed, untrusted, or + incomplete. Order completed candidates by parsed `started_at` instant then + positive numeric check-run ID. `completed_at` proves terminal consistency + but never defines invocation recency. Require the selected run to succeed. +3. Extend the closed recovery certificate with schema v3: an ordered + `recovered_merges` list plus one activation. Validate exact plan equality, + chronological first-parent adjacency, identities, aggregate required checks, + protected-check provenance on every recovered/target head, uniqueness, and + consumption. Schema v3 permits at most two recovered merges; production + requires exactly both named entries. +4. Bind production recovery to PR #187, PR #188, and direct-next 00R2 only. +5. Prove adversarial rerun histories, permutation invariance, exact recovery, + non-serialization, replay rejection, and deterministic repeated output. +6. Preserve the existing workflow, signing, human approval, successor stops, + and required-check names unchanged. + +Rejected alternatives: ignore duplicate runs, accept any successful run, use API +array order, use only a check name/status context, rerun until history changes, +manually edit signed state, or add a persistent recovery bypass. diff --git a/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/RISKS.md b/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/RISKS.md index 160c0fb5..d8d8ed4d 100644 --- a/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/RISKS.md +++ b/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/RISKS.md @@ -12,3 +12,6 @@ | Concurrent queue entries interact | High | Validate each exact merge-group SHA and invalidate on queue recomputation. | | Planning intake cannot enter signed history | Critical | Filter only validated directory entries from the GitHub recursive tree map, preserve every validated supported non-tree leaf identity, reject unsupported entries, and recover exact PR `#187` plus 00R1 through a consumed two-merge certificate. | | Recovery becomes a reusable bypass | Critical | Bind schema-v1 recovery to PR #187 merge `8928ba80eeaf31e609dbdeda7d2cc22e9ea482c8` and the exact 00R1 activation identity; require the ordered two-merge plan, successful checks, full consumption, and no persisted exemption. | +| Legitimate check reruns block signed memory | Critical | Validate every same-name candidate, order completed trusted invocations by parsed start instant and positive check-run ID, then require the unique latest candidate to succeed. | +| A stale success hides a later failure | Critical | Latest validated run wins; an in-progress, failed, cancelled, foreign-app, malformed, or timestamp-ambiguous latest run fails closed. | +| Three-merge repair becomes general authority | Critical | Schema v3 permits at most two recovered merges and production names only PR #187, PR #188, and 00R2; require exact first-parent adjacency, unique identities, full consumption before signing, and no serialization. | diff --git a/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/STATUS.md b/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/STATUS.md index 6a1af548..f776f7ed 100644 --- a/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/STATUS.md +++ b/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/STATUS.md @@ -1,14 +1,16 @@ # STATUS: WS-ENG-007 - Concurrent PR Review Reconciliation -- Phase: planning-intake recovery +- Phase: canonical check-evidence recovery - Gate: fail-closed automation repair - Active planning chunk: none - Active implementation chunk: none -- Recovery chunk: `WS-ENG-007-00R1` +- Completed but unreconciled recovery chunk: `WS-ENG-007-00R1` +- Proposed recovery chunk: `WS-ENG-007-00R2` - Proposed implementation successor after recovery: `WS-ENG-007-01` - Separate explicit start required: true -- Current gate: PR #187 merged at - `8928ba80eeaf31e609dbdeda7d2cc22e9ea482c8`, but signed post-merge memory and - the fresh explicit start both fail closed because recursive GitHub tree - evidence includes directory entries while the reviewed PR file inventory and - independent local Git checker contain only recursive non-tree entries. +- Current gate: PR #188 merged at + `c65633f8f0991dbefe7b0635e053aab0df8f9af8`. Its tree normalization repair is + correct, but signed reconciliation still fails because PR #187 has two + successful trusted `agent-gates` reruns and planning intake incorrectly + treats repeated check evidence as ambiguous. Signed state remains at + `73b457925b02301587b83d01ced0adb66319d134`; no successor is active. diff --git a/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/chunks/WS-ENG-007-00R2-canonical-check-recovery.md b/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/chunks/WS-ENG-007-00R2-canonical-check-recovery.md new file mode 100644 index 00000000..6f2e1496 --- /dev/null +++ b/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/chunks/WS-ENG-007-00R2-canonical-check-recovery.md @@ -0,0 +1,144 @@ +# Chunk Contract: WS-ENG-007-00R2 — Canonical Check Evidence Recovery + +## Goal + +Make signed merge reconciliation stable under legitimate GitHub Actions reruns +and reconcile the exact PR #187 → PR #188 → 00R2 backlog once. + +## Why this chunk exists + +PR #188 fixed recursive tree parity, but run `29984940789` failed because PR +#187 has two successful trusted `agent-gates` check runs. Cardinality is mutable +GitHub history and is not a valid ambiguity signal. Signed state remains at +`73b457925b02301587b83d01ced0adb66319d134`. + +## Start phase + +`implementation` + +## Risk + +L1 / P0 policy, audit, CI-evidence, and signed-memory recovery + +## Authorization boundary + +This otherwise-unstartable repair uses a reviewed exact schema-v3 recovery +certificate. It grants no workflow, repository, secret, merge, or continuing +start authority. + +## Allowed files + +```text +scripts/update_post_merge_memory.py +scripts/test_update_post_merge_memory.py +scripts/test_agent_gates.py +scripts/test_check_loop_memory_state.py +.agent-loop/policies/loop-memory-recovery.json +docs/operations_post_merge_memory.md +.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/CHUNK_MAP.md +.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/STATUS.md +.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/RISKS.md +.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/DECISIONS.md +.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/chunks/WS-ENG-007-00R2-canonical-check-recovery.md +.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/reviews/WS-ENG-007-00R2-internal-review-evidence.md +.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/reviews/WS-ENG-007-00R2-pr-trust-bundle.md +.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/reviews/WS-ENG-007-00R2-external-review-response.md +.agent-loop/merge-intents/WS-ENG-007-00R2.json +``` + +## Not allowed + +- Workflow, required-check name, branch-protection, coverage, dependency, + signing-key, secret, permission, product, backend, or frontend changes. +- Treating CodeRabbit status as implementation-start authority. +- Accepting a foreign app, wrong head, incomplete or malformed check run. +- Letting an older success override a later failure. +- Persistent, wildcard, reorderable, cross-repository, or reusable recovery. +- Starting `WS-ENG-007-01` or `WS-ENG-006-01` in this chunk. + +## Acceptance criteria + +- [ ] Every same-name candidate is validated before selection. Any poisoned + candidate fails whether older or newer than a valid success and under + every input permutation. Completed trusted candidates are ordered by + parsed `started_at` instant then positive check-run ID; the unique latest + invocation determines success. Completion order never defines recency. +- [ ] Zero matches, incomplete API response, foreign app, wrong head, missing or + malformed ID/timestamps, any non-completed run, non-success latest + conclusion, or a canonical identity collision fails closed. Timestamp + parsing is timezone-aware RFC3339 normalized to instants; naive/non-string + values fail. IDs require `type(id) is int` and `id > 0`; booleans, + byte-identical duplicate IDs, and conflicting ID reuse fail. +- [ ] Tests cover one success, two successes, failure→success, success→failure, + an older-started success completing after a newer-started failure, + queued/in-progress evidence, cancelled/timed_out/skipped conclusions, + foreign app with the protected name, wrong head, offset-equivalent and + identical timestamps resolved by ID, duplicate ID rejection, malformed + timestamps, API truncation, and evidence order permutation. Poisoned + candidates appear both older and newer than a valid success. Later failure + is tested independently for `agent-gates` and `test`. +- [ ] The real PR #187 shape—two successful trusted `agent-gates` plus one + successful trusted `test`—passes deterministically and is permutation + invariant. +- [ ] Schema v3 permits only `schema_version`, ordered `recovered_merges`, and + `activation`; the list contains one or two entries, is identity-unique and SHA-unique, + and every entry binds initiative, chunk, positive PR number, and merge SHA. + Tests reject unknown/missing keys at every level, empty/three-entry lists, + duplicate identity or SHA under changed counterparts, bool/invalid PR, + malformed SHA, activation collision, and policy/Git order mismatch. +- [ ] Production policy binds exactly PR #187 merge + `8928ba80eeaf31e609dbdeda7d2cc22e9ea482c8` / `WS-ENG-007-PLAN`, then PR #188 + merge `c65633f8f0991dbefe7b0635e053aab0df8f9af8` / + `WS-ENG-007-00R1`, then activation `WS-ENG-007-00R2`. +- [ ] Recovery requires the exact adjacent first-parent plan + `[8928ba80…, c65633f8…, target]`, validates every recovered identity and + required-check aggregate, validates exact protected-check provenance on + every recovered and target reviewed head, consumes all three exemptions + before signing, serializes none, + and rejects missing, extra, reordered, repeated, intervening, wrong-parent, + wrong-identity, check-failure, partial-consumption, and replay cases. +- [ ] Applying the exact authenticated plan to two fresh copies of the same + starting state produces byte-identical complete generated closed trees, + including signing input. Replay from completed state proves no exemption + reinjection and no publication change. +- [ ] Final state records 00R2 stopped with `WS-ENG-007-01` as explicit-start + successor; both `WS-ENG-007-01` and `WS-ENG-006-01` remain inactive. +- [ ] Existing tree, planning grammar, merge attribution, lifecycle, signing, + checker, and recovery regression suites remain blocking and unchanged in + authority semantics. +- [ ] Exactly one schema-v2 merge intent names `WS-ENG-007-01` as the same- + initiative explicit-start successor. + +## Verification commands + +```bash +PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 python3 -m pytest -q scripts/test_update_post_merge_memory.py scripts/test_agent_gates.py scripts/test_check_loop_memory_state.py +python3 scripts/test_agent_gates.py +python3 scripts/update_post_merge_memory.py validate-merge-intent --repository-root . --base-ref origin/main +PR_HEAD_SHA="$(git rev-parse HEAD)" 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 +- test delta + +## Human review focus + +Confirm latest-run semantics match GitHub rerun behavior, later failures cannot +be hidden, and schema-v3 authority is exact, ordered, consumed, and non-reusable. + +## Stop condition + +Stop after this PR. Do not start either successor until the user merges this +specific PR and signed automation proves the exact recovered state. diff --git a/.agent-loop/merge-intents/WS-ENG-007-00R2.json b/.agent-loop/merge-intents/WS-ENG-007-00R2.json new file mode 100644 index 00000000..a3f9ce2d --- /dev/null +++ b/.agent-loop/merge-intents/WS-ENG-007-00R2.json @@ -0,0 +1,9 @@ +{ + "chunk_id": "WS-ENG-007-00R2", + "chunk_title": "Canonical Check Evidence Recovery", + "initiative_id": "WS-ENG-007", + "next_chunk_id": "WS-ENG-007-01", + "next_chunk_title": "Reviewed Patch and Base-Delta Reconciliation", + "next_requires_explicit_start": true, + "schema_version": 2 +} diff --git a/.agent-loop/policies/loop-memory-recovery.json b/.agent-loop/policies/loop-memory-recovery.json index e0d7a6cb..e5e1e5fa 100644 --- a/.agent-loop/policies/loop-memory-recovery.json +++ b/.agent-loop/policies/loop-memory-recovery.json @@ -1,13 +1,21 @@ { "activation": { - "chunk_id": "WS-ENG-007-00R1", + "chunk_id": "WS-ENG-007-00R2", "initiative_id": "WS-ENG-007" }, - "recovered_merge": { - "chunk_id": "WS-ENG-007-PLAN", - "initiative_id": "WS-ENG-007", - "merge_sha": "8928ba80eeaf31e609dbdeda7d2cc22e9ea482c8", - "pr_number": 187 - }, - "schema_version": 1 + "recovered_merges": [ + { + "chunk_id": "WS-ENG-007-PLAN", + "initiative_id": "WS-ENG-007", + "merge_sha": "8928ba80eeaf31e609dbdeda7d2cc22e9ea482c8", + "pr_number": 187 + }, + { + "chunk_id": "WS-ENG-007-00R1", + "initiative_id": "WS-ENG-007", + "merge_sha": "c65633f8f0991dbefe7b0635e053aab0df8f9af8", + "pr_number": 188 + } + ], + "schema_version": 3 } diff --git a/docs/operations_post_merge_memory.md b/docs/operations_post_merge_memory.md index 94969a48..9356fea5 100644 --- a/docs/operations_post_merge_memory.md +++ b/docs/operations_post_merge_memory.md @@ -261,6 +261,37 @@ Recovery does not start implementation. `WS-ENG-007-01` and `WS-ENG-006-01` remain stopped afterward; each requires its own ordinary explicit signed start on exact current `main`. +## WS-ENG-007 Canonical Check-Evidence Recovery + +PR #188 repaired tree normalization but its post-merge run failed because PR +#187 contains two legitimate successful `agent-gates` reruns. Protected check +validation now treats reruns as ordered evidence versions, not a cardinality +error. Every same-name candidate must target the exact reviewed head, come from +the pinned GitHub Actions application, contain a strict positive unique ID and +timezone-aware lifecycle timestamps, and be completed. Completed invocations +are ordered by parsed `started_at` instant and ID; `completed_at` validates +terminal consistency but never determines recency. The latest invocation must +succeed. A later failure therefore cannot be hidden by an older run that +finishes afterward, and any foreign, malformed, incomplete, or duplicate-ID +candidate fails the whole protected-name set. + +`WS-ENG-007-00R2` uses closed recovery schema v3. The certificate permits at +most two recovered merges and production names exactly this first-parent order: + +1. PR #187 / `WS-ENG-007-PLAN` / + `8928ba80eeaf31e609dbdeda7d2cc22e9ea482c8`; +2. PR #188 / `WS-ENG-007-00R1` / + `c65633f8f0991dbefe7b0635e053aab0df8f9af8`; +3. the direct-next `WS-ENG-007-00R2` activation merge. + +Exact protected GitHub Actions provenance and aggregate required-check success +are required for every merge. The ordered plan must begin at signed state +`73b457925b02301587b83d01ced0adb66319d134`; any extra, missing, reordered, +repeated, intervening, or non-adjacent merge fails. All temporary identities are +consumed before signing, never serialize into the generated tree, and are inert +on replay. Recovery starts no implementation: `WS-ENG-007-01` and +`WS-ENG-006-01` each remain stopped pending their own explicit signed start. + ## Historical WS-ENG-006 Exact Root Repair `WS-ENG-006-00` uses the closed two-merge recovery certificate to reconcile diff --git a/scripts/test_agent_gates.py b/scripts/test_agent_gates.py index 4d312247..60b956ad 100644 --- a/scripts/test_agent_gates.py +++ b/scripts/test_agent_gates.py @@ -2148,19 +2148,111 @@ def test_ws_eng_007_recovery_policy_is_exactly_pinned() -> None: policy = json.loads(Path(".agent-loop/policies/loop-memory-recovery.json").read_text()) assert policy == { "activation": { - "chunk_id": "WS-ENG-007-00R1", + "chunk_id": "WS-ENG-007-00R2", "initiative_id": "WS-ENG-007", }, - "recovered_merge": { - "chunk_id": "WS-ENG-007-PLAN", - "initiative_id": "WS-ENG-007", - "merge_sha": "8928ba80eeaf31e609dbdeda7d2cc22e9ea482c8", - "pr_number": 187, - }, - "schema_version": 1, + "recovered_merges": [ + { + "chunk_id": "WS-ENG-007-PLAN", + "initiative_id": "WS-ENG-007", + "merge_sha": "8928ba80eeaf31e609dbdeda7d2cc22e9ea482c8", + "pr_number": 187, + }, + { + "chunk_id": "WS-ENG-007-00R1", + "initiative_id": "WS-ENG-007", + "merge_sha": "c65633f8f0991dbefe7b0635e053aab0df8f9af8", + "pr_number": 188, + }, + ], + "schema_version": 3, } +def test_planning_checks_canonicalize_trusted_reruns_and_fail_closed() -> None: + """Protected checks select the latest invocation after validating all runs.""" + updater = load_module("planning_check_reruns", "scripts/update_post_merge_memory.py") + head_sha = "a" * 40 + + def run( + check_id: int, + name: str, + started: str, + completed: str, + conclusion: str = "success", + ) -> dict: + return { + "id": check_id, + "name": name, + "head_sha": head_sha, + "status": "completed", + "conclusion": conclusion, + "started_at": started, + "completed_at": completed, + "app": {"id": updater.GITHUB_ACTIONS_APP_ID, "slug": updater.GITHUB_ACTIONS_APP_SLUG}, + } + + class CheckClient: + def __init__(self, runs: list[dict]) -> None: + self.runs = runs + + def get_json(self, _path: str): + return {"total_count": len(self.runs), "check_runs": self.runs} + + test_run = run(20, "test", "2026-07-23T05:00:00Z", "2026-07-23T05:01:00Z") + older = run(10, "agent-gates", "2026-07-23T05:00:00Z", "2026-07-23T05:10:00Z") + newer = run(11, "agent-gates", "2026-07-23T05:02:00Z", "2026-07-23T05:03:00Z") + for ordered in ([older, newer, test_run], [test_run, newer, older]): + updater._validate_protected_actions_checks( + CheckClient(ordered), "Flow-Research/workstream", head_sha + ) + + old_failure = {**older, "conclusion": "failure"} + updater._validate_protected_actions_checks( + CheckClient([newer, old_failure, test_run]), "Flow-Research/workstream", head_sha + ) + new_failure = {**newer, "conclusion": "failure"} + assert_loop_error( + updater, + lambda: updater._validate_protected_actions_checks( + CheckClient([older, new_failure, test_run]), "Flow-Research/workstream", head_sha + ), + "invalid provenance", + ) + test_failure = {**test_run, "conclusion": "timed_out"} + assert_loop_error( + updater, + lambda: updater._validate_protected_actions_checks( + CheckClient([older, newer, test_failure]), "Flow-Research/workstream", head_sha + ), + "invalid provenance", + ) + poisoned = ( + {**older, "app": {"id": 1, "slug": "foreign"}}, + {**older, "head_sha": "b" * 40}, + {**older, "status": "queued", "completed_at": None}, + {**older, "id": True}, + {**older, "started_at": "2026-07-23 05:00:00"}, + ) + for bad in poisoned: + for ordered in ([bad, newer, test_run], [test_run, newer, bad]): + assert_loop_error( + updater, + lambda ordered=ordered: updater._validate_protected_actions_checks( + CheckClient(list(ordered)), "Flow-Research/workstream", head_sha + ), + "invalid", + ) + assert_loop_error( + updater, + lambda: updater._validate_protected_actions_checks( + CheckClient([older, dict(older), newer, test_run]), + "Flow-Research/workstream", head_sha, + ), + "invalid provenance", + ) + + def test_planning_intake_collection_binds_paths_trees_and_check_sources() -> None: """Planning intake collection binds the reviewed tree and trusted checks.""" updater = load_module("planning_intake_collection", "scripts/update_post_merge_memory.py") @@ -2274,17 +2366,23 @@ def get_json(self, path: str): if "/check-runs?per_page=100" in path: runs = [ { + "id": index, "name": name, "head_sha": head_sha, "status": self.check_status, "conclusion": self.check_conclusion, + "started_at": "2026-07-22T07:59:00Z", "completed_at": "2026-07-22T08:00:00Z", "app": {"id": self.app_id, "slug": self.app_slug}, } - for name in ("agent-gates", "test") + for index, name in enumerate(("agent-gates", "test"), start=1) ] if self.duplicate_run: - runs.append(dict(runs[0])) + runs.append({ + **runs[0], "id": 3, + "started_at": "2026-07-22T08:01:00Z", + "completed_at": "2026-07-22T08:02:00Z", + }) return {"total_count": len(runs), "check_runs": runs} if "STATUS.md?ref=" in path: value = "- Active planning chunk: none\n- Active implementation chunk: none\n" @@ -2353,7 +2451,6 @@ def get_json(self, path: str): client.mode = "100644" cases = ( (lambda: setattr(client, "app_slug", "foreign-app"), "invalid provenance"), - (lambda: setattr(client, "duplicate_run", True), "missing or duplicated"), (lambda: setattr(client, "check_status", "queued"), "invalid provenance"), (lambda: setattr(client, "check_conclusion", "cancelled"), "invalid provenance"), (lambda: setattr(client, "file_status", "renamed"), "additive files only"), @@ -7281,6 +7378,7 @@ def main() -> int: test_independent_checker_accepts_and_mutates_planning_intake_state, test_planning_tree_entries_canonicalize_recursive_directory_objects, test_ws_eng_007_recovery_policy_is_exactly_pinned, + test_planning_checks_canonicalize_trusted_reruns_and_fail_closed, test_planning_intake_collection_binds_paths_trees_and_check_sources, test_eng006_exact_recovery_certificate_is_consumed_and_inert_on_replay, test_eng007_two_merge_recovery_binds_pr187_and_consumes_authority, diff --git a/scripts/test_update_post_merge_memory.py b/scripts/test_update_post_merge_memory.py index cc5fcceb..e965bfb3 100644 --- a/scripts/test_update_post_merge_memory.py +++ b/scripts/test_update_post_merge_memory.py @@ -465,6 +465,60 @@ def _recovery_policy() -> dict: } +def _recovery_policy_v3() -> dict: + return { + "schema_version": 3, + "activation": { + "initiative_id": "WS-ENG-007", + "chunk_id": "WS-ENG-007-00R2", + }, + "recovered_merges": [ + { + "initiative_id": "WS-ENG-007", + "chunk_id": "WS-ENG-007-PLAN", + "pr_number": 187, + "merge_sha": "c" * 40, + }, + { + "initiative_id": "WS-ENG-007", + "chunk_id": "WS-ENG-007-00R1", + "pr_number": 188, + "merge_sha": "d" * 40, + }, + ], + } + + +@pytest.mark.parametrize( + "mutation", + [ + lambda policy: policy.update(extra=True), + lambda policy: policy.pop("activation"), + lambda policy: policy.update(recovered_merges=[]), + lambda policy: policy.update(recovered_merges=[*policy["recovered_merges"], { + "initiative_id": "WS-ENG-008", "chunk_id": "WS-ENG-008-01", + "pr_number": 189, "merge_sha": "e" * 40, + }]), + lambda policy: policy["recovered_merges"][0].update(pr_number=True), + lambda policy: policy["recovered_merges"][0].update(merge_sha="bad"), + lambda policy: policy["recovered_merges"][1].update( + merge_sha=policy["recovered_merges"][0]["merge_sha"] + ), + lambda policy: policy["recovered_merges"][1].update( + initiative_id="WS-ENG-007", chunk_id="WS-ENG-007-PLAN", pr_number=187 + ), + lambda policy: policy.update( + activation={"initiative_id": "WS-ENG-007", "chunk_id": "WS-ENG-007-PLAN"} + ), + ], +) +def test_recovery_policy_v3_schema_fails_closed(mutation) -> None: + policy = _recovery_policy_v3() + mutation(policy) + with pytest.raises(loop.LoopMemoryError): + loop._validate_recovery_policy(policy) + + @pytest.mark.parametrize( ("mutation", "message"), [ @@ -648,6 +702,86 @@ def test_prepare_recovery_binds_exact_target_and_two_merge_plan( ] +def test_prepare_recovery_v3_binds_three_adjacent_merges_and_is_deterministic( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + base = _record() + base["source"]["main_sha"] = "a" * 40 + recovered_plan = _merge_record( + "WS-ENG-007", "WS-ENG-007-PLAN", 187, "c" * 40, "a" * 40 + ) + recovered_r1 = _merge_record( + "WS-ENG-007", "WS-ENG-007-00R1", 188, "d" * 40, "c" * 40 + ) + target = _merge_record( + "WS-ENG-007", "WS-ENG-007-00R2", 189, "e" * 40, "d" * 40 + ) + records = {"c" * 40: recovered_plan, "d" * 40: recovered_r1, "e" * 40: target} + for index, record in enumerate(records.values(), start=1): + record["source"]["head_sha"] = str(index) * 40 + monkeypatch.setattr(loop, "_load_json_at_commit", lambda *_args: _recovery_policy_v3()) + monkeypatch.setattr( + loop, "collect_merge_record", + lambda _client, _repository, sha: json.loads(json.dumps(records[sha])), + ) + protected_heads: list[str] = [] + monkeypatch.setattr( + loop, "_validate_protected_actions_checks", + lambda _client, _repository, head: protected_heads.append(head), + ) + roots = [tmp_path / "state-one", tmp_path / "state-two"] + final_trees: list[dict[str, str]] = [] + for state_root in roots: + loop.apply_merge_record(state_root, json.loads(json.dumps(base))) + exemptions = loop.prepare_recovery_exemptions( + object(), "Flow-Research/workstream", repository_root=tmp_path, + state_root=state_root, target_sha="e" * 40, + planned_shas=["c" * 40, "d" * 40, "e" * 40], + ) + assert [item["pr_number"] for item in exemptions] == [187, 188, 189] + for sha in ("c" * 40, "d" * 40, "e" * 40): + assert loop.apply_merge_record( + state_root, json.loads(json.dumps(records[sha])), + recovery_exemptions=exemptions, + ) + loop.assert_recovery_consumed(state_root, "e" * 40, exemptions) + final_trees.append({ + path.relative_to(state_root).as_posix(): path.read_text(encoding="utf-8") + for path in sorted(state_root.rglob("*")) if path.is_file() + }) + assert loop.prepare_recovery_exemptions( + object(), "Flow-Research/workstream", repository_root=tmp_path, + state_root=state_root, target_sha="e" * 40, planned_shas=[], + ) == [] + assert final_trees[0] == final_trees[1] + assert protected_heads == ["1" * 40, "2" * 40, "3" * 40] * 2 + + for bad_plan in ( + ["d" * 40, "c" * 40, "e" * 40], + ["c" * 40, "e" * 40], + ["c" * 40, "d" * 40, "e" * 40, "f" * 40], + ): + state_root = tmp_path / f"bad-{len(bad_plan)}-{bad_plan[0][0]}" + loop.apply_merge_record(state_root, json.loads(json.dumps(base))) + with pytest.raises(loop.LoopMemoryError, match="exact ordered"): + loop.prepare_recovery_exemptions( + object(), "Flow-Research/workstream", repository_root=tmp_path, + state_root=state_root, target_sha="e" * 40, planned_shas=bad_plan, + ) + + broken = json.loads(json.dumps(recovered_r1)) + broken["source"]["first_parent_sha"] = "b" * 40 + records["d" * 40] = broken + state_root = tmp_path / "bad-parent" + loop.apply_merge_record(state_root, json.loads(json.dumps(base))) + 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="e" * 40, + planned_shas=["c" * 40, "d" * 40, "e" * 40], + ) + + def test_prepare_recovery_rejects_non_exact_plan( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: diff --git a/scripts/update_post_merge_memory.py b/scripts/update_post_merge_memory.py index 8726ed77..09dffaa6 100644 --- a/scripts/update_post_merge_memory.py +++ b/scripts/update_post_merge_memory.py @@ -44,6 +44,9 @@ REPOSITORY_PATTERN = re.compile(r"^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$") SHA_PATTERN = re.compile(r"^[0-9a-f]{40}$") SHA256_PATTERN = re.compile(r"^[0-9a-f]{64}$") +RFC3339_PATTERN = re.compile( + r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$" +) REQUIRED_CHECKS = ("agent-gates", "test", "CodeRabbit") REQUIRED_METADATA_KEYS = { "schema_version", @@ -815,22 +818,48 @@ def _validate_protected_actions_checks( raise LoopMemoryError("planning intake check-run evidence is incomplete") for name in ("agent-gates", "test"): matches = [item for item in runs if isinstance(item, dict) and item.get("name") == name] - if len(matches) != 1: - raise LoopMemoryError(f"planning intake check {name} is missing or duplicated") - item = matches[0] - app = item.get("app") - if ( - item.get("head_sha") != head_sha - or item.get("status") != "completed" - or item.get("conclusion") != "success" - or not item.get("completed_at") - or not isinstance(app, dict) - or app.get("id") != GITHUB_ACTIONS_APP_ID - or app.get("slug") != GITHUB_ACTIONS_APP_SLUG - ): + if not matches: + raise LoopMemoryError(f"planning intake check {name} is missing") + candidates: list[tuple[datetime, int, dict[str, Any]]] = [] + seen_ids: set[int] = set() + for item in matches: + app = item.get("app") + check_id = item.get("id") + started_at = _rfc3339_instant(item.get("started_at")) + completed_at = _rfc3339_instant(item.get("completed_at")) + if ( + type(check_id) is not int + or check_id <= 0 + or check_id in seen_ids + or item.get("head_sha") != head_sha + or item.get("status") != "completed" + or not isinstance(item.get("conclusion"), str) + or completed_at < started_at + or not isinstance(app, dict) + or app.get("id") != GITHUB_ACTIONS_APP_ID + or app.get("slug") != GITHUB_ACTIONS_APP_SLUG + ): + raise LoopMemoryError(f"planning intake check {name} has invalid provenance") + seen_ids.add(check_id) + candidates.append((started_at, check_id, item)) + item = max(candidates, key=lambda candidate: (candidate[0], candidate[1]))[2] + if item.get("conclusion") != "success": raise LoopMemoryError(f"planning intake check {name} has invalid provenance") +def _rfc3339_instant(value: Any) -> datetime: + """Return one timezone-aware RFC3339 instant or fail closed.""" + if not isinstance(value, str) or not RFC3339_PATTERN.fullmatch(value): + raise LoopMemoryError("planning intake check timestamp is invalid") + try: + parsed = datetime.fromisoformat(value.replace("Z", "+00:00")) + except ValueError as exc: + raise LoopMemoryError("planning intake check timestamp is invalid") from exc + if parsed.tzinfo is None or parsed.utcoffset() is None: + raise LoopMemoryError("planning intake check timestamp is invalid") + return parsed + + def _tree_entries( client: GitHubClient, repository: str, tree_sha: str, label: str ) -> dict[str, tuple[str, str, str]]: @@ -2079,15 +2108,15 @@ def _validate_recovery_policy(payload: Any) -> dict[str, Any]: version = payload.get("schema_version") if version == 2 and "recovered_merge" in payload: raise LoopMemoryError("recovery policy is unsupported") - expected = ( - {"schema_version", "activation", "recovered_merge"} - if version == 1 - else {"schema_version", "activation", "mode"} - ) + expected = { + 1: {"schema_version", "activation", "recovered_merge"}, + 2: {"schema_version", "activation", "mode"}, + 3: {"schema_version", "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} or not isinstance(activation, dict): + if version not in {1, 2, 3} 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") @@ -2097,6 +2126,39 @@ def _validate_recovery_policy(payload: Any) -> dict[str, Any]: if payload.get("mode") != "exact_single_target": raise LoopMemoryError("recovery policy mode is unsupported") return json.loads(_canonical_json(payload)) + if version == 3: + recovered_merges = payload.get("recovered_merges") + if not isinstance(recovered_merges, list) or not 1 <= len(recovered_merges) <= 2: + raise LoopMemoryError("recovered merge inventory is invalid") + identities: set[tuple[str, str, int]] = set() + merge_shas: set[str] = set() + for recovered in recovered_merges: + if not isinstance(recovered, dict) or set(recovered) != { + "initiative_id", "chunk_id", "pr_number", "merge_sha" + }: + raise LoopMemoryError("recovered merge identity is invalid") + if ( + not _is_valid_exemption_id( + recovered.get("initiative_id"), recovered.get("chunk_id") + ) + or type(recovered.get("pr_number")) is not int + or recovered["pr_number"] <= 0 + ): + raise LoopMemoryError("recovered merge identity is invalid") + _validate_sha(recovered.get("merge_sha")) + identity = ( + recovered["initiative_id"], recovered["chunk_id"], recovered["pr_number"] + ) + if identity in identities or recovered["merge_sha"] in merge_shas: + raise LoopMemoryError("recovered merge inventory is not unique") + if ( + recovered["initiative_id"] == activation["initiative_id"] + and recovered["chunk_id"] == activation["chunk_id"] + ): + raise LoopMemoryError("recovery activation collides with recovered merge") + identities.add(identity) + merge_shas.add(recovered["merge_sha"]) + return json.loads(_canonical_json(payload)) recovered = payload.get("recovered_merge") if not isinstance(recovered, dict) or set(recovered) != { "initiative_id", "chunk_id", "pr_number", "merge_sha" @@ -2167,6 +2229,49 @@ 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"] == 3: + recovered_policies = policy["recovered_merges"] + expected_shas = [item["merge_sha"] for item in recovered_policies] + [target_sha] + if planned_shas != expected_shas: + raise LoopMemoryError("recovery plan is not the exact ordered sequence") + recovered_records = [ + collect_merge_record(client, repository, item["merge_sha"]) + for item in recovered_policies + ] + for recovered_policy, recovered_record in zip( + recovered_policies, recovered_records, strict=True + ): + if _record_exemption(recovered_record) != { + "initiative_id": recovered_policy["initiative_id"], + "chunk_id": recovered_policy["chunk_id"], + "pr_number": recovered_policy["pr_number"], + }: + raise LoopMemoryError("recovered merge does not match its certificate") + signed_main = ( + state.get("event", {}).get("main_sha") + if _event_type(state) in {"start", "cancel"} + else state.get("source", {}).get("main_sha") + ) + records = [*recovered_records, target_record] + expected_parent = signed_main + for merge_sha, record in zip(planned_shas, records, strict=True): + source = record.get("source", {}) + if ( + source.get("main_sha") != merge_sha + or source.get("first_parent_sha") != expected_parent + ): + raise LoopMemoryError("recovery plan is not first-parent adjacent") + expected_parent = merge_sha + _validate_protected_actions_checks( + client, repository, source.get("head_sha") + ) + if not record.get("checks", {}).get("all_required_passed"): + raise LoopMemoryError("recovery required checks did not pass") + exemptions = [_record_exemption(record) for record in records] + 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 recovered = policy["recovered_merge"] if planned_shas != [recovered["merge_sha"], target_sha]: raise LoopMemoryError("recovery plan is not the exact two-merge sequence") From 69d704e376ace21e38cd92d8416ce5962ad04e42 Mon Sep 17 00:00:00 2001 From: Abiorh001 Date: Thu, 23 Jul 2026 08:01:18 +0100 Subject: [PATCH 2/7] fix(loop-memory): validate serialized recovery authority --- scripts/test_agent_gates.py | 9 ++++ scripts/test_update_post_merge_memory.py | 69 +++++++++++++++++++++++- scripts/update_post_merge_memory.py | 33 ++++++++---- 3 files changed, 100 insertions(+), 11 deletions(-) diff --git a/scripts/test_agent_gates.py b/scripts/test_agent_gates.py index 60b956ad..7f677639 100644 --- a/scripts/test_agent_gates.py +++ b/scripts/test_agent_gates.py @@ -2233,6 +2233,7 @@ def get_json(self, _path: str): {**older, "status": "queued", "completed_at": None}, {**older, "id": True}, {**older, "started_at": "2026-07-23 05:00:00"}, + {**older, "conclusion": "forged"}, ) for bad in poisoned: for ordered in ([bad, newer, test_run], [test_run, newer, bad]): @@ -2251,6 +2252,14 @@ def get_json(self, _path: str): ), "invalid provenance", ) + assert_loop_error( + updater, + lambda: updater._validate_protected_actions_checks( + CheckClient([older, newer, {**test_run, "id": newer["id"]}]), + "Flow-Research/workstream", head_sha, + ), + "invalid provenance", + ) def test_planning_intake_collection_binds_paths_trees_and_check_sources() -> None: diff --git a/scripts/test_update_post_merge_memory.py b/scripts/test_update_post_merge_memory.py index e965bfb3..e19ee51e 100644 --- a/scripts/test_update_post_merge_memory.py +++ b/scripts/test_update_post_merge_memory.py @@ -507,6 +507,12 @@ def _recovery_policy_v3() -> dict: lambda policy: policy["recovered_merges"][1].update( initiative_id="WS-ENG-007", chunk_id="WS-ENG-007-PLAN", pr_number=187 ), + lambda policy: policy["recovered_merges"][1].update( + initiative_id="WS-ENG-007", chunk_id="WS-ENG-007-PLAN", pr_number=999 + ), + lambda policy: policy["recovered_merges"][1].update( + initiative_id="WS-ENG-008", chunk_id="WS-ENG-008-01", pr_number=187 + ), lambda policy: policy.update( activation={"initiative_id": "WS-ENG-007", "chunk_id": "WS-ENG-007-PLAN"} ), @@ -739,12 +745,17 @@ def test_prepare_recovery_v3_binds_three_adjacent_merges_and_is_deterministic( planned_shas=["c" * 40, "d" * 40, "e" * 40], ) assert [item["pr_number"] for item in exemptions] == [187, 188, 189] + serialized = json.dumps({"schema_version": 1, "exemptions": exemptions}) for sha in ("c" * 40, "d" * 40, "e" * 40): + reloaded = loop._validate_recovery_exemptions(json.loads(serialized)) assert loop.apply_merge_record( state_root, json.loads(json.dumps(records[sha])), - recovery_exemptions=exemptions, + recovery_exemptions=reloaded, ) - loop.assert_recovery_consumed(state_root, "e" * 40, exemptions) + loop.assert_recovery_consumed( + state_root, "e" * 40, + loop._validate_recovery_exemptions(json.loads(serialized)), + ) final_trees.append({ path.relative_to(state_root).as_posix(): path.read_text(encoding="utf-8") for path in sorted(state_root.rglob("*")) if path.is_file() @@ -755,6 +766,15 @@ def test_prepare_recovery_v3_binds_three_adjacent_merges_and_is_deterministic( ) == [] assert final_trees[0] == final_trees[1] assert protected_heads == ["1" * 40, "2" * 40, "3" * 40] * 2 + four = { + "schema_version": 1, + "exemptions": [ + {"initiative_id": f"WS-ENG-00{index}", "chunk_id": f"WS-ENG-00{index}-01", "pr_number": 180 + index} + for index in range(1, 5) + ], + } + with pytest.raises(loop.LoopMemoryError, match="unique and bounded"): + loop._validate_recovery_exemptions(four) for bad_plan in ( ["d" * 40, "c" * 40, "e" * 40], @@ -991,6 +1011,51 @@ def test_recovery_cli_round_trip_consumes_inventory( ]) == 0 +def test_recovery_v3_cli_round_trip_reloads_three_entry_inventory( + tmp_path: Path, + monkeypatch: pytest.MonkeyPatch, + capsys: pytest.CaptureFixture[str], +) -> None: + state_root = tmp_path / "state-v3" + base = _record() + base["legacy_exemptions"] = [] + loop.apply_merge_record(state_root, base) + records = { + "c" * 40: _merge_record("WS-ENG-007", "WS-ENG-007-PLAN", 187, "c" * 40, "a" * 40), + "d" * 40: _merge_record("WS-ENG-007", "WS-ENG-007-00R1", 188, "d" * 40, "c" * 40), + "e" * 40: _merge_record("WS-ENG-007", "WS-ENG-007-00R2", 189, "e" * 40, "d" * 40), + } + monkeypatch.setenv("GITHUB_TOKEN", "token") + monkeypatch.setattr(loop, "_assert_state_branch", lambda _root: None) + monkeypatch.setattr(loop, "GitHubClient", lambda _token, _url: object()) + monkeypatch.setattr(loop, "_load_json_at_commit", lambda *_args: _recovery_policy_v3()) + monkeypatch.setattr(loop, "collect_merge_record", lambda _client, _repository, sha: records[sha]) + monkeypatch.setattr(loop, "_validate_protected_actions_checks", lambda *_args: None) + plan_file = tmp_path / "plan-v3" + plan_file.write_text(f"{'c' * 40}\n{'d' * 40}\n{'e' * 40}\n", encoding="utf-8") + assert loop.main([ + "prepare-recovery", "--repository", "Flow-Research/workstream", + "--repository-root", str(tmp_path), "--state-root", str(state_root), + "--target-sha", "e" * 40, "--plan-file", str(plan_file), + ]) == 0 + recovery_file = tmp_path / "recovery-v3.json" + recovery_file.write_text(capsys.readouterr().out, encoding="utf-8") + assert len(loop._validate_recovery_exemptions(json.loads(recovery_file.read_text()))) == 3 + common = [ + "--repository", "Flow-Research/workstream", "--repository-root", str(tmp_path), + "--state-root", str(state_root), "--branch-root", str(state_root), + ] + for sha in ("c" * 40, "d" * 40, "e" * 40): + assert loop.main([ + "update", *common, "--merge-sha", sha, + "--recovery-file", str(recovery_file), + ]) == 0 + assert loop.main([ + "assert-recovery-consumed", "--state-root", str(state_root), + "--target-sha", "e" * 40, "--recovery-file", str(recovery_file), + ]) == 0 + + @dataclass class _Client: run: object diff --git a/scripts/update_post_merge_memory.py b/scripts/update_post_merge_memory.py index 09dffaa6..66a08eda 100644 --- a/scripts/update_post_merge_memory.py +++ b/scripts/update_post_merge_memory.py @@ -71,6 +71,10 @@ ) GITHUB_ACTIONS_APP_ID = 15368 GITHUB_ACTIONS_APP_SLUG = "github-actions" +CHECK_RUN_CONCLUSIONS = frozenset({ + "action_required", "cancelled", "failure", "neutral", "skipped", "stale", + "success", "timed_out", +}) class LoopMemoryError(RuntimeError): @@ -816,12 +820,12 @@ def _validate_protected_actions_checks( total = payload.get("total_count") if isinstance(payload, dict) else None if not isinstance(runs, list) or type(total) is not int or total != len(runs): raise LoopMemoryError("planning intake check-run evidence is incomplete") + seen_ids: set[int] = set() for name in ("agent-gates", "test"): matches = [item for item in runs if isinstance(item, dict) and item.get("name") == name] if not matches: raise LoopMemoryError(f"planning intake check {name} is missing") candidates: list[tuple[datetime, int, dict[str, Any]]] = [] - seen_ids: set[int] = set() for item in matches: app = item.get("app") check_id = item.get("id") @@ -833,7 +837,7 @@ def _validate_protected_actions_checks( or check_id in seen_ids or item.get("head_sha") != head_sha or item.get("status") != "completed" - or not isinstance(item.get("conclusion"), str) + or item.get("conclusion") not in CHECK_RUN_CONCLUSIONS or completed_at < started_at or not isinstance(app, dict) or app.get("id") != GITHUB_ACTIONS_APP_ID @@ -2040,7 +2044,14 @@ def _validate_recovery_exemptions(payload: Any) -> list[dict[str, Any]]: (item["initiative_id"], item["chunk_id"], item["pr_number"]) for item in chronological ] - if len(chronological) > 2 or len(identities) != len(set(identities)): + chunk_identities = [(item[0], item[1]) for item in identities] + pr_numbers = [item[2] for item in identities] + if ( + len(chronological) > 3 + or len(identities) != len(set(identities)) + or len(chunk_identities) != len(set(chunk_identities)) + or len(pr_numbers) != len(set(pr_numbers)) + ): raise LoopMemoryError("recovery exemption inventory is not unique and bounded") return chronological @@ -2130,7 +2141,8 @@ def _validate_recovery_policy(payload: Any) -> dict[str, Any]: recovered_merges = payload.get("recovered_merges") if not isinstance(recovered_merges, list) or not 1 <= len(recovered_merges) <= 2: raise LoopMemoryError("recovered merge inventory is invalid") - identities: set[tuple[str, str, int]] = set() + chunk_identities: set[tuple[str, str]] = set() + pr_numbers: set[int] = set() merge_shas: set[str] = set() for recovered in recovered_merges: if not isinstance(recovered, dict) or set(recovered) != { @@ -2146,17 +2158,20 @@ def _validate_recovery_policy(payload: Any) -> dict[str, Any]: ): raise LoopMemoryError("recovered merge identity is invalid") _validate_sha(recovered.get("merge_sha")) - identity = ( - recovered["initiative_id"], recovered["chunk_id"], recovered["pr_number"] - ) - if identity in identities or recovered["merge_sha"] in merge_shas: + chunk_identity = (recovered["initiative_id"], recovered["chunk_id"]) + if ( + chunk_identity in chunk_identities + or recovered["pr_number"] in pr_numbers + or recovered["merge_sha"] in merge_shas + ): raise LoopMemoryError("recovered merge inventory is not unique") if ( recovered["initiative_id"] == activation["initiative_id"] and recovered["chunk_id"] == activation["chunk_id"] ): raise LoopMemoryError("recovery activation collides with recovered merge") - identities.add(identity) + chunk_identities.add(chunk_identity) + pr_numbers.add(recovered["pr_number"]) merge_shas.add(recovered["merge_sha"]) return json.loads(_canonical_json(payload)) recovered = payload.get("recovered_merge") From bb3f03b3b9026a7eb3a9adb40e657e07c4eafac3 Mon Sep 17 00:00:00 2001 From: Abiorh001 Date: Thu, 23 Jul 2026 08:03:38 +0100 Subject: [PATCH 3/7] fix(loop-memory): version three-entry recovery transport --- .../WS-ENG-007-00R2-canonical-check-recovery.md | 4 ++++ docs/operations_post_merge_memory.md | 3 +++ scripts/test_update_post_merge_memory.py | 14 +++++++++++--- scripts/update_post_merge_memory.py | 13 ++++++++++--- 4 files changed, 28 insertions(+), 6 deletions(-) diff --git a/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/chunks/WS-ENG-007-00R2-canonical-check-recovery.md b/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/chunks/WS-ENG-007-00R2-canonical-check-recovery.md index 6f2e1496..dd768067 100644 --- a/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/chunks/WS-ENG-007-00R2-canonical-check-recovery.md +++ b/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/chunks/WS-ENG-007-00R2-canonical-check-recovery.md @@ -97,6 +97,10 @@ docs/operations_post_merge_memory.md before signing, serializes none, and rejects missing, extra, reordered, repeated, intervening, wrong-parent, wrong-identity, check-failure, partial-consumption, and replay cases. +- [ ] Ephemeral recovery-file schema v1 retains its original maximum of two + entries. Transport schema v2 is emitted only for the exact three-entry + schema-v3 recovery result, requires exactly three unique entries, and + rejects unknown versions, partial inventories, and four entries. - [ ] Applying the exact authenticated plan to two fresh copies of the same starting state produces byte-identical complete generated closed trees, including signing input. Replay from completed state proves no exemption diff --git a/docs/operations_post_merge_memory.md b/docs/operations_post_merge_memory.md index 9356fea5..bab9898e 100644 --- a/docs/operations_post_merge_memory.md +++ b/docs/operations_post_merge_memory.md @@ -291,6 +291,9 @@ repeated, intervening, or non-adjacent merge fails. All temporary identities are consumed before signing, never serialize into the generated tree, and are inert on replay. Recovery starts no implementation: `WS-ENG-007-01` and `WS-ENG-006-01` each remain stopped pending their own explicit signed start. +The ephemeral recovery-file transport preserves schema v1 at its original +two-entry maximum; schema v2 is emitted only for this exact three-entry result +and requires exactly three unique entries on every reload. ## Historical WS-ENG-006 Exact Root Repair diff --git a/scripts/test_update_post_merge_memory.py b/scripts/test_update_post_merge_memory.py index e19ee51e..641af4ec 100644 --- a/scripts/test_update_post_merge_memory.py +++ b/scripts/test_update_post_merge_memory.py @@ -745,7 +745,7 @@ def test_prepare_recovery_v3_binds_three_adjacent_merges_and_is_deterministic( planned_shas=["c" * 40, "d" * 40, "e" * 40], ) assert [item["pr_number"] for item in exemptions] == [187, 188, 189] - serialized = json.dumps({"schema_version": 1, "exemptions": exemptions}) + serialized = json.dumps({"schema_version": 2, "exemptions": exemptions}) for sha in ("c" * 40, "d" * 40, "e" * 40): reloaded = loop._validate_recovery_exemptions(json.loads(serialized)) assert loop.apply_merge_record( @@ -767,7 +767,7 @@ def test_prepare_recovery_v3_binds_three_adjacent_merges_and_is_deterministic( assert final_trees[0] == final_trees[1] assert protected_heads == ["1" * 40, "2" * 40, "3" * 40] * 2 four = { - "schema_version": 1, + "schema_version": 2, "exemptions": [ {"initiative_id": f"WS-ENG-00{index}", "chunk_id": f"WS-ENG-00{index}-01", "pr_number": 180 + index} for index in range(1, 5) @@ -775,6 +775,11 @@ def test_prepare_recovery_v3_binds_three_adjacent_merges_and_is_deterministic( } with pytest.raises(loop.LoopMemoryError, match="unique and bounded"): loop._validate_recovery_exemptions(four) + three = {"schema_version": 1, "exemptions": four["exemptions"][:3]} + with pytest.raises(loop.LoopMemoryError, match="unique and bounded"): + loop._validate_recovery_exemptions(three) + with pytest.raises(loop.LoopMemoryError, match="unsupported"): + loop._validate_recovery_exemptions({**three, "schema_version": 3}) for bad_plan in ( ["d" * 40, "c" * 40, "e" * 40], @@ -993,6 +998,7 @@ def test_recovery_cli_round_trip_consumes_inventory( ]) == 0 recovery_file = tmp_path / "recovery.json" recovery_file.write_text(capsys.readouterr().out, encoding="utf-8") + assert json.loads(recovery_file.read_text())["schema_version"] == 1 common = [ "--repository", "Flow-Research/workstream", "--repository-root", str(tmp_path), "--state-root", str(state_root), "--branch-root", str(state_root), @@ -1040,7 +1046,9 @@ def test_recovery_v3_cli_round_trip_reloads_three_entry_inventory( ]) == 0 recovery_file = tmp_path / "recovery-v3.json" recovery_file.write_text(capsys.readouterr().out, encoding="utf-8") - assert len(loop._validate_recovery_exemptions(json.loads(recovery_file.read_text()))) == 3 + serialized = json.loads(recovery_file.read_text()) + assert serialized["schema_version"] == 2 + assert len(loop._validate_recovery_exemptions(serialized)) == 3 common = [ "--repository", "Flow-Research/workstream", "--repository-root", str(tmp_path), "--state-root", str(state_root), "--branch-root", str(state_root), diff --git a/scripts/update_post_merge_memory.py b/scripts/update_post_merge_memory.py index 66a08eda..b717c5cc 100644 --- a/scripts/update_post_merge_memory.py +++ b/scripts/update_post_merge_memory.py @@ -2029,9 +2029,12 @@ def _validate_recovery_exemptions(payload: Any) -> list[dict[str, Any]]: or not isinstance(payload.get("exemptions"), list) ): raise LoopMemoryError("recovery exemption inventory has an invalid schema") + version = payload.get("schema_version") + if version not in {1, 2}: + raise LoopMemoryError("recovery exemption inventory is unsupported") chronological = json.loads(_canonical_json(payload["exemptions"])) _validate_legacy_exemptions({ - "schema_version": payload.get("schema_version"), + "schema_version": 1, "exemptions": sorted( chronological, key=lambda item: ( @@ -2047,7 +2050,8 @@ def _validate_recovery_exemptions(payload: Any) -> list[dict[str, Any]]: chunk_identities = [(item[0], item[1]) for item in identities] pr_numbers = [item[2] for item in identities] if ( - len(chronological) > 3 + (version == 1 and len(chronological) > 2) + or (version == 2 and len(chronological) != 3) or len(identities) != len(set(identities)) or len(chunk_identities) != len(set(chunk_identities)) or len(pr_numbers) != len(set(pr_numbers)) @@ -3278,7 +3282,10 @@ def main(argv: list[str] | None = None) -> int: repository_root=args.repository_root, state_root=args.state_root, target_sha=args.target_sha, planned_shas=planned_shas, ) - print(_canonical_json({"schema_version": 1, "exemptions": exemptions})) + transport_version = 2 if len(exemptions) == 3 else 1 + print(_canonical_json({ + "schema_version": transport_version, "exemptions": exemptions + })) elif args.command == "assert-recovery-consumed": exemptions = _validate_recovery_exemptions(_load_json(args.recovery_file)) assert_recovery_consumed(args.state_root, args.target_sha, exemptions) From 020d2c288325e8654203115e448241655fd85604 Mon Sep 17 00:00:00 2001 From: Abiorh001 Date: Thu, 23 Jul 2026 08:05:52 +0100 Subject: [PATCH 4/7] docs(loop-memory): record canonical recovery evidence --- ...S-ENG-007-00R2-external-review-response.md | 22 ++++++ ...S-ENG-007-00R2-internal-review-evidence.md | 78 +++++++++++++++++++ .../WS-ENG-007-00R2-pr-trust-bundle.md | 47 +++++++++++ 3 files changed, 147 insertions(+) create mode 100644 .agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/reviews/WS-ENG-007-00R2-external-review-response.md create mode 100644 .agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/reviews/WS-ENG-007-00R2-internal-review-evidence.md create mode 100644 .agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/reviews/WS-ENG-007-00R2-pr-trust-bundle.md diff --git a/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/reviews/WS-ENG-007-00R2-external-review-response.md b/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/reviews/WS-ENG-007-00R2-external-review-response.md new file mode 100644 index 00000000..e79156fc --- /dev/null +++ b/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/reviews/WS-ENG-007-00R2-external-review-response.md @@ -0,0 +1,22 @@ +# External Review Response: WS-ENG-007-00R2 + +## Comments addressed + +None before publication. + +## Comments deferred + +None. + +## Human decisions needed + +None before external review. + +## Commands rerun + +The complete deterministic command set is recorded in the internal evidence and +PR trust bundle. + +## Remaining risks + +CodeRabbit, GitHub Actions, and human review remain required external checks. diff --git a/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/reviews/WS-ENG-007-00R2-internal-review-evidence.md b/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/reviews/WS-ENG-007-00R2-internal-review-evidence.md new file mode 100644 index 00000000..ea269307 --- /dev/null +++ b/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/reviews/WS-ENG-007-00R2-internal-review-evidence.md @@ -0,0 +1,78 @@ +# Internal Review Evidence: WS-ENG-007-00R2 + +## Chunk + +`WS-ENG-007-00R2` - Canonical Check Evidence Recovery + +open sub-agent sessions: none + +valid findings addressed: yes + +## Reviewed Revision + +Reviewed code SHA: bb3f03b3b9026a7eb3a9adb40e657e07c4eafac3 + +Reviewed at: 2026-07-23T07:05:13Z + +Reviewer run IDs: senior-engineering=/root/eng006_senior_arch_docs; QA/test=/root/eng006_qa_ci_tests; security/auth=/root/eng006_security_ops_reuse; product/ops=/root/eng006_security_ops_reuse; architecture=/root/eng006_senior_arch_docs; docs=/root/eng006_senior_arch_docs; CI-integrity=/root/eng006_qa_ci_tests; reuse/dedup=/root/eng006_security_ops_reuse; test-delta=/root/eng006_qa_ci_tests + +The inherited session names identify the available reviewer pool. Every session +was explicitly assigned to this exact WS-ENG-007-00R2 revision. + +## Reviewer Results + +| Reviewer | Result | Blocking findings | +|---|---:|---| +| senior engineering | PASS AFTER FIXES | None | +| QA/test | PASS AFTER FIXES | None | +| security/auth | PASS AFTER FIXES | None | +| product/ops | PASS | None | +| architecture | PASS AFTER FIXES | None | +| CI integrity | PASS AFTER FIXES | None | +| docs | PASS AFTER FIXES | None | +| reuse/dedup | PASS | None | +| test delta | PASS AFTER FIXES | None | + +## Valid Findings Addressed + +- Changed rerun recency from completion time to parsed invocation start plus + strict positive check-run ID, preventing delayed older success from hiding a + newer failure. +- Validated every protected candidate, closed conclusion values, and enforced + check-run ID uniqueness across both protected names. +- Required exact protected provenance for PR #187, PR #188, and the activation + target. +- Closed policy schema v3 to at most two recovered merges with independent + chunk, PR, and SHA uniqueness and exact first-parent order. +- Exercised the actual serialized recovery-file boundary and corrected its + bound from two to three entries. +- Preserved recovery transport schema v1 at its historical two-entry maximum; + introduced schema v2 requiring exactly three entries. +- Added full prepare-to-file-to-three-updates-to-consumption CLI coverage, + over-bound/partial/unknown rejection, deterministic fresh-state comparison, + and replay protection. + +## Commands Run + +```bash +PYTEST_DISABLE_PLUGIN_AUTOLOAD=1 python3 -m pytest -q scripts/test_update_post_merge_memory.py scripts/test_agent_gates.py scripts/test_check_loop_memory_state.py +python3 scripts/test_agent_gates.py +python3 scripts/update_post_merge_memory.py validate-merge-intent --repository-root . --base-ref origin/main +python3 scripts/check_markdown_links.py +python3 scripts/check_stale_workstream_wording.py +git diff --check origin/main...HEAD +``` + +Results: 231 focused tests and 98 manual gate tests passed. All other commands +passed. Live PR #187 and PR #188 protected-check responses also validated under +the canonical selector. + +## Remaining Risk + +Recovery remains first-parent adjacency-bound. Any intervening `main` merge +invalidates the certificate and requires a fresh reviewed plan. + +## Stop Condition + +Neither successor is active. `WS-ENG-007-01` and `WS-ENG-006-01` each require +their own explicit signed start after successful reconciliation. diff --git a/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/reviews/WS-ENG-007-00R2-pr-trust-bundle.md b/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/reviews/WS-ENG-007-00R2-pr-trust-bundle.md new file mode 100644 index 00000000..8dec4774 --- /dev/null +++ b/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/reviews/WS-ENG-007-00R2-pr-trust-bundle.md @@ -0,0 +1,47 @@ +# PR Trust Bundle: WS-ENG-007-00R2 + +## Goal + +Make signed loop-memory reconciliation stable under legitimate trusted check +reruns and recover the exact PR #187 → PR #188 → 00R2 sequence once. + +## Design + +Every same-name protected check candidate is validated before selection. +Completed trusted invocations are ordered by timezone-normalized `started_at` +and strict positive check-run ID. Completion time cannot reorder invocations; +the newest invocation must succeed. Any malformed, foreign, incomplete, +wrong-head, unknown-conclusion, or duplicate-ID evidence fails closed. + +Policy schema v3 names at most two recovered merges and the activation. The +production certificate pins PR #187, PR #188, and direct-next 00R2 in exact +first-parent order. Protected provenance is checked on every head. Temporary +identities are consumed before signing and never serialize or replay. + +Recovery-file schema v1 remains limited to two identities. Schema v2 is emitted +only for an exact three-identity result and requires exactly three on every +reload. + +## Evidence + +- 231 focused tests passed. +- 98 manual agent-gate tests passed. +- The real PR #187 duplicate-success and PR #188 check histories validate. +- Legacy schema-v1 and new schema-v2 CLI recovery round trips pass. +- Merge intent, Markdown links, stale wording, and committed diff checks pass. +- All nine internal tracks passed exact reviewed SHA + `bb3f03b3b9026a7eb3a9adb40e657e07c4eafac3` after findings were repaired. +- No workflow, required-check name, coverage, dependency, signing key, secret, + permission, product behavior, or human merge authority changed. + +## Human Review Focus + +Confirm newer invocation failure cannot be hidden, all three merge heads receive +protected provenance validation, and recovery transport/policy versions remain +closed and non-reusable. + +## Human Merge Ownership + +Only the user may approve and merge this PR. No other PR may merge first. After +signed reconciliation, both successors remain stopped pending separate explicit +starts. From 345c83c0393f3a2957d9d03f58b93eac39d65cb2 Mon Sep 17 00:00:00 2001 From: Abiorh001 Date: Thu, 23 Jul 2026 08:21:29 +0100 Subject: [PATCH 5/7] docs(loop-memory): address CodeRabbit markdown review --- .../WS-ENG-007-00R2-external-review-response.md | 14 +++++++++----- .../reviews/WS-ENG-007-00R2-pr-trust-bundle.md | 6 ++++++ docs/operations_post_merge_memory.md | 4 ++-- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/reviews/WS-ENG-007-00R2-external-review-response.md b/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/reviews/WS-ENG-007-00R2-external-review-response.md index e79156fc..a1ce588a 100644 --- a/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/reviews/WS-ENG-007-00R2-external-review-response.md +++ b/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/reviews/WS-ENG-007-00R2-external-review-response.md @@ -2,7 +2,8 @@ ## Comments addressed -None before publication. +- CodeRabbit minor: reflowed the PR `#187` reference in the operations runbook + so Markdown does not parse it as a malformed ATX heading. ## Comments deferred @@ -10,13 +11,16 @@ None. ## Human decisions needed -None before external review. +None. ## Commands rerun -The complete deterministic command set is recorded in the internal evidence and -PR trust bundle. +```bash +python3 scripts/check_markdown_links.py +python3 scripts/check_stale_workstream_wording.py +git diff --check origin/main...HEAD +``` ## Remaining risks -CodeRabbit, GitHub Actions, and human review remain required external checks. +No new risk. Exact adjacency and fresh exact-head checks remain blocking. diff --git a/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/reviews/WS-ENG-007-00R2-pr-trust-bundle.md b/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/reviews/WS-ENG-007-00R2-pr-trust-bundle.md index 8dec4774..c5be10ab 100644 --- a/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/reviews/WS-ENG-007-00R2-pr-trust-bundle.md +++ b/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/reviews/WS-ENG-007-00R2-pr-trust-bundle.md @@ -34,6 +34,12 @@ reload. - No workflow, required-check name, coverage, dependency, signing key, secret, permission, product behavior, or human merge authority changed. +## External Review + +CodeRabbit raised one valid minor Markdown finding. The PR `#187` reference in +the operations runbook was reflowed so it cannot be parsed as a malformed +heading. No external finding was deferred. + ## Human Review Focus Confirm newer invocation failure cannot be hidden, all three merge heads receive diff --git a/docs/operations_post_merge_memory.md b/docs/operations_post_merge_memory.md index bab9898e..f75fd572 100644 --- a/docs/operations_post_merge_memory.md +++ b/docs/operations_post_merge_memory.md @@ -263,8 +263,8 @@ on exact current `main`. ## WS-ENG-007 Canonical Check-Evidence Recovery -PR #188 repaired tree normalization but its post-merge run failed because PR -#187 contains two legitimate successful `agent-gates` reruns. Protected check +PR `#188` repaired tree normalization but its post-merge run failed because PR +`#187` contains two legitimate successful `agent-gates` reruns. Protected check validation now treats reruns as ordered evidence versions, not a cardinality error. Every same-name candidate must target the exact reviewed head, come from the pinned GitHub Actions application, contain a strict positive unique ID and From 0738421a9c948ced6c90a13f834cb0775226fa31 Mon Sep 17 00:00:00 2001 From: Abiorh001 Date: Thu, 23 Jul 2026 08:23:06 +0100 Subject: [PATCH 6/7] docs(loop-memory): bind CodeRabbit reapproval evidence --- .../reviews/WS-ENG-007-00R2-internal-review-evidence.md | 8 ++++++-- .../reviews/WS-ENG-007-00R2-pr-trust-bundle.md | 3 ++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/reviews/WS-ENG-007-00R2-internal-review-evidence.md b/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/reviews/WS-ENG-007-00R2-internal-review-evidence.md index ea269307..f1f71b8a 100644 --- a/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/reviews/WS-ENG-007-00R2-internal-review-evidence.md +++ b/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/reviews/WS-ENG-007-00R2-internal-review-evidence.md @@ -10,9 +10,9 @@ valid findings addressed: yes ## Reviewed Revision -Reviewed code SHA: bb3f03b3b9026a7eb3a9adb40e657e07c4eafac3 +Reviewed code SHA: 345c83c0393f3a2957d9d03f58b93eac39d65cb2 -Reviewed at: 2026-07-23T07:05:13Z +Reviewed at: 2026-07-23T07:22:46Z Reviewer run IDs: senior-engineering=/root/eng006_senior_arch_docs; QA/test=/root/eng006_qa_ci_tests; security/auth=/root/eng006_security_ops_reuse; product/ops=/root/eng006_security_ops_reuse; architecture=/root/eng006_senior_arch_docs; docs=/root/eng006_senior_arch_docs; CI-integrity=/root/eng006_qa_ci_tests; reuse/dedup=/root/eng006_security_ops_reuse; test-delta=/root/eng006_qa_ci_tests @@ -67,6 +67,10 @@ Results: 231 focused tests and 98 manual gate tests passed. All other commands passed. Live PR #187 and PR #188 protected-check responses also validated under the canonical selector. +All reviewer tracks reapproved exact head +`345c83c0393f3a2957d9d03f58b93eac39d65cb2` after the CodeRabbit Markdown +repair. That repair changes no executable code, tests, workflows, or authority. + ## Remaining Risk Recovery remains first-parent adjacency-bound. Any intervening `main` merge diff --git a/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/reviews/WS-ENG-007-00R2-pr-trust-bundle.md b/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/reviews/WS-ENG-007-00R2-pr-trust-bundle.md index c5be10ab..a49d1361 100644 --- a/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/reviews/WS-ENG-007-00R2-pr-trust-bundle.md +++ b/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/reviews/WS-ENG-007-00R2-pr-trust-bundle.md @@ -30,7 +30,8 @@ reload. - Legacy schema-v1 and new schema-v2 CLI recovery round trips pass. - Merge intent, Markdown links, stale wording, and committed diff checks pass. - All nine internal tracks passed exact reviewed SHA - `bb3f03b3b9026a7eb3a9adb40e657e07c4eafac3` after findings were repaired. + `345c83c0393f3a2957d9d03f58b93eac39d65cb2` after internal and CodeRabbit + findings were repaired. - No workflow, required-check name, coverage, dependency, signing key, secret, permission, product behavior, or human merge authority changed. From 55a11d9e0ae356734dbcce73564f5f570220a81b Mon Sep 17 00:00:00 2001 From: Abiorh001 Date: Thu, 23 Jul 2026 08:25:38 +0100 Subject: [PATCH 7/7] docs(loop-memory): record resolved external thread --- .../reviews/WS-ENG-007-00R2-external-review-response.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/reviews/WS-ENG-007-00R2-external-review-response.md b/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/reviews/WS-ENG-007-00R2-external-review-response.md index a1ce588a..42a929f4 100644 --- a/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/reviews/WS-ENG-007-00R2-external-review-response.md +++ b/.agent-loop/initiatives/WS-ENG-007-concurrent-pr-review-reconciliation/reviews/WS-ENG-007-00R2-external-review-response.md @@ -3,7 +3,8 @@ ## Comments addressed - CodeRabbit minor: reflowed the PR `#187` reference in the operations runbook - so Markdown does not parse it as a malformed ATX heading. + so Markdown does not parse it as a malformed ATX heading. The review thread + is resolved on PR #189. ## Comments deferred