You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
G2: add planning authoring and suggestion cards - #150
add Plans controls for whole-plan drafting and independent edit suggestions
poll and dismiss durable planning requests with bounded backoff and guarded lifecycle ownership
preserve newer guidance, completed historical results, focus, and stale-context visibility
validate exact v1 result structures, endpoint mode, immutable plan binding, and lifecycle/result combinations before rendering
render agent output as escaped literal text; applying a draft or card remains G3
Tracks #145. #146 owns G3 Apply and the transport-size follow-up. This PR does not change web/server.ts, Store, runner, or Docker/agent-isolation contracts.
Done when
npx playwright test test/browser/plans.spec.ts test/browser/plans-authoring.spec.ts && npm run typecheck passes and proves authoring/suggestion results cannot erase newer input, masquerade as current after a binding or lifecycle change, or render stale/malformed polling responses as current.
focused G2 browser: 17/17 passed locally and independently
planning/backend: 169/169 passed independently
typecheck: passed locally, independently, and in both CI runs
exact-head CI push run 37832029312: passed; full non-Docker Vitest suite and full Playwright suite passed
exact-head CI pull-request run 37832038020: passed; full non-Docker Vitest suite and full Playwright suite passed
visual check: passed against DESIGN.md
full local npm test: infrastructure-blocked by shared Docker saturation; stopped after 15 minutes and 539 containers without a product assertion
real-Docker agent-isolation suite: not run for G2 because no trigger-path file changed; PR F5: prepare rebases and run head-bound checks #149 owns the repository's active heavyweight Docker validation
Review record
Author self-review fixed Dismiss focus replacement and added the focus regression.
Independent rounds found and drove fixes for old-poll/cancellation ordering, malformed-status ownership, completed history, binding/mode validation, status rollback, hostile output, complete nested v1 payloads, and JSON-Schema-compatible Unicode code-point bounds.
Copilot rounds 1-5 found cancelled output retention, missing stale-dismissal coverage, invalidated actions/labels, disabled styling, stale architecture text, operation mode, complete payload validation, consumed-state presentation, and impossible failed output. Each valid finding received a targeted regression and a fresh exact-head independent review.
Latest independent full-diff review on 26d03e6...b35e67b is CLEAN. Independent validation: typecheck, planning/backend 169/169, focused G2 browser 17/17, and diff check.
Copilot round 6 on b35e67b reported zero open code findings and a validation-only summary concern. The claim that CI was unconfirmed is declined as factually contradicted by the two successful exact-head runs above. The separate Docker-backed suite remains explicitly infrastructure-blocked/not applicable to this UI-only diff; it is not represented as passed.
Review-lesson audit
Lifecycle ordering, stale/consumed state, cancellation ownership, focus preservation, and controlled interleavings are covered by the existing Async jobs and polling, Async review UI, and Required race regressions rules.
Exact response shapes, mode/binding checks, fail-closed malformed output, and exact-head readback are covered by External status confirmation and Review readiness.
Complete typed payloads, Unicode bounds, and escaped hostile content are covered by Agent-controlled content plus the existing schema/claim-verification review rules.
Disabled styling and architecture-status corrections are one-off implementation/documentation consistency fixes; no new operating rule is needed.
No review finding requires a new AGENTS.md/CLAUDE.md rule.
Scope boundary
G2 starts, observes, displays, and dismisses planning results. G3 owns revision-bound Apply and request-time draft/attachment preservation. G4 owns real-provider/store integration and remaining T18 browser/adapter acceptance.
Dismiss action remains visible for invalidated results
web/public/app.js:1004
Only pending and ready requests can be cancelled by the Store, but this renders Dismiss for retained invalidated (and any failed) result as well. After an import invalidates a ready request, clicking this button reconciles back to invalidated, so the button and result never disappear. Limit the action to a current ready request while retaining it during cancelling to preserve focus.
Disabled Suggest buttons still appear enabled
web/public/style.css:844
The new primary Suggest button remains blue when aria-disabled: the later button.primary rule overrides the generic disabled colors with equal specificity. That makes an unavailable/in-flight action still look enabled, contrary to the disabled-state design contract. Add a more-specific override for these controls.
Architecture document has outdated feature-status entries
docs/implementation/planning-screen.md:7
This documents G2 as delivered, but the repository-wide architecture document still says “plan authoring and suggestion controls” are not built and describes the Plans screen as display/import only (docs/architecture.md:47,167). Update those build-state entries so developers do not get contradictory feature-status guidance.
Copilot round 2 summary-only concerns addressed in 1fa7cb6:
Stale or terminal non-ready results no longer expose Dismiss; the control remains only for a current ready result or the already-running cancelling state needed to preserve focus.
Added failing-before/passing-after coverage that imports a newer revision while an older ready response is held, then proves the stale result has no Dismiss action.
Primary aria-disabled controls now use the same surface, muted text and line border treatment as other disabled buttons. The computed-style regression is transition-safe and passed three repeated runs plus the full focused file.
docs/architecture.md now consistently records G2 authoring and suggestion controls as built while G3 Apply remains future work.
Exact-head validation before push: typecheck passed; G2 browser 13/13. Fresh independent full-diff review is CLEAN on base/head 26d03e6 / 1fa7cb6. No concerns were declined.
Copilot rounds: round 1 two inline findings fixed; round 2 confirmed both resolved and surfaced three summary-only concerns, all reproduced or corrected and fixed in 1fa7cb6
No findings were declined
Local full-suite Docker gate remains infrastructure-blocked by shared Docker saturation; both remote full non-Docker and browser CI suites are green
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
Poll validation misses operation mode, and invalidated historical results can be mislabeled as non-stale.
0 open findings
Previously missed (2)
In code that hasn't changed since last review
Mark invalidated historical results as stale
web/public/app.js:938
An invalidated request is stale even when its revision and snapshot IDs still match—for example, the Store can invalidate a ready result when its checkpoint continuation changes. If another request starts, this result moves to history, but this predicate returns false and the only remaining label becomes “Earlier result” rather than “Stale.” Include the lifecycle state in the stale predicate so invalidated historical output is never presented as current-context output.
Reject responses with a mode mismatched to the operation
web/public/app.js:974
The status validator ignores the operation discriminator returned by suggestion reads. A malformed response with mode: "draft" is accepted when it contains a suggestion-shaped reply (and draft responses accept an injected suggestion mode), so a cross-operation polling payload can render instead of failing closed. Validate the endpoint-specific mode shape along with the lifecycle fields, and cover the wrong-mode response in the malformed-status regression.
Copilot round 3 summary-only concerns addressed in bd5380b:
Invalidated requests are now stale by lifecycle state even when revision and snapshot still match. A failing-before/passing-after regression proves the current invalidated status and its later historical label both remain stale.
Poll validation now enforces the endpoint-specific operation shape: suggestion reads require mode=suggest, while draft reads reject any mode discriminator because the draft API contract omits it.
The wrong-operation regression covers both directions, proves no foreign payload renders, preserves request ownership, and then recovers to each real terminal response.
Status validation first proves the payload is an object, so primitive JSON also fails through the standard guarded path.
Exact-head validation before push: typecheck passed; G2 browser 15/15. Fresh independent full-diff review is CLEAN on base/head 26d03e6 / bd5380b; the reviewer independently reran 15/15. No findings were declined.
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
Partial payload validators allow malformed drafts and edit cards to be presented as valid results.
0 open findings
Previously missed (1)
In code that hasn't changed since last review
Validate complete edit payloads before accepting ready results
web/public/app.js:960
These branches accept schema-invalid edit cards as ready results. For example, add_item passes with new_item: null, and add_file passes with only a path, even though the v1 edit contract requires the complete typed payload (and null unused payloads). Such malformed polling data is then rendered as a valid suggestion. Validate the full edit shape and operation-specific payload invariants before accepting the status.
This issue also appears on line 980 of the same file.
Copilot round 4 follow-up on the complete-result validation finding:
Replaced partial ready-result checks with exact v1 plan/edit-reply validation, including complete nested item/file/check objects and operation-specific used/null payloads.
Added failing-before coverage for incomplete add_item, partial add_file, and incomplete nested draft items, plus positive coverage for all nine edit operations.
Independent review then found a Unicode boundary mismatch: JavaScript code-unit length could reject JSON-Schema-valid astral text and retain polling ownership. Fixed by bounded Unicode code-point counting and added a 4,000-emoji reply regression that verifies rendering and control release.
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
Consumed and malformed failed lifecycle states can be presented incorrectly.
0 open findings
Previously missed (2)
In code that hasn't changed since last review
Treat consumed results as stale
web/public/app.js:938
consumed is accepted as a valid lifecycle state, but it is neither stale here nor handled by renderPlanAuthor. If another tab or API client applies the result between polls, this tab still holds the old review data, so the consumed result renders as current with an empty status. Treat consumed output as stale (and prompt the user to refresh) because applying it necessarily advanced the plan revision.
Reject non-null results for failed requests
web/public/app.js:1041
failed currently falls through to accepting a non-null valid plan/reply. The Store can only attach output when publishing ready; failed requests settle from pending with no output. Accepting this impossible combination lets a malformed poll render and later archive agent output under a failed request. Require a null result for both pending and failed, while retaining the optional-result behavior only for cancelled/invalidated.
consumed results are now always stale because applying them necessarily advanced the durable plan outside this screen's current review response. They render with stale status, expose no Dismiss action, and release author controls.
failed status now requires a null result. Impossible failed+output observations fail closed without rendering and retain pending polling ownership.
Added a controlled failing-before/passing-after browser regression: the malformed failed response is observed while the second poll is held, then explicit release proves the consumed response renders stale and releases controls.
New exact reviewed pair: 26d03e6c0e5d50c136ffc3e5c0d74cd3af52303e...b35e67bfac745dd3a4cafd44b0e8df568241bc09
The review reported 0 open findings and one validation-only summary concern: “the full suite and CI remain unconfirmed.” No code failure or counterexample was identified.
Declined in part, with current exact-head evidence:
CI is confirmed for b35e67bfac745dd3a4cafd44b0e8df568241bc09: runs 37832029312 and 37832038020 both succeeded.
Each run executed typecheck, the repository's full non-Docker Vitest command, and the full Playwright command.
The Docker-backed agent-isolation suite is reported separately and is not claimed as passed. It was locally infrastructure-blocked by shared-daemon saturation, this UI-only diff changes none of its trigger paths, and PR F5: prepare rebases and run head-bound checks #149 owns the active heavyweight Docker validation.
The PR body now records the exact commands/gates, all review rounds, and the review-lesson audit. No source change was made, so the clean independent review remains valid for exact pair 26d03e6...b35e67b.
* Close out Lane G status records (#145)
Lane G (G1-G4) merged in PRs #148, #150, #151 and #152. The README and
the design overview still described the Planning screen as unbuilt, and
build step 6 said "no screen yet". Record the screen as built, mark build
step 6 done, and record Lane G's release of the shared web files plus
the remaining lane F follow-up (#146) in the planning-screen document.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Point lane status readers to the live table; name released files (#145)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Clarify Lane G handoff and screen name (#145)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Tighten Lane G handoff wording (#145)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Mark lane G done in the Wave 2 table; state #146 ownership source (#145)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Name the web files lane G released in the Wave 2 table (#145)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Put the finished lane G row in the past tense (#145)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Drop unneeded history from the lane G row (#145)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Match the H row layout; cite where #146 is assigned to lane F (#145)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* README: planning agent needs a github review, not the demo (#145)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Clarify met G4 condition and demo limits (#145)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Put wave 2 ownership rules in the past tense (#145)
Copilot round 1 found that the Wave 2 note and integration rule 2 still
gave G active ownership of web files the handoff says were released.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Record where H4a's web files went after G1 (#145)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Name G3's one web/server.ts change in the handoff records (#145)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Put the H4a ownership bullet in the past tense (#145)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Name F's wave 2 server files in integration rule 2 (#145)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Remove unneeded ownership history; quote the AGENTS.md rule (#145)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Keep only supported ownership history (#145)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Describe wave 1 config ownership as the plan's (#145)
Copilot round 2 found that rule 2 still stated C's and D's finished
wave 1 assignments as active rules.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Mark the lane plans as the original schedule (#145)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Link the Lane G close-out follow-ups (#155, #156) (#145)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* List all open Lane G follow-ups in the handoff (#145)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Note what the Plans screen does for amendments today (#145)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Match the handoff to #155's scope (#145)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Update next steps and menu-link status for the Plans screen (#145)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Describe the built Dismiss control in plan-format (#145)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Match plan-format's Plans screen controls to the built screen (#145)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Mark unbuilt Plans screen design features (#155) (#145)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Match the handoff to #155's widened scope (#145)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Name the suggestion Apply control; date the Plans DT update (#145)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Correct the amendment and Settings notes (#145)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Qualify build step 6 status with #155's unbuilt parts (#145)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Place the refresh line below the cards in plan-format (#145)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Qualify the Plans screen in What to do next (#145)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Say the Plans screen has no approval step yet (#145)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* List the approval step among #155's unbuilt parts (#145)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Put lane H's ownership section in the past tense (#145)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Put the H4b bullet in the past tense (#145)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Keep H4b's storage note as the original plan (#145)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Keep plan-format's v1 contract text; list shipped differences separately (#145)
Copilot round 3: the guide's suggestion-card wording had been changed to
match the shipped screen, which conflicts with the immutable v1 contract
(schema/versions/1/semantics.md). Restore the contract-mirroring text,
describe what the shipped screen does in its own section, and track the
non-conformance in #159.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Cite #159 in status lines; note the #146 import limit (#145)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* List the amendment-proposal gap in plan-format's shipped section (#145)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Record that #146 was fixed by PR #158 (#145)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Tracks #145. #146 owns G3 Apply and the transport-size follow-up. This PR does not change
web/server.ts, Store, runner, or Docker/agent-isolation contracts.Done when
npx playwright test test/browser/plans.spec.ts test/browser/plans-authoring.spec.ts && npm run typecheckpasses and proves authoring/suggestion results cannot erase newer input, masquerade as current after a binding or lifecycle change, or render stale/malformed polling responses as current.Validation
Validated head:
b35e67bfac745dd3a4cafd44b0e8df568241bc09Base:
26d03e6c0e5d50c136ffc3e5c0d74cd3af52303eDESIGN.mdnpm test: infrastructure-blocked by shared Docker saturation; stopped after 15 minutes and 539 containers without a product assertionReview record
26d03e6...b35e67bis CLEAN. Independent validation: typecheck, planning/backend 169/169, focused G2 browser 17/17, and diff check.b35e67breported zero open code findings and a validation-only summary concern. The claim that CI was unconfirmed is declined as factually contradicted by the two successful exact-head runs above. The separate Docker-backed suite remains explicitly infrastructure-blocked/not applicable to this UI-only diff; it is not represented as passed.Review-lesson audit
AGENTS.md/CLAUDE.mdrule.Scope boundary
G2 starts, observes, displays, and dismisses planning results. G3 owns revision-bound Apply and request-time draft/attachment preservation. G4 owns real-provider/store integration and remaining T18 browser/adapter acceptance.