Skip to content

Commit 9e1da73

Browse files
committed
fix: close durable loop reconciliation gaps
1 parent 21d0f73 commit 9e1da73

18 files changed

Lines changed: 1478 additions & 227 deletions

.codex/agents/echo-ui-review-adjudicator.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,8 @@ installed executable `"$ECHO_UI_LOOP_CONTROL_PLANE/scripts/with-github-identity"
1414
`gh pr review --comment`. Do not use the repository launcher, invoke the `.mjs`
1515
implementation directly, or run raw or mutating `gh api`,
1616
alter global authentication, or post through the executor identity.
17+
The body must include exactly one
18+
`<!-- issue-dev-loop:<run-id>:<finding-id>:adjudication:REJECT_FINDING:head:<sha> -->`
19+
marker for the existing current-head finding. This adjudication review is separate
20+
from, and must not include, a review-cycle marker.
1721
"""

loops/issue-dev-loop/LOOP.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,13 +103,13 @@ When the owner requests changes, verify the owner-authored GitHub comment or `CH
103103

104104
### 10. Complete
105105

106-
Only the remote owner-merge gate permits `completed`. Before a completion record exists, `prepare-finalization` remotely verifies the automation-authored Ready notification, a strictly later owner `ready_for_review` timeline event with no later redraft, an `APPROVED` review for the exact reviewed head SHA, and a merge performed by `codeacme17`. It then requires successful canonical `pr_completed` GitHub delivery whose body binds the merge SHA and whose remote timestamp is at or after the merge, and waits for the optional webhook attempt to settle within its bound. The identity router reserves `pr_completed` for the trusted finalization command. The finalization record binds distinct Ready and completion comment URLs/timestamps plus webhook outcome. Only after those prerequisites may the automation identity publish the record to the GitHub state journal. Publication, `observe-owner-merge`, terminal transition, and future reconciliation re-fetch that proof; a failed delivery or crash before publication leaves the active checkpoint resumable instead of suppressing it. Mutable local events are audit records, never authorization. Record merge SHA and timestamp, remove `loop:claimed`, update state, append the run summary, and retain links to published evidence. A closed unmerged PR is `cancelled`, not completed.
106+
Only the remote owner-merge gate permits `completed`. Before a completion record exists, `prepare-finalization` remotely verifies the automation-authored Ready notification, a strictly later owner `ready_for_review` timeline event with no later redraft, and the latest owner review for the exact reviewed head SHA; that latest review must be `APPROVED`, must be strictly after Ready, and must not be superseded by requested changes or dismissal. It also requires a merge performed by `codeacme17`. The command then requires successful canonical `pr_completed` GitHub delivery whose body binds the merge SHA and whose remote timestamp is at or after the merge, and waits for the optional webhook attempt to settle within its bound. The identity router reserves `pr_completed` for the trusted finalization command. The finalization record binds distinct Ready and completion comment URLs/timestamps plus webhook outcome. Failed and blocked records instead bind the latest durable `waiting_for_owner` checkpoint, its digest, the current pause timestamp, and the matching notification's remote timestamp; stale notifications from earlier pauses cannot terminate or suppress the active checkpoint. Only after those prerequisites may the automation identity publish the record to the GitHub state journal. Publication, `observe-owner-merge`, terminal transition, and future reconciliation re-fetch that proof; a failed delivery or crash before publication leaves the active checkpoint resumable instead of suppressing it. Mutable local events are audit records, never authorization. Record merge SHA and timestamp, remove `loop:claimed`, update state, append the run summary, and retain links to published evidence. A closed unmerged PR is `cancelled`, not completed.
107107

108108
## State and history
109109

110110
Keep `state.md` small and deliberate. It may be rewritten and contains only active runs, open PRs, blockers, follow-ups, current hypotheses, and learned constraints.
111111

