Skip to content

Commit 0913c8f

Browse files
mchwangclaude
andauthored
Show the merge job's step list and let a person cancel the merge alone (D16) (#194)
* Show the merge job's step list and let a person cancel the merge alone (D16) Merge PR's job now records the design's seven steps (Fetch, Rebase, Push, Re-run checks, GitHub checks, Already-fixed check, Merge) with each step's state and start and end times, in its merge_jobs record. Preparation and the guarded merge report their steps through display-only callbacks. The review screen shows the list under the review strip with a Cancel merge button. Cancelling stops the job and its preparation without cancelling the task: the task keeps its status, its pull request stays open, and Merge PR may be clicked again. It is refused once GitHub has been asked to merge. A restart after a cancel settles the job cancelled. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Address independent review round 1 of the merge step list Give the server test's fake preparation stopForMergeCancel; poll at once when a cancel is answered with a job that ended; rewrite only the elapsed times each second; name the pending Cancel 'Cancel requested'; test a cancel during the guarded merge's validation, during the job's first review load and before shutdown, and older poll records after a cancel; document where the list says a job stopped and when Cancel shows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Address independent review round 2 of the merge step list Poll quickly again after a cancel, so its outcome shows within seconds of a backed-off poll; settle a restart after both cancels as a cancelled task; let screen readers read each step's word and time as text and move focus off a hidden Cancel; test a merge cancel during the push; document every case where no step reads where the job stopped, repeat cancels and clock skew. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Address independent review round 3 of the merge step list Let a stop win over an inspection that answers after it, poll at once after an ended cancel answer unless a poll of this generation is in flight, escape a step's fallback word, and say that Cancel explains itself when the Merge step's write failed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Address independent review round 4 of the merge step list Poll again within 2 s when a cancel is answered with an ended or newer job while a poll is on its way; move focus off Cancel whenever the bar or the button is hidden; write why a disabled Cancel cannot stop the merge beside it; give the restart row its cancelled case. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Address independent review round 5 of the merge step list Describe Cancel by its note only while the note is shown, test that focus moves off a hidden Cancel (to Refresh when the merge control hides too), and keep focus on the page when a failed load hides the merge controls. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Address independent review round 6 of the merge step list Test that focus on Cancel moves to Refresh when a reload hides the merge control or fails, and stop the elapsed-time ticker when a failed load hides the step bar. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Address Copilot review of the merge step list Keep the step list of a job that ended queued through the queue polls; ignore a cancel answer the poll already showed, follow a newer running job it carries and discard polls of older jobs; show a refusal only while the cancelled job is still shown running. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Tidy the cancel-answer doc bullet and say what clears the step list (#194 review round 8) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Say that a restart settles a cancelled merge as cancelled (#194 review round 9) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Address independent review round 10 of the merge step list Refuse a merge-only cancel while the task is being cancelled, keep the pending-cancel flag per job, update the Shutdown row and the #end comment for a cancelled merge, name Cancel merge in the architecture overview, note the approved D16 amendment beside D16, and test the refusal banner for an enabled Cancel. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep a cancel refusal in the banner until the job's record changes (#194 review round 11) Also clear only this cancel's own pending state, check the task-cancel state the refusal test relies on, and qualify the repeat-cancel rule. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Show a held cancel refusal again on each poll, and list the step-list tests (#194 review round 12) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Mark build step 4 done: #174, #176 and the step list are closed (#194 review round 13) Merges main (#195, #196, #197) and updates the status lines this PR rewrites; tests that a held cancel refusal also survives Refresh. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * List D16's unbuilt failure banners and Queue row as missing; qualify onAsking (#194 review round 14) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Address independent review round 15 of the merge step list Add the step list's Waiting and Running marks to DESIGN.md (approved), an AGENTS.md rule for focus when a background update hides the focused control, cite #199 for the rest of D16 and DT7, save the waiting checks only when they change, and correct the onAsking comment. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Cite #199 on the build step 4 checklist line, word the checks-write comment, and add Skipped to DESIGN.md (#194 review round 16) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Add the Stopped and Unknown marks to DESIGN.md, record the step list's placement, and test focus when a reload shows the job ended (#194 review round 17) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Test the older-job poll guard, the poll speed on a list-step change and the list surviving the timer (#194 review round 18) Also log the step bar's placement in DESIGN.md and name the F6b queue test in the handoff doc's test list. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Move focus off Merge PR and its blockers button when a reload hides them, and test each way (#194 review round 19) Reads the focused control before hiding it, since hiding blurs it at once. Also drops the redundant first save of the waiting checks and widens DESIGN.md's step-bar log entry. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Decide Merge status's visibility once, so a running job's poll keeps focus on it (#194 review round 20) Also test the fallback to Merge PR when a reload clears the last blocker, and word the test fixture's revision note as order only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Move focus off a hidden merge control only after the render sets its final state (#194 review round 21) Focus went to a still-disabled Merge PR (without a runner) and fell to the page. renderMerge now records the focused control, renders, then picks Merge PR only when shown and enabled, otherwise Refresh. The AGENTS.md rule now says the fallback must be shown and enabled and a control's visibility is decided once per update. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Check Cancel's focus against the control focused before the update, and test the disabled fallback (#194 review round 22) Also word the fallback as "not disabled (aria-disabled qualifies)" in the rule and the comment, drop the second write of Merge status's visibility, and document every focus move in merge-handoff.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Merge renderMergeSteps' doc comments, and say Cancel is marked unavailable, not disabled (#194 review round 23) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Test the display-only onAsking guard and a queued job's Merge step, and keep Cancel pending until the poll answers (#194 review round 24) Also replace the last "disabled Cancel" and "until Refresh" wording. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep Cancel unavailable until a poll stops reporting the cancelled job as running (#194 review round 25) A poll already in flight could answer the job's older running record and clear the wait early. The in-flight test now checks Cancel after the held poll's answer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Read the in-flight Cancel state once, test that a replayed click does not call onAsking, and note restart downtime in step times (#194 review round 26) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Test the older-record poll guard, an inspection answering after shutdown, Fetch failing for review, and a joined click (#194 review round 27) Also name the merge step bar in DESIGN.md's Frame. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Test on the real preparation path that Fetch is still running when a moved head stops it (#194 review round 28) Also list round 27's tests in the handoff doc. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Test that Merge stays waiting when the attempt is not saved, and Rebase and Push are skipped when nothing moved (#194 review round 29) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Test a restart while a task cancel is stopping, a cancel refused after a failed ending write, and a held refusal ending when the job moves on (#194 review round 30) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Test each merge step state's mark, word and DESIGN.md color (#194 review round 31) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Test that a pending cancel stays with its own merge job (#194 review round 32) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep a cancel's wait when a poll answers for an older merge job (#194 Copilot review) A poll that read an older job is discarded; it no longer ends the wait of a newer job whose cancel was answered as ended first. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Bind a cancel's wait to the job shown when its answer arrives (#194 Copilot review) An older job's cancel answered late, carrying the newer job that has just ended, no longer replaces that newer job's wait with its own. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Hold the ended record a cancel's answer carried (#194 review round 35) Cancel stays requested for every record older than the ended job the answer carried, so a poll read before that job ended no longer offers Cancel again. The poll no longer clears the wait. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Test a Refresh answered after a cancel; name Cancel merge in the running-merge refusal (#194 review round 36) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Wait for each cancel test's answer to be shown, not a fixed time (#194 review round 37) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Wait for Cancel's render, not a response or a fixed time, in the cancel tests (#194 review round 38) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Wait for a render or the next poll in every D16 no-change check (#194 review round 39) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Add the no-change race check rule to AGENTS.md (#194 review rounds 37-39) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Prove the step timer ran; widen the no-poll window past the poll delay (#194 review round 40) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Check an unavailable Cancel sends nothing over the poll-delay window (#194 review round 41) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Poll for the recorded cancel instead of reading it at once (#194 review round 42) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep the latest ended record when a cancel answers late (#194 Copilot review) An answer is newer than the record shown, not always than the one held: across three jobs, an older job's late answer replaced a newer ended record and let Cancel be offered for that newer job. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Test that a later ended record replaces the held one (#194 review round 44) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Present and keep a newer ended job a late cancel answer carries (#194 Copilot review) When the page already showed its own job ended, an answer carrying a newer ended job was dropped: its outcome was never shown and its record was not kept, so a Refresh read while it ran could offer Cancel. The answer now keeps the record and asks the poll, which follows a later job also after the shown job ended. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Poll until a newer ended job is shown; drop an older job's queue attempt (#194 review round 46) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Test the cancel-answer path alone, polling's stop, and a later job's own queue attempt (#194 review round 47) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Drop an older job's queue attempt when a full reload keeps a newer job (#194 Copilot review) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep observations made while a merge click is on its way over its answer (#194 Copilot review) A late cancel answer could adopt the click's job, a cancel of it could be accepted, and then the click's older answer replaced that record, offering Cancel again. The click's answer is now reconciled like a full response. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Keep an ended job's view against a poll read earlier; keep a followed job on a racing click refusal (#194 review round 50) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Follow an ended job's own attempt by action ID; drop only earlier or foreign answers (#194 review round 51) Round 50's guard dropped every queue answer for an ended job unless it ended queued, so a job that ended submitted with its attempt in GitHub's queue never reached Merged. A refused click now keeps any running job the page follows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Follow its own attempt only for a job that ended queued or submitted (#194 review round 52) A blocked or failed job drops a copy of its own attempt kept while it asked GitHub; a submitted job follows its attempt on every path; a refused click carrying a later ended record presents it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Leave a refused click's queued or submitted end to the poll; test the guard's ended-state check (#194 review round 53) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Test that a refused click never presents an older ended record (#194 review round 54) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 0ffd195 commit 0913c8f

18 files changed

Lines changed: 2668 additions & 81 deletions

‎AGENTS.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ For features with background jobs, polling, retries, cancellation, or shutdown:
3232
- Preserve completed historical results, but visibly mark them stale when their snapshot, plan revision, assignment, or referenced code no longer matches.
3333
- When polling updates one part of the screen, update only that state. Preserve scroll position unless the user was already following the bottom.
3434
- While a request is in flight, do not disable the control that has keyboard focus; disabling it drops focus to the page. Mark it `aria-disabled`, ignore repeat activation with an in-flight guard, and test that focus stays on the control after the response.
35+
- When a background update (a poll, a reload, an answer) hides or removes the control that has keyboard focus, move focus to a named fallback that is still shown and not `disabled` (an `aria-disabled` control qualifies) once the update has set every control's final state, and test that focus lands there for each way the control can be hidden. Decide a focusable control's visibility once per update: hiding it and showing it again blurs it.
3536
- When a row or control's visual selection determines the current content or input, expose the same state with the appropriate accessibility attribute, such as `aria-current` or `aria-selected`, and test it across navigation.
3637

3738
## Required race regressions
@@ -53,6 +54,8 @@ Every reproduced race requires a failing-before and passing-after regression. As
5354

5455
A race regression must assert that the interleaving it names actually happened (for example which abort stopped which stage, or that a deadline fired before a release) before it uses the outcome as evidence. Otherwise machine load can turn it into a test of a different order that still passes.
5556

57+
A check that an event changed nothing (a late answer, a stale poll, a reload read earlier) must first prove the page handled that event, then read the state once: wait for a render or a state the event causes, or for a request the page sends only after handling it, and hold any later answer that could repair the state before the read. A fixed wait, the network response alone, or a retrying assertion on a state that already held before the event is not that proof: a page that handles the event late, or wrongly, then passes. A check that nothing is sent after an event can only be made over a window: wait longer than the shortest delay after which the page would send it, and say so beside the check.
58+
5659
## Review readiness
5760

5861
- Before requesting or re-requesting an automated Copilot review, self-review the full current diff, fix every issue found, and repeat the self-review and fix cycle until a complete pass finds no new issues. Re-run the relevant validation after fixes, then complete the mandatory independent-agent review loop below before requesting Copilot review.

‎DESIGN.md‎

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -144,9 +144,11 @@ components:
144144
| Token | Meaning | Icon and word |
145145
|---|---|---|
146146
| `success` | OK: passed, in scope, approved | ✓ plus a word such as "OK" or "Passed" |
147-
| `warning` | Attribution warning, stale | ! plus a word such as "Stale" or "Ambiguous" |
147+
| `warning` | Attribution warning, stale, or a step that did not finish normally | ! plus a word such as "Stale" or "Ambiguous" ("Stopped" for a cancelled merge step, "Unknown" when GitHub's merge outcome is not known) |
148148
| `error` | Scope or test failure, blocker, unplanned | ✕ plus a word such as "Fail" or "Unplanned" |
149-
| `neutral-status` | Not applicable | – plus "N/A" |
149+
| `neutral-status` | Not applicable | – plus "N/A" (or "Skipped" for a step that was not needed) |
150+
| `neutral-status` | Not started yet (a step that is waiting) | ○ plus "Waiting" |
151+
| `text` | In progress (a step that is running) | ● plus "Running" |
150152

151153
- `success` is much lighter than `error`. This lets red-green color-blind users still tell them apart by brightness. Never rely on hue alone.
152154
- **Contrast (checked 2026-09-22):** every text and status color is at least 5.0:1 on every surface. Body text is 14.6:1 on `surface`.
@@ -174,7 +176,7 @@ components:
174176
| 1280–1439px | The conversation pane collapses to a tab. The plan list narrows to 208px |
175177
| Below 1280px | A notice asks for a wider window |
176178

177-
**Frame:** a 44px app bar (the shared menu, design review D13), then a 48px review strip (issue, PR, revision, progress, blockers), then full-height panes. Panes can be resized and collapsed.
179+
**Frame:** a 44px app bar (the shared menu, design review D13), then a 48px review strip (issue, PR, revision, progress, blockers), then, while a merge job runs and until the review reloads, the merge step bar, then full-height panes. Panes can be resized and collapsed.
178180

179181
**Rhythm:** a 4px base grid. Rows are 32px. Pane padding is 12px. Space between sections is 16px or 24px. Keep density tight inside lists and give the code pane the most room.
180182

@@ -257,3 +259,7 @@ There are no shadows and no glows. Depth comes from the surface brightness steps
257259
| 2026-09-22 | The serif "second voice" for the user's own words was not adopted | The user chose variant B. Two faces are enough |
258260
| 2026-09-22 | Logo: terminal-prompt mark (`>_`) in a rounded square, in `text` color | Taken from the approved review-screen mockup. It was left out of the first HTML screen and restored at the user's request |
259261
| 2026-09-22 | Contrast-checked values kept instead of values extracted from the mockup image | The extracted values (for example error #DA1E28) failed contrast on dark surfaces |
262+
| 2026-10-21 | Two progress marks for the merge step list: ○ "Waiting" in `neutral-status` and ● "Running" in `text` | Steps that have not started or are under way are not outcomes, so they take no outcome color. Approved by the user (PR #194) |
263+
| 2026-10-21 | The not-applicable mark also reads "Skipped" for a merge step that was not needed | Same meaning as N/A, worded for a step. Part of the step list the user approved (PR #194) |
264+
| 2026-10-21 | The warning mark also reads "Stopped" (a cancelled merge step) and "Unknown" (GitHub's merge outcome not known) | Both mean "look at this, it did not finish normally". Approved by the user (PR #194) |
265+
| 2026-10-21 | While a merge job runs, and after it ends until the review reloads, a step bar sits between the review strip and the panes | It shows the merge step list and Cancel merge without replacing the review strip's merge control. Approved by the user (PR #194) |

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
Review agent-made Git changes one plan item at a time. The approved plan lists each item's files and acceptance checks; the review engine shows which item produced each change and flags foreign or overlapping work.
44

5-
**Status:** the plan/linking library, SQLite store, local review screen, Plans screen (plan import, plus agent drafts and suggestions with revision-bound Apply; drafts and suggestions need a configured `github` review, not the demo), opt-in code-writing runner, durable local rebase, exact-head plan command checks, and the one-click merge job are implemented. Run `npm run demo` and open its private local URL. Ask, planning and the runner use Claude Code inside locked-down containers; Codex is refused because it reads code only by running commands ([why](docs/implementation/agent-isolation.md#codex-is-refused-in-every-phase)). They need Docker, plus `CLAUDE_CODE_OAUTH_TOKEN` (from `claude setup-token`). Startup builds the agent image, which can take a few minutes. A configured GitHub review can merge only after the guarded exact-head gate passes. The agent container, vendor-only network, credential-free command-check profile, and Claude/Codex adapters are described in [agent isolation](docs/implementation/agent-isolation.md). With a runner, the **Merge PR** button pushes the rebased head, waits for its required checks, and hands that exact pair to the guarded merge ([merge handoff](docs/implementation/merge-handoff.md)). The paired human review experiment was cancelled before results were recorded and no longer blocks roadmap work; optional future validation is tracked in [#19](https://github.com/codeabovelab/codeboost/issues/19).
5+
**Status:** the plan/linking library, SQLite store, local review screen, Plans screen (plan import, plus agent drafts and suggestions with revision-bound Apply; drafts and suggestions need a configured `github` review, not the demo), opt-in code-writing runner, durable local rebase, exact-head plan command checks, and the one-click merge job (with its step list and a **Cancel merge** button) are implemented. Run `npm run demo` and open its private local URL. Ask, planning and the runner use Claude Code inside locked-down containers; Codex is refused because it reads code only by running commands ([why](docs/implementation/agent-isolation.md#codex-is-refused-in-every-phase)). They need Docker, plus `CLAUDE_CODE_OAUTH_TOKEN` (from `claude setup-token`). Startup builds the agent image, which can take a few minutes. A configured GitHub review can merge only after the guarded exact-head gate passes. The agent container, vendor-only network, credential-free command-check profile, and Claude/Codex adapters are described in [agent isolation](docs/implementation/agent-isolation.md). With a runner, the **Merge PR** button pushes the rebased head, waits for its required checks, and hands that exact pair to the guarded merge ([merge handoff](docs/implementation/merge-handoff.md)). The paired human review experiment was cancelled before results were recorded and no longer blocks roadmap work; optional future validation is tracked in [#19](https://github.com/codeabovelab/codeboost/issues/19).
66

77
## Development
88

0 commit comments

Comments
 (0)