112-
`logs/index.jsonl` is append-only and contains compact finalized-run summaries plus reconciliation tombstones for rows that lack a durable counterpart; metrics ignore tombstoned rows. `logs/triggers.jsonl` is the append-only record of cheap trigger decisions, including successful no-ops and resumes. The dedicated GitHub state-journal issue is the durable cross-worktree source for active checkpoints, terminal records, and pending/completed evolve sessions. After every durable run phase, publish the exact `prepare-checkpoint` body and validate it with `record-checkpoint`; the next phase re-fetches that automation-authored comment and refuses stale, absent, edited, or locally forged proof. Each compact checkpoint also embeds the digest-bound `$implement` result, evidence manifest, review result, and finalization record referenced by its event chain, when present, so later gates remain resumable. Terminal comments supersede active checkpoints only after finalization reconciliation validates them, including remote owner proof for completion. `loopctl reconcile` discovers resumable state, excludes local terminal rows without verified remote records, rebuilds evolve request/completion state, and recomputes metrics from verified journal history; it does not write run state into an arbitrary checkout. The orchestrator creates a clean worktree at the recorded exact branch/head, then `restore-checkpoint` verifies both before restoring run files and required small artifacts. A resumable run is returned as `workType: resume` before new issue selection. Commit sanitized `run.json`, pre-publication `events.jsonl`, summaries, and relevant screenshots to the issue branch so they are reviewable in its PR. The exact-head CI manifest and full proof travel in an Actions artifact. Keep raw local command output and large recordings in ignored `raw/` or `test-results/` directories. GitHub journal comments, PR reviews, workflow artifacts, and merge metadata are authoritative; local indexes and metrics are reconstructable caches.
112+
`logs/index.jsonl` is append-only and contains compact finalized-run summaries plus reconciliation tombstones for rows that lack a durable counterpart; metrics ignore tombstoned rows. A durable record may be restored after its tombstone, but duplicate active finalizations, consecutive tombstones, and conflicting restorations are invalid. `logs/triggers.jsonl` is the append-only record of cheap trigger decisions, including successful no-ops and resumes. The dedicated GitHub state-journal issue is the durable cross-worktree source for active checkpoints, terminal records, and pending/completed evolve sessions. After every durable run phase, publish the exact `prepare-checkpoint` body and validate it with `record-checkpoint`; the next phase re-fetches that automation-authored comment and refuses stale, absent, edited, or locally forged proof. Each compact checkpoint also embeds the digest-bound `$implement` result, evidence manifest, review result, and finalization record referenced by its event chain, when present, so later gates remain resumable. Terminal comments supersede active checkpoints only after finalization reconciliation validates them, including remote owner proof for completion and current-pause proof for failed/blocked runs. `loopctl reconcile` discovers resumable state, excludes local terminal rows without verified remote records, rebuilds evolve request/completion state, and recomputes metrics from verified journal history; it does not write run state into an arbitrary checkout. The orchestrator creates a clean worktree at the recorded exact branch/head, then `restore-checkpoint` verifies both before restoring run files and required small artifacts. A resumable run is returned as `workType: resume` before new issue selection. Commit sanitized `run.json`, pre-publication `events.jsonl`, summaries, and relevant screenshots to the issue branch so they are reviewable in its PR. The exact-head CI manifest and full proof travel in an Actions artifact. Keep raw local command output and large recordings in ignored `raw/` or `test-results/` directories. GitHub journal comments, PR reviews, workflow artifacts, and merge metadata are authoritative; local indexes and metrics are reconstructable caches.
113113

114114
Never log secrets, full environment dumps, cookies, auth headers, private user data, or raw prompts containing sensitive information.
115115

loops/issue-dev-loop/SKILL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ Run verification appropriate to the change and require `pnpm verify` before the
6161

6262
Before publishing the final review round, write the complete cycle result with an unassigned final `reviewUrl`, then run `loopctl.mjs review-digest --result <absolute-path>`. Add the returned marker to the final review body, publish the non-approving review, replace the unassigned URL with GitHub's actual review URL, and confirm `review-digest` is unchanged. After all responses are posted, run `loopctl.mjs record-review --run-id <id> --result <absolute-path> --review-url <github-review-url>`. The publication digest deliberately canonicalizes GitHub-assigned review URLs while the stored full-file digest still protects the final artifact. Both evidence and review gates must name the current PR head. Keep the PR Draft, emit a blocking `pr_ready_for_review` notification asking `codeacme17` to mark it Ready and review it, then transition from `waiting_for_owner` to `awaiting_owner_review` with the PR URL and exact head SHA.
6363

64-
The owner is the only actor allowed to mark a Draft PR Ready, approve it, or merge it. Never call non-`--undo` `gh pr ready`, `gh pr merge`, enable auto-merge, push to `main`, push to `dev`, dismiss owner feedback, or bypass branch protections. Before any terminal transition, run `prepare-finalization`. For completion, that command paginates the PR timeline and reviews, requires a Ready transition authored by `codeacme17` strictly after the remotely verified Ready-notification comment with no later redraft, requires the owner's exact-head approval and merge, delivers the informational `pr_completed` GitHub notification with the merge SHA after the remote merge timestamp, waits for the bounded webhook attempt to settle, and binds distinct notification URLs/timestamps into the record. Raw executor comments cannot use the reserved `pr_completed` marker. If canonical GitHub delivery fails, no terminal record is created. Only then publish the exact body to the configured state-journal issue and pass its result/comment URL to `observe-owner-merge`; that command revalidates the remote record, appends the local notification/owner audit events, and finalizes. Future workspaces run `reconcile` to revalidate the same notification, Ready, approval, and merge proof before rebuilding local history or suppressing an active checkpoint.
64+
The owner is the only actor allowed to mark a Draft PR Ready, approve it, or merge it. Never call non-`--undo` `gh pr ready`, `gh pr merge`, enable auto-merge, push to `main`, push to `dev`, dismiss owner feedback, or bypass branch protections. Before any terminal transition, run `prepare-finalization`. For completion, that command paginates the PR timeline and reviews, requires a Ready transition authored by `codeacme17` strictly after the remotely verified Ready-notification comment with no later redraft, and requires the latest exact-head owner review to be a strictly later `APPROVED` decision before the owner merge. It delivers the informational `pr_completed` GitHub notification with the merge SHA after the remote merge timestamp, waits for the bounded webhook attempt to settle, and binds distinct notification URLs/timestamps into the record. For `failed` or `blocked`, publish a checkpoint after the blocking notification and pause; the terminal record must bind that exact `waiting_for_owner` checkpoint, its digest, current pause, and the matching notification timestamp. Raw executor comments cannot use the reserved `pr_completed` marker. If canonical GitHub delivery fails, no terminal record is created. Only then publish the exact body to the configured state-journal issue and pass its result/comment URL to `observe-owner-merge`; that command revalidates the remote record, appends the local notification/owner audit events, and finalizes. Future workspaces run `reconcile` to revalidate the same notification, pause or Ready, approval, and merge proof before rebuilding local history or suppressing an active checkpoint.
6565

6666
For any pause, do not resume from silence or an arbitrary comment. First require a successfully delivered blocking notification. Then verify the owner's GitHub decision with `loopctl.mjs record-owner-response --run-id <id> --response-url <comment-or-review-url>`. A normal comment must include the exact `RESUME <run-id>` token printed in the notification; a `CHANGES_REQUESTED` review is itself an explicit decision and must be submitted against the run's current exact head SHA. Only then may `loopctl.mjs transition --run-id <id> --status running` continue. Before any repair work or new push, publish that transition's checkpoint, run the unchanged exact PR through `gh pr ready --undo --repo codeacme17/echo-ui`, observe the same head as Draft with `record-pr`, and publish another checkpoint. `$implement` repair attestations and later PR rebinds are rejected unless this durable redraft happened after the current owner response.
6767

loops/issue-dev-loop/evolve/EVOLVE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,4 @@ Every evolve change requires a dedicated `codex/evolve-<request-id>` draft PR ta
1818

1919
Never optimize by weakening evidence, increasing autonomous authority, deleting unfavorable history, or hiding no-work and failure runs.
2020

21-
After the owner merges the evolve PR, run `loopctl.mjs evolve-complete --request-id <id> --summary <summary> --pr-url <url>` through the installed automation wrapper. The trusted command re-fetches the digest-bound request, owner Ready/approval/merge proof, posts a digest-bound `evolve-completion` record to the state journal, and only then clears the pending request. The identity router reserves that marker for this command. Every scheduled `reconcile` rebuilds pending and completed evolve requests, `lastEvolvedRunCount`, and completion counters from those automation-authored journal records before deciding whether another evolve session is due. A local file, silence, an unmerged PR, or a completion comment created before the recorded merge never clears a request.
21+
After the owner merges the evolve PR, run `loopctl.mjs evolve-complete --request-id <id> --summary <summary> --pr-url <url>` through the installed automation wrapper. The trusted command re-fetches the digest-bound request, owner Ready/approval/merge proof, reuses an already-valid matching completion after a crash or ambiguous retry, otherwise posts a digest-bound `evolve-completion` record to the state journal, and only then clears the pending request. The identity router reserves that marker for this command. Every scheduled `reconcile` coalesces identical same-digest request or completion publications while rejecting conflicting ones, then rebuilds pending and completed evolve requests, `lastEvolvedRunCount`, and completion counters before deciding whether another evolve session is due. A local file, silence, an unmerged PR, or a completion comment created before the recorded merge never clears a request.

loops/issue-dev-loop/review/REVIEW.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ The fresh reviewer publishes each round verbatim through `reviewerGitHubLogin` a
2727

2828
Before the final round is published, write the complete cycle result with a temporary/unassigned final `reviewUrl` and run the installed `loopctl review-digest --result <path>`. Put its returned `<!-- issue-dev-loop:<run-id>:review-result-sha256:<digest> -->` marker in the final body. After GitHub assigns the review URL, replace the temporary value and rerun `review-digest`; it must be unchanged. This canonical publication digest replaces only review URLs with a fixed placeholder and therefore avoids a URL↔digest cycle. The later recorded full-file digest still protects the final URLs and every other byte. The reviewer must not downgrade severity or omit findings.
2929

30-
After all replies are posted, create one cycle result matching `result.schema.json`. Its `cycle` is the next durable review cycle number, and it contains every round's review URL, every finding, its final classification, response URL, and evidence. Executor replies include both the readable evidence (and accepted fix SHA) plus `<!-- issue-dev-loop:<run-id>:<finding-id>:<classification> -->`. Rejected P0/P1 findings additionally require an independent or owner comment containing `<!-- issue-dev-loop:<run-id>:<finding-id>:adjudication:<verdict> -->`. Run `record-review` with the final GitHub review URL; it paginates every reviewer-authored review on the recorded PR and requires one-to-one membership for the current run/cycle before it verifies replies, identities, timestamps, ancestry, adjudications, and markers. The publication gate rejects skipped or duplicate cycle rounds. Generic events cannot forge this reserved gate.
30+
After all replies are posted, create one cycle result matching `result.schema.json`. Its `cycle` is the next durable review cycle number, and it contains every round's review URL, every finding, its final classification, response URL, and evidence. Executor replies include both the readable evidence (and accepted fix SHA) plus `<!-- issue-dev-loop:<run-id>:<finding-id>:<classification> -->`. Rejected P0/P1 findings additionally require an independent or owner COMMENT publication containing `<!-- issue-dev-loop:<run-id>:<finding-id>:adjudication:<verdict>:head:<sha> -->`. Independent `REJECT_FINDING` adjudication is a separate body-only, non-approving GitHub review published through the reviewer identity; it is bound to an existing current-head reviewer finding, cannot be duplicated, and does not consume a review cycle round. Run `record-review` with the final GitHub review URL; it paginates every reviewer-authored review on the recorded PR and requires one-to-one membership for the current run/cycle before it verifies replies, identities, timestamps, ancestry, adjudications, and markers. The publication gate rejects skipped or duplicate cycle rounds. Generic events cannot forge this reserved gate.
3131

3232
## Completion
3333

loops/issue-dev-loop/review/response-policy.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Classify every automated finding as one of:
1010

1111
Accepted findings return to a new `$implement` invocation that starts after the review was submitted. Reply only after that invocation finishes and the fix is pushed; include commit SHA, commands, results, and evidence links.
1212

13-
Rejected findings require a precise counterclaim and reproducible evidence in the GitHub reply. Do not reply with bare disagreement. An executor cannot unilaterally close a disputed P0 or P1; publish `REJECT_FINDING` from the independent reviewer identity or `OWNER_REJECTED_FINDING` from the owner, include its adjudication marker/URL in the cycle result, and escalate `NEEDS_OWNER` outcomes.
13+
Rejected findings require a precise counterclaim and reproducible evidence in the GitHub reply. Do not reply with bare disagreement. An executor cannot unilaterally close a disputed P0 or P1; publish `REJECT_FINDING` from the independent reviewer identity as a separate body-only COMMENT review, or `OWNER_REJECTED_FINDING` from the owner. The publication must carry `<!-- issue-dev-loop:<run-id>:<finding-id>:adjudication:<verdict>:head:<sha> -->`; include its review/comment URL in the cycle result, and escalate `NEEDS_OWNER` outcomes.
1414

1515
Do not delete review comments. Log classification, response time, response URL, commit, and evidence reference under the run ID.
1616

0 commit comments

Comments
 (0)