diff --git a/extensions/iterator.js b/extensions/iterator.js index ee03834..30e5483 100644 --- a/extensions/iterator.js +++ b/extensions/iterator.js @@ -382,7 +382,12 @@ export default function iteratorExtension(pi) { /** Save one backlog mutation without spending a model turn. */ const saveBacklog = async (input) => { const cwd = ctxCwd(); - session?.showWorking?.("Saving backlog candidate…"); + // Backlog writes are allowed during a model turn, but must not replace or + // clear that turn's working guard. The normal turn-end refresh will pick up + // the filesystem change; idle saves still refresh immediately. + const preserveAgentWorking = session?.isWorking?.() === true; + if (!preserveAgentWorking) + session?.showWorking?.("Saving backlog candidate…"); try { const result = await runJson(scriptPath("write"), [], { cwd, @@ -401,8 +406,10 @@ export default function iteratorExtension(pi) { } catch (e) { notifyUi(`backlog not saved — ${e.message}`, "error"); } finally { - session?.clearWorking?.(); - await refreshHub(cwd); + if (!preserveAgentWorking) { + session?.clearWorking?.(); + await refreshHub(cwd); + } } }; diff --git a/lib/gather.mjs b/lib/gather.mjs index 6d8350b..bdaf3dd 100644 --- a/lib/gather.mjs +++ b/lib/gather.mjs @@ -1550,13 +1550,25 @@ export function gatherReview(startDir, opts = {}) { const matches = (o) => o.tests.has(f.path) || o.res.some((re) => re.test(f.path)); let owner = owners.find(matches); - if (opts.feature) { - const selectedOwner = owners.find( - (o) => o.slug === opts.feature && matches(o), - ); - if (selectedOwner) owner = selectedOwner; - } - if (owner) { + const selectedOwner = opts.feature + ? owners.find((o) => o.slug === opts.feature) + : null; + if (opts.feature && source === "commits") { + // The requested feature's commits are the attribution boundary. A path + // declared only by another feature still belongs in this commit-backed + // review as incidental; otherwise focused/consolidated review silently + // drops part of the selected feature's diff. + if (selectedOwner && matches(selectedOwner)) { + owner = selectedOwner; + f.group = selectedOwner.tests.has(f.path) ? "tests" : "declared"; + } else { + owner = { slug: opts.feature }; + f.group = "incidental"; + } + } else if (opts.feature && selectedOwner && matches(selectedOwner)) { + owner = selectedOwner; + f.group = selectedOwner.tests.has(f.path) ? "tests" : "declared"; + } else if (owner) { f.group = owner.tests.has(f.path) ? "tests" : "declared"; } else if (source === "commits") { // Already committed with the focused feature — nothing will be diff --git a/lib/session-server.mjs b/lib/session-server.mjs index 023cb8a..f365051 100644 --- a/lib/session-server.mjs +++ b/lib/session-server.mjs @@ -550,7 +550,8 @@ export function createSessionServer({ onUnsolicited, onControl, log } = {}) { const api = { isRunning: () => Boolean(server && server.listening), - hasPending: () => pending != null, + hasPending: () => pending !== null, + isWorking: () => working !== null, async start() { if (api.isRunning()) return { port, url: displayUrl(port) }; diff --git a/lib/ui.mjs b/lib/ui.mjs index 5b6ac6f..77a935e 100644 --- a/lib/ui.mjs +++ b/lib/ui.mjs @@ -186,7 +186,7 @@ function primaryClick(){ if(typeof onPrimary==='function') onPrimary(); } async function post(payload, okMsg, options){ if(document.body.classList.contains('iterator-ro') && !options?.allowWhileWorking){ alert('Claude is working — actions are disabled until it finishes.'); - return; + return false; } var btn=document.getElementById('primary'); __submitted = true; @@ -198,13 +198,15 @@ async function post(payload, okMsg, options){ __submitted = false; if(btn){ btn.disabled=false; if(typeof refresh==='function') refresh(); else btn.textContent=btn.dataset.prev||'Accept'; } alert('Not sent — Claude is still working (or this view is stale). Try again when the dashboard refreshes.'); - return; + return false; } if(btn) btn.textContent = '✓ ' + (okMsg||'Sent to Claude'); + return true; }catch(e){ __submitted=false; if(btn){ btn.disabled=false; if(typeof refresh==='function') refresh(); else btn.textContent=btn.dataset.prev||'Accept'; } alert('Could not reach local server: ' + e.message); + return false; } } diff --git a/lib/views/planning.mjs b/lib/views/planning.mjs index c24e2c3..21614bf 100644 --- a/lib/views/planning.mjs +++ b/lib/views/planning.mjs @@ -54,9 +54,13 @@ const BODY = ` const JS = ` const CH = D.features || []; -function backlogAction(payload, button, message){ +async function backlogAction(payload, button, message){ if(button){ button.disabled = true; button.dataset.label = button.textContent; button.textContent = 'Saving…'; } - post({ type:'backlog', ...payload }, message || 'Saved', { allowWhileWorking:true }); + await post({ type:'backlog', ...payload }, message || 'Saved', { allowWhileWorking:true }); + // During an agent turn the server deliberately does not refresh this view: + // doing so would clear the model-working guard. Restore the local control so + // more filesystem-only backlog edits remain possible until turn-end refresh. + if(button){ button.disabled = false; if(button.dataset.label) button.textContent = button.dataset.label; delete button.dataset.label; } } function selectedBacklogGoal(){ const selected = (D.backlog || []).filter(item => item.selected); diff --git a/memory/backlog/index.md b/memory/backlog/index.md index 858d2c7..61faf3c 100644 --- a/memory/backlog/index.md +++ b/memory/backlog/index.md @@ -2,11 +2,10 @@ type: Backlog title: Iterator backlog description: Saved ideas and bugs kept separate from active plan features. -items: "[{\"id\":\"idea-backlog-always-active\",\"title\":\"idea backlog always active\",\"details\":\"THe idea backlog just writes and reads files in the filesystem so it doesnt need to be deactivated while work in the agent is running so i can plan while work is done.\",\"kind\":\"idea\",\"selected\":true,\"created\":\"2026-07-17T14:07:01.680Z\",\"updated\":\"2026-07-17T14:09:05.129Z\"},{\"id\":\"parallel-features-implement\",\"title\":\"Parallel features implement\",\"details\":\"Add a Implement next wave button that implements all features without dependencies. Also a review all button that opens a review with the with all fgeatueis so to the left i can select a feature and then have the diffs for a feature. the implement automatic is different as it reviews iteself and implements all.\",\"kind\":\"idea\",\"selected\":true,\"created\":\"2026-07-17T14:08:13.716Z\",\"updated\":\"2026-07-17T14:09:05.624Z\"}]" -timestamp: 2026-07-17T14:09:05.625Z +items: "[]" +timestamp: 2026-07-17T15:00:30.011Z --- # Backlog -* [idea] idea backlog always active — selected -* [idea] Parallel features implement — selected +(empty) diff --git a/memory/decisions/index.md b/memory/decisions/index.md index bface48..a1de1a5 100644 --- a/memory/decisions/index.md +++ b/memory/decisions/index.md @@ -3,6 +3,7 @@ Durable product and implementation choices agents should preserve. * [Consume selected backlog ideas on plan approval](/decisions/consume-accepted-backlog-ideas.md) - Selected idea or bug candidates leave the backlog only after deterministic plan approval. +* [Parallel feature waves and consolidated review](/decisions/parallel-feature-waves-and-consolidated-review.md) - The dashboard supports fixed dependency-ready implementation waves and commit-backed multi-feature review without weakening explicit acceptance. * [Polish dashboard and multi-agent workflows](/decisions/polish-dashboard-and-multi-agent-workflows.md) - Dashboard polish and workflow refinements that clarify project context, constrain settings to usable models, and support deterministic Claude Code feature execution. * [Powerline shows the sandbox-published UI port](/decisions/powerline-shows-sandbox-ui-port.md) - The footer trails a ui:PORT segment resolved from ITERATOR_DISPLAY_PORT, falling back to ~/.pisbx-env because sbx run never sources it into pi's environment. * [Return to Work when Settings closes](/decisions/settings-close-returns-to-work.md) - Idle Settings close events restore the refreshed Work hub without changing the settings persistence path. diff --git a/memory/decisions/parallel-feature-waves-and-consolidated-review.md b/memory/decisions/parallel-feature-waves-and-consolidated-review.md new file mode 100644 index 0000000..738d2b7 --- /dev/null +++ b/memory/decisions/parallel-feature-waves-and-consolidated-review.md @@ -0,0 +1,26 @@ +--- +type: Decision +title: Parallel feature waves and consolidated review +description: The dashboard supports fixed dependency-ready implementation waves and commit-backed multi-feature review without weakening explicit acceptance. +status: accepted +date: 2026-07-17 +tags: [workflow, dashboard, review, automation] +files: ["lib/gather.mjs", "lib/views/planning.mjs", "lib/views/hub.mjs", "lib/views/review.mjs", "lib/session-server.mjs", "lib/ui.mjs", "lib/pi-tools.mjs", "extensions/iterator.js", "skills/iterator-implement/SKILL.md", "skills/iterator-review/SKILL.md", "test/gather.test.mjs", "test/ui.test.mjs", "test/session-server.test.mjs", "test/pi-tools.test.mjs"] +timestamp: 2026-07-17T14:57:44.258Z +--- + +## Decision + +Keep durable backlog CRUD available while an agent is working, but continue blocking actions that would start a second model flow. A ready-feature wave snapshots the server-derived pending-and-ready set at click time, implements each member independently, and never adds later-unblocked features to that wave. Pause requeues the interrupted feature and waits for its aborted turn to finish before Continue can resume it. + +Review remains an explicit acceptance gate. The dashboard can open a consolidated review for implemented features with recorded commits; each feature's diff is rebuilt independently and stays selectable and attributable for findings and acceptance. + +## Consequences + +Browser views render server-derived readiness and review scope rather than inferring workflow state. Automated implementation may prepare work and run checks, but does not mark features done. The shared-library changes are synchronized into the shipped skill copies, and the review UI remains responsive on narrow screens. + +# Retired plan + +Condensed from plan "Keep backlog planning available and support parallel feature waves" (3 features, archived under /features/archive/2026-07-17-parallel-feature-waves-and-consolidated-review/). + +Token usage: 6985200 in / 44878 out / 29827584 cache-read / 0 cache-write over 215 turns (per-step breakdown in the archived usage.md). diff --git a/memory/features/always-available-backlog.md b/memory/features/always-available-backlog.md index ed37f02..d566485 100644 --- a/memory/features/always-available-backlog.md +++ b/memory/features/always-available-backlog.md @@ -1,37 +1,49 @@ --- type: Feature -title: Keep the backlog available during active work -description: Planning continues to show and accept saved filesystem backlog candidates while a plan is active. +title: Keep the idea backlog editable during agent work +description: Let users create, edit, delete, and select backlog candidates while an implementation turn is running. status: done size: medium depends_on: [] -files: ["lib/gather.mjs", "lib/views/planning.mjs", "lib/session-server.mjs", "test/gather.test.mjs", "test/ui.test.mjs", "test/session-server.test.mjs"] -memories: [pitfalls/cancel-now-after-grace-timer, architecture/browser-server-contract, architecture/workflow-state-ownership, decisions/consume-accepted-backlog-ideas, decisions/iterator-dashboard-feature-workflow, decisions/polish-dashboard-and-multi-agent-workflows, decisions/settings-close-returns-to-work] -timestamp: "2026-07-17T14:16:43.824Z" +files: ["lib/session-server.mjs", "lib/views/planning.mjs", "extensions/iterator.js", "test/session-server.test.mjs", "test/ui.test.mjs"] +memories: [pitfalls/cancel-now-after-grace-timer, architecture/browser-server-contract, architecture/package-and-skill-layout, architecture/workflow-state-ownership, decisions/consume-accepted-backlog-ideas, decisions/iterator-dashboard-feature-workflow, decisions/parallel-feature-waves-and-consolidated-review, decisions/polish-dashboard-and-multi-agent-workflows] +timestamp: "2026-07-17T15:14:16.187Z" tags: [] commits: + - sha: f44d2f9e2da9ba16c40498873fadf4824c686891 + kind: feature + date: 2026-07-17 - sha: f44d2f9e2da9ba16c40498873fadf4824c686891 kind: implement date: 2026-07-17 -done: 2026-07-17 + - sha: 9ac6ae09207725a4f1be9ba3c94bcb5a3e18b8c1 + kind: implement + date: 2026-07-17 reviewed: 2026-07-17 +done: 2026-07-17 --- # Implementation notes -Expose the existing bundle backlog in the active-plan Planning payload and keep its mutation path limited to deterministic approved-plan consumption. Render the same compact candidate controls for active and idle plans, including the responsive interaction behavior already used by Planning. +Treat backlog CRUD as a deterministic filesystem mutation, not a new model flow. Permit only `{ type: "backlog" }` through the session server while it is working; reject every other unsolicited action. Keep the Planning UI responsive, retain selection intent until deterministic plan approval, and refresh the dashboard after the write. # Snippets ```js -return { step: "hub", plan: { title, status }, stage, features, backlog: b.backlog }; +const backlogWrite = parsed.type === "backlog"; +if (!pending && working != null && !backlogWrite) { + res.writeHead(409, { "Content-Type": "application/json" }); + res.end('{"busy":true}'); + return; +} ``` # Blast radius -Planning payload and dashboard interactions; backlog candidates must still be consumed only by approved plan writes. +The session dashboard's concurrency guard and Planning backlog controls; no second agent flow may start while backlog CRUD remains available. # Review ## 2026-07-17 -* **Approved** _(agent review: openai-codex/gpt-5.6-sol)_ — Backlog CRUD and selection remain available during active agent work while all model-dispatching dashboard actions stay protected by the busy guard; focused tests cover both allowed and rejected submissions. +* **Approved** _(agent review: openai-codex/gpt-5.6-sol)_ — Approved: backlog writes preserve the active model-working guard, unrelated submissions remain blocked, local controls are restored for further filesystem-only edits, and the regression/full test suites pass. +* **Needs changes** _(agent review: openai-codex/gpt-5.6-sol)_ — Backlog CRUD clears the active agent-working guard: `saveBacklog()` in `extensions/iterator.js` calls `session.showWorking("Saving backlog candidate…")`, then `clearWorking()` and `refreshHub()`. When invoked during implementation, this overwrites and removes the model turn's working state, re-enabling unrelated dashboard actions while the agent is still running. Preserve/restore the existing working state (or make backlog saves use a separate non-destructive status path), and add an integration test proving a backlog save during active work leaves other `/submit` actions blocked with 409 until the model turn actually ends. diff --git a/memory/features/archive/2026-07-17-parallel-feature-waves-and-consolidated-review/always-available-backlog.md b/memory/features/archive/2026-07-17-parallel-feature-waves-and-consolidated-review/always-available-backlog.md new file mode 100644 index 0000000..ed37f02 --- /dev/null +++ b/memory/features/archive/2026-07-17-parallel-feature-waves-and-consolidated-review/always-available-backlog.md @@ -0,0 +1,37 @@ +--- +type: Feature +title: Keep the backlog available during active work +description: Planning continues to show and accept saved filesystem backlog candidates while a plan is active. +status: done +size: medium +depends_on: [] +files: ["lib/gather.mjs", "lib/views/planning.mjs", "lib/session-server.mjs", "test/gather.test.mjs", "test/ui.test.mjs", "test/session-server.test.mjs"] +memories: [pitfalls/cancel-now-after-grace-timer, architecture/browser-server-contract, architecture/workflow-state-ownership, decisions/consume-accepted-backlog-ideas, decisions/iterator-dashboard-feature-workflow, decisions/polish-dashboard-and-multi-agent-workflows, decisions/settings-close-returns-to-work] +timestamp: "2026-07-17T14:16:43.824Z" +tags: [] +commits: + - sha: f44d2f9e2da9ba16c40498873fadf4824c686891 + kind: implement + date: 2026-07-17 +done: 2026-07-17 +reviewed: 2026-07-17 +--- + +# Implementation notes + +Expose the existing bundle backlog in the active-plan Planning payload and keep its mutation path limited to deterministic approved-plan consumption. Render the same compact candidate controls for active and idle plans, including the responsive interaction behavior already used by Planning. + +# Snippets + +```js +return { step: "hub", plan: { title, status }, stage, features, backlog: b.backlog }; +``` + +# Blast radius + +Planning payload and dashboard interactions; backlog candidates must still be consumed only by approved plan writes. + +# Review + +## 2026-07-17 +* **Approved** _(agent review: openai-codex/gpt-5.6-sol)_ — Backlog CRUD and selection remain available during active agent work while all model-dispatching dashboard actions stay protected by the busy guard; focused tests cover both allowed and rejected submissions. diff --git a/memory/features/archive/2026-07-17-parallel-feature-waves-and-consolidated-review/implement-ready-feature-wave.md b/memory/features/archive/2026-07-17-parallel-feature-waves-and-consolidated-review/implement-ready-feature-wave.md new file mode 100644 index 0000000..3148d21 --- /dev/null +++ b/memory/features/archive/2026-07-17-parallel-feature-waves-and-consolidated-review/implement-ready-feature-wave.md @@ -0,0 +1,45 @@ +--- +type: Feature +title: Implement a dependency-ready feature wave +description: A Work action launches implementation for every feature ready at the start of the wave and reports each result. +status: done +size: large +depends_on: [] +files: ["lib/gather.mjs", "lib/status.mjs", "lib/views/hub.mjs", "extensions/iterator.js", "skills/iterator-implement/SKILL.md", "test/gather.test.mjs", "test/status.test.mjs", "test/ui.test.mjs", "test/session-server.test.mjs"] +memories: [pitfalls/cancel-now-after-grace-timer, pitfalls/client-js-template-literal-escaping, architecture/package-and-skill-layout, architecture/workflow-state-ownership, patterns/safe-browser-rendering, decisions/iterator-dashboard-feature-workflow, decisions/polish-dashboard-and-multi-agent-workflows, decisions/powerline-shows-sandbox-ui-port] +timestamp: "2026-07-17T14:33:03.325Z" +tags: [] +commits: + - sha: 01e5a15cedfc71138680d73ed63276ba010eaca8 + kind: implement + date: 2026-07-17 + - sha: cc53c06e7cf02c1c31774902ecc72f585b9fa0ec + kind: implement + date: 2026-07-17 + - sha: 93ee1955897efe51f805bf23f284b593c4e6cf08 + kind: implement + date: 2026-07-17 +reviewed: 2026-07-17 +done: 2026-07-17 +--- + +# Implementation notes + +Use server-derived readiness rather than browser filtering. Add an extension/skill orchestration entry point that snapshots ready features, processes only that snapshot, records per-feature success or failure, refreshes the dashboard between results, and leaves every feature subject to the existing review/acceptance lifecycle. + +# Snippets + +```js +const ready = readiness(b.features, b.settings);\nconst features = b.features.map((c) => ({ name: c.slug, ...ready.get(c.slug) })); +``` + +# Blast radius + +Work controls and feature lifecycle coordination; must not auto-accept or include features unblocked after the wave starts. + +# Review + +## 2026-07-17 +* **Approved** _(agent review: openai-codex/gpt-5.6-sol)_ — The ready-wave snapshot is server-derived and fixed, each feature commits independently without auto-acceptance, failures are reported, and Pause/Continue now serializes resumption behind the aborted turn’s agent_end in both lifecycle orderings. +* **Needs changes** _(agent review: openai-codex/gpt-5.6-sol)_ — Pause/Continue still has a race: Continue can call advanceFeatureWave and dispatch the retried feature before the aborted turn's agent_end fires; that stale agent_end then sees the retried feature active but still pending, marks it failed, and dispatches the next queue member concurrently. Track an abort-in-flight flag/generation: Pause requeues and marks the old turn pending completion, Continue waits when that flag is set, and agent_end clears it then resumes only if state is no longer paused. Add regression coverage for Continue both before and after the aborted agent_end. +* **Needs changes** _(agent review: openai-codex/gpt-5.6-sol)_ — Wave execution ignores the dashboard Pause control: onControl('pause') leaves featureWave active, and agent_end still calls advanceFeatureWave without checking state.paused, so the aborted active feature is marked failed and the next queued feature is dispatched immediately. Preserve/requeue the active item on pause, make advanceFeatureWave no-op while paused, and have Continue resume the wave before falling back to auto mode; add regression coverage for this transition. diff --git a/memory/features/archive/2026-07-17-parallel-feature-waves-and-consolidated-review/index.md b/memory/features/archive/2026-07-17-parallel-feature-waves-and-consolidated-review/index.md new file mode 100644 index 0000000..feeaf9e --- /dev/null +++ b/memory/features/archive/2026-07-17-parallel-feature-waves-and-consolidated-review/index.md @@ -0,0 +1,5 @@ +# Features + +* [Keep the backlog available during active work](always-available-backlog.md) - ✅ done · medium · Planning continues to show and accept saved filesystem backlog candidates while a plan is active. +* [Implement a dependency-ready feature wave](implement-ready-feature-wave.md) - ✅ done · large · A Work action launches implementation for every feature ready at the start of the wave and reports each result. +* [Review implemented features together](review-multiple-implemented-features.md) - ✅ done · large · A consolidated review lets users select an implemented feature and inspect only that feature’s diff and findings. diff --git a/memory/features/archive/2026-07-17-parallel-feature-waves-and-consolidated-review/plan.md b/memory/features/archive/2026-07-17-parallel-feature-waves-and-consolidated-review/plan.md new file mode 100644 index 0000000..b87eb7d --- /dev/null +++ b/memory/features/archive/2026-07-17-parallel-feature-waves-and-consolidated-review/plan.md @@ -0,0 +1,52 @@ +--- +type: Plan +title: Keep backlog planning available and support parallel feature waves +description: Allow durable backlog planning during active work and add wave-level implementation and consolidated review controls. +status: approved +branch: iterator/consume-accepted-backlog-ideas +created: 2026-07-17 +timestamp: "2026-07-17T14:42:24.417Z" +plan_reviewed: 2026-07-17 +--- + +# Goal + +Keep saved ideas available for planning while other agent work is in progress, and let users implement every dependency-ready feature as a wave with a consolidated, selectable review experience. This reduces workflow idle time without weakening deterministic state or the explicit acceptance gate. + +# Architecture + +- Extend the Planning payload and view so saved filesystem-backed backlog candidates remain readable and selectable independently of an active plan; preserve the approved-plan-only consumption boundary from `decisions/consume-accepted-backlog-ideas` and `decisions/iterator-dashboard-feature-workflow`. +- Add server-derived wave orchestration contracts around existing feature readiness: `lib/status.mjs` remains the authority for dependency satisfaction, while gather supplies the ready feature set and views only render it (`architecture/workflow-state-ownership`). +- Build wave implementation as a deterministic sequence/coordination flow that runs each initially dependency-ready feature independently, records per-feature results, and does not infer or mutate lifecycle state in the browser. +- Extend review payloads and the dashboard review UI to scope multiple implemented features and offer a left-hand feature selector whose selected diff is shown on the right; keep session-server output and interaction contracts machine-readable (`architecture/browser-server-contract`). +- Keep canonical changes in `lib/` and synchronize shipped skill copies after implementation, following the established package-and-skill layout. + +# Dependencies + +(none) + +# Key decisions + +- Backlog candidates remain active planning inputs throughout implementation and are removed only by successful deterministic plan approval (`decisions/consume-accepted-backlog-ideas`). +- “Implement next wave” targets only features that are dependency-ready at the wave's start; it must surface individual failures and never treat newly unblocked features as part of that same wave. +- Automated wave implementation may run its own checks/review preparation, but it must retain Pi's explicit user-controlled review and acceptance gate rather than silently marking features done (`decisions/polish-dashboard-and-multi-agent-workflows`). +- “Review all” aggregates only implemented wave/plan features into one review session, with per-feature diffs and findings kept attributable to the selected feature. +- The dashboard additions follow `memory/design.md`: compact dark controls, blue only for primary/progress actions, responsive stacking below 640px, and no workflow-state derivation in client code. + +# Features + +* [Keep the backlog available during active work](/features/always-available-backlog.md) - Planning continues to show and accept saved filesystem backlog candidates while a plan is active. +* [Implement a dependency-ready feature wave](/features/implement-ready-feature-wave.md) - A Work action launches implementation for every feature ready at the start of the wave and reports each result. +* [Review implemented features together](/features/review-multiple-implemented-features.md) - A consolidated review lets users select an implemented feature and inspect only that feature’s diff and findings. + +# Plan review + +## 2026-07-17 _(agent review: openai-codex/gpt-5.6-sol)_ + +## Verdict + +The five feature commits deliver the approved goal and follow the recorded architecture and key decisions: backlog CRUD/selection remains available during agent work; the dependency-ready set is derived server-side and frozen per implementation wave; wave implementation stops at `implemented` for explicit review; and consolidated review rebuilds each implemented feature from its own commits with selectable, attributable diffs. The dashboard controls follow the saved compact/responsive design, shared `lib/` changes were synchronized into shipped skill copies, all three features are done with approved reviews, and the full test suite passed (346 tests). + +## Loose end + +- **Working tree outside the bundle is not clean.** Uncommitted changes remain in `lib/gather.mjs`, `lib/pi-tools.mjs`, `lib/session-server.mjs`, `test/gather.test.mjs`, `test/pi-tools.test.mjs`, and `test/session-server.test.mjs`. They are not part of the reviewed feature commits and should be inspected, discarded, or committed before retiring/merging the plan. `git diff --check` reports no whitespace errors. diff --git a/memory/features/archive/2026-07-17-parallel-feature-waves-and-consolidated-review/review-multiple-implemented-features.md b/memory/features/archive/2026-07-17-parallel-feature-waves-and-consolidated-review/review-multiple-implemented-features.md new file mode 100644 index 0000000..a3f136d --- /dev/null +++ b/memory/features/archive/2026-07-17-parallel-feature-waves-and-consolidated-review/review-multiple-implemented-features.md @@ -0,0 +1,37 @@ +--- +type: Feature +title: Review implemented features together +description: A consolidated review lets users select an implemented feature and inspect only that feature’s diff and findings. +status: done +size: large +depends_on: [] +files: ["lib/gather.mjs", "lib/views/hub.mjs", "lib/views/review.mjs", "lib/session-server.mjs", "extensions/iterator.js", "test/gather.test.mjs", "test/ui.test.mjs", "test/session-server.test.mjs"] +memories: [pitfalls/cancel-now-after-grace-timer, pitfalls/client-js-template-literal-escaping, architecture/browser-server-contract, architecture/package-and-skill-layout, architecture/workflow-state-ownership, patterns/safe-browser-rendering, decisions/iterator-dashboard-feature-workflow, decisions/polish-dashboard-and-multi-agent-workflows] +timestamp: "2026-07-17T14:41:24.387Z" +tags: [] +commits: + - sha: e5853320eff434b2aaecca8069ceabceeecdffa3 + kind: implement + date: 2026-07-17 +done: 2026-07-17 +reviewed: 2026-07-17 +--- + +# Implementation notes + +Extend review gathering with an explicit multi-feature scope that rebuilds each selected feature from its own commits, retains per-feature ownership and pitfall findings, and renders a responsive left selector with a selected-feature diff pane. Accept/review feedback must remain attributable to each feature and preserve the explicit acceptance gate. + +# Snippets + +```js +const selected = opts.feature ? b.features.filter((c) => c.slug === opts.feature) : b.features;\nconst features = [];\nfor (const c of selected) { /* build attributed diff */ } +``` + +# Blast radius + +Review gather/render and commit acceptance interfaces; a multi-review must not blend unrelated diffs or silently mark any feature done. + +# Review + +## 2026-07-17 +* **Approved** _(agent review: openai-codex/gpt-5.6-sol)_ — Review all is explicitly scoped to implemented features with recorded commits, rebuilds and attributes each feature diff independently, preserves per-feature feedback and acceptance, and keeps the existing selector usable on desktop and mobile. diff --git a/memory/features/archive/2026-07-17-parallel-feature-waves-and-consolidated-review/usage.md b/memory/features/archive/2026-07-17-parallel-feature-waves-and-consolidated-review/usage.md new file mode 100644 index 0000000..cb4185c --- /dev/null +++ b/memory/features/archive/2026-07-17-parallel-feature-waves-and-consolidated-review/usage.md @@ -0,0 +1,45 @@ +--- +type: Usage +title: Token usage +description: Per-step model/token ledger for the active plan — written only by the usage op. +totals: "{\"steps\":{\"hub\":{\"openai-codex/gpt-5.6-terra\":{\"input\":175681,\"output\":515,\"cacheRead\":308736,\"cacheWrite\":0,\"turns\":5}},\"plan\":{\"openai-codex/gpt-5.6-terra\":{\"input\":58734,\"output\":3635,\"cacheRead\":366080,\"cacheWrite\":0,\"turns\":19}},\"implement\":{\"openai-codex/gpt-5.6-terra\":{\"input\":235343,\"output\":6089,\"cacheRead\":2935808,\"cacheWrite\":0,\"turns\":43},\"openai-codex/gpt-5.6-sol\":{\"input\":4385841,\"output\":28968,\"cacheRead\":23019008,\"cacheWrite\":0,\"turns\":126}},\"review\":{\"openai-codex/gpt-5.6-sol\":{\"input\":2129601,\"output\":5671,\"cacheRead\":3197952,\"cacheWrite\":0,\"turns\":22}}},\"features\":{\"retire-plan\":{\"input\":175681,\"output\":515,\"cacheRead\":308736,\"cacheWrite\":0,\"turns\":5},\"always-available-backlog\":{\"input\":436207,\"output\":6962,\"cacheRead\":3155456,\"cacheWrite\":0,\"turns\":47},\"implement-ready-feature-wave\":{\"input\":3803315,\"output\":22133,\"cacheRead\":14354944,\"cacheWrite\":0,\"turns\":95},\"review-multiple-implemented-features\":{\"input\":2483284,\"output\":10394,\"cacheRead\":9985024,\"cacheWrite\":0,\"turns\":44}}}" +timestamp: 2026-07-17T14:42:34.466Z +--- + +# Usage + +## hub + +| model | input | output | cache read | cache write | turns | +| --- | ---: | ---: | ---: | ---: | ---: | +| openai-codex/gpt-5.6-terra | 175681 | 515 | 308736 | 0 | 5 | + +## plan + +| model | input | output | cache read | cache write | turns | +| --- | ---: | ---: | ---: | ---: | ---: | +| openai-codex/gpt-5.6-terra | 58734 | 3635 | 366080 | 0 | 19 | + +## implement + +| model | input | output | cache read | cache write | turns | +| --- | ---: | ---: | ---: | ---: | ---: | +| openai-codex/gpt-5.6-terra | 235343 | 6089 | 2935808 | 0 | 43 | +| openai-codex/gpt-5.6-sol | 4385841 | 28968 | 23019008 | 0 | 126 | + +## review + +| model | input | output | cache read | cache write | turns | +| --- | ---: | ---: | ---: | ---: | ---: | +| openai-codex/gpt-5.6-sol | 2129601 | 5671 | 3197952 | 0 | 22 | + +## Per feature + +| feature | input | output | cache read | cache write | turns | +| --- | ---: | ---: | ---: | ---: | ---: | +| retire-plan | 175681 | 515 | 308736 | 0 | 5 | +| always-available-backlog | 436207 | 6962 | 3155456 | 0 | 47 | +| implement-ready-feature-wave | 3803315 | 22133 | 14354944 | 0 | 95 | +| review-multiple-implemented-features | 2483284 | 10394 | 9985024 | 0 | 44 | + +Total: 6985200 in / 44878 out / 29827584 cache-read / 0 cache-write over 215 turns. diff --git a/memory/features/implement-ready-feature-wave.md b/memory/features/implement-ready-feature-wave.md index 3148d21..f327798 100644 --- a/memory/features/implement-ready-feature-wave.md +++ b/memory/features/implement-ready-feature-wave.md @@ -1,13 +1,13 @@ --- type: Feature -title: Implement a dependency-ready feature wave -description: A Work action launches implementation for every feature ready at the start of the wave and reports each result. +title: Implement a fixed dependency-ready feature wave +description: Start and advance a snapshot of every pending feature that is ready when the user clicks Implement next wave. status: done -size: large +size: medium depends_on: [] -files: ["lib/gather.mjs", "lib/status.mjs", "lib/views/hub.mjs", "extensions/iterator.js", "skills/iterator-implement/SKILL.md", "test/gather.test.mjs", "test/status.test.mjs", "test/ui.test.mjs", "test/session-server.test.mjs"] -memories: [pitfalls/cancel-now-after-grace-timer, pitfalls/client-js-template-literal-escaping, architecture/package-and-skill-layout, architecture/workflow-state-ownership, patterns/safe-browser-rendering, decisions/iterator-dashboard-feature-workflow, decisions/polish-dashboard-and-multi-agent-workflows, decisions/powerline-shows-sandbox-ui-port] -timestamp: "2026-07-17T14:33:03.325Z" +files: ["lib/gather.mjs", "lib/views/hub.mjs", "lib/pi-tools.mjs", "extensions/iterator.js", "skills/iterator-implement/SKILL.md", "test/gather.test.mjs", "test/ui.test.mjs", "test/pi-tools.test.mjs"] +memories: [pitfalls/client-js-template-literal-escaping, architecture/package-and-skill-layout, architecture/workflow-state-ownership, patterns/safe-browser-rendering, decisions/iterator-dashboard-feature-workflow, decisions/parallel-feature-waves-and-consolidated-review, decisions/polish-dashboard-and-multi-agent-workflows, decisions/powerline-shows-sandbox-ui-port] +timestamp: "2026-07-17T15:17:49.567Z" tags: [] commits: - sha: 01e5a15cedfc71138680d73ed63276ba010eaca8 @@ -19,27 +19,36 @@ commits: - sha: 93ee1955897efe51f805bf23f284b593c4e6cf08 kind: implement date: 2026-07-17 -reviewed: 2026-07-17 done: 2026-07-17 +reviewed: 2026-07-17 --- # Implementation notes -Use server-derived readiness rather than browser filtering. Add an extension/skill orchestration entry point that snapshots ready features, processes only that snapshot, records per-feature success or failure, refreshes the dashboard between results, and leaves every feature subject to the existing review/acceptance lifecycle. +Gather server-derived ready-wave candidates, expose the Work-tab control, and dispatch each snapshot member independently. Persist queue, active feature, and result state through pause/resume; do not add features unblocked later, and record decision conflicts or failed rounds without dispatching them. Preserve the separate explicit review gate after implementation. # Snippets ```js -const ready = readiness(b.features, b.settings);\nconst features = b.features.map((c) => ({ name: c.slug, ...ready.get(c.slug) })); +if (feature.status === "pending") { + next.active = featureName; + return { + wave: next, + action: { + step: "implement", + role: "implementer", + feature: featureName, + cmd: `/skill:iterator-implement ${featureName} --auto`, + }, + }; +} ``` # Blast radius -Work controls and feature lifecycle coordination; must not auto-accept or include features unblocked after the wave starts. +Feature lifecycle dispatch, automatic-flow controls, and the Work dashboard; readiness must remain server-derived by lib/status.mjs and lib/gather.mjs. # Review ## 2026-07-17 -* **Approved** _(agent review: openai-codex/gpt-5.6-sol)_ — The ready-wave snapshot is server-derived and fixed, each feature commits independently without auto-acceptance, failures are reported, and Pause/Continue now serializes resumption behind the aborted turn’s agent_end in both lifecycle orderings. -* **Needs changes** _(agent review: openai-codex/gpt-5.6-sol)_ — Pause/Continue still has a race: Continue can call advanceFeatureWave and dispatch the retried feature before the aborted turn's agent_end fires; that stale agent_end then sees the retried feature active but still pending, marks it failed, and dispatches the next queue member concurrently. Track an abort-in-flight flag/generation: Pause requeues and marks the old turn pending completion, Continue waits when that flag is set, and agent_end clears it then resumes only if state is no longer paused. Add regression coverage for Continue both before and after the aborted agent_end. -* **Needs changes** _(agent review: openai-codex/gpt-5.6-sol)_ — Wave execution ignores the dashboard Pause control: onControl('pause') leaves featureWave active, and agent_end still calls advanceFeatureWave without checking state.paused, so the aborted active feature is marked failed and the next queued feature is dispatched immediately. Preserve/requeue the active item on pause, make advanceFeatureWave no-op while paused, and have Continue resume the wave before falling back to auto mode; add regression coverage for this transition. +* **Approved** _(agent review: openai-codex/gpt-5.6-sol)_ — Approved: the server-derived ready set is snapshotted once, each queued feature dispatches independently and stops at implemented, failures/conflicts remain attributable, and pause/continue serialization prevents duplicate or skipped dispatches. diff --git a/memory/features/index.md b/memory/features/index.md index feeaf9e..82e8c58 100644 --- a/memory/features/index.md +++ b/memory/features/index.md @@ -1,5 +1,5 @@ # Features -* [Keep the backlog available during active work](always-available-backlog.md) - ✅ done · medium · Planning continues to show and accept saved filesystem backlog candidates while a plan is active. -* [Implement a dependency-ready feature wave](implement-ready-feature-wave.md) - ✅ done · large · A Work action launches implementation for every feature ready at the start of the wave and reports each result. -* [Review implemented features together](review-multiple-implemented-features.md) - ✅ done · large · A consolidated review lets users select an implemented feature and inspect only that feature’s diff and findings. +* [Keep the idea backlog editable during agent work](always-available-backlog.md) - ✅ done · medium · Let users create, edit, delete, and select backlog candidates while an implementation turn is running. +* [Implement a fixed dependency-ready feature wave](implement-ready-feature-wave.md) - ✅ done · medium · Start and advance a snapshot of every pending feature that is ready when the user clicks Implement next wave. +* [Review all implemented features together](review-multiple-implemented-features.md) - ✅ done · medium · Open one selectable, commit-backed review for every implemented feature that has recorded commits. diff --git a/memory/features/review-multiple-implemented-features.md b/memory/features/review-multiple-implemented-features.md index a3f136d..c9c2da9 100644 --- a/memory/features/review-multiple-implemented-features.md +++ b/memory/features/review-multiple-implemented-features.md @@ -1,37 +1,47 @@ --- type: Feature -title: Review implemented features together -description: A consolidated review lets users select an implemented feature and inspect only that feature’s diff and findings. +title: Review all implemented features together +description: Open one selectable, commit-backed review for every implemented feature that has recorded commits. status: done -size: large +size: medium depends_on: [] -files: ["lib/gather.mjs", "lib/views/hub.mjs", "lib/views/review.mjs", "lib/session-server.mjs", "extensions/iterator.js", "test/gather.test.mjs", "test/ui.test.mjs", "test/session-server.test.mjs"] -memories: [pitfalls/cancel-now-after-grace-timer, pitfalls/client-js-template-literal-escaping, architecture/browser-server-contract, architecture/package-and-skill-layout, architecture/workflow-state-ownership, patterns/safe-browser-rendering, decisions/iterator-dashboard-feature-workflow, decisions/polish-dashboard-and-multi-agent-workflows] -timestamp: "2026-07-17T14:41:24.387Z" +files: ["lib/gather.mjs", "lib/views/hub.mjs", "lib/views/review.mjs", "extensions/iterator.js", "skills/iterator-review/SKILL.md", "test/gather.test.mjs", "test/ui.test.mjs", "test/session-server.test.mjs"] +memories: [pitfalls/cancel-now-after-grace-timer, pitfalls/client-js-template-literal-escaping, architecture/package-and-skill-layout, architecture/workflow-state-ownership, patterns/safe-browser-rendering, decisions/iterator-dashboard-feature-workflow, decisions/parallel-feature-waves-and-consolidated-review, decisions/polish-dashboard-and-multi-agent-workflows] +timestamp: "2026-07-17T15:27:27.022Z" tags: [] commits: - sha: e5853320eff434b2aaecca8069ceabceeecdffa3 kind: implement date: 2026-07-17 -done: 2026-07-17 + - sha: 3a7f49f30705940e761692e6649bf3c833cdfe7f + kind: implement + date: 2026-07-17 reviewed: 2026-07-17 +done: 2026-07-17 --- # Implementation notes -Extend review gathering with an explicit multi-feature scope that rebuilds each selected feature from its own commits, retains per-feature ownership and pitfall findings, and renders a responsive left selector with a selected-feature diff pane. Accept/review feedback must remain attributable to each feature and preserve the explicit acceptance gate. +Expose Review all only for a nontrivial server-derived review scope. Gather each implemented feature's recorded commits independently, combine the feature-grouped payload without losing attribution, and render a sidebar selector with each feature's own diff, findings, tests, and pitfalls. Keep acceptance explicit and per-feature even in the consolidated view. # Snippets ```js -const selected = opts.feature ? b.features.filter((c) => c.slug === opts.feature) : b.features;\nconst features = [];\nfor (const c of selected) { /* build attributed diff */ } +if (opts.feature === "all") { + const selected = b.features.filter(c => + c.fm.status === "implemented" && resolveFeatureCommits(b.root, c).length > 0, + ); + const rounds = selected.map(c => gatherReview(b.root, { feature: c.slug })); + return { multiReview: true, reviewScope: selected.map(c => c.slug), features: rounds.flatMap(round => round.features) }; +} ``` # Blast radius -Review gather/render and commit acceptance interfaces; a multi-review must not blend unrelated diffs or silently mark any feature done. +Commit attribution, review findings, and explicit feature acceptance; unrelated working-tree changes must not pollute a feature's commit-backed diff. # Review ## 2026-07-17 -* **Approved** _(agent review: openai-codex/gpt-5.6-sol)_ — Review all is explicitly scoped to implemented features with recorded commits, rebuilds and attributes each feature diff independently, preserves per-feature feedback and acceptance, and keeps the existing selector usable on desktop and mobile. +* **Approved** _(agent review: openai-codex/gpt-5.6-sol)_ — Approved: consolidated scope is server-derived from implemented features with commits, each feature diff is rebuilt independently, cross-declared commit files remain visible as incidental, feature selection stays attributable, and responsive/client-script tests pass. +* **Needs changes** _(agent review: openai-codex/gpt-5.6-sol)_ — Consolidated review loses cross-declared files from a feature's own commits. `gatherReview(..., { feature: slug })` still resolves ownership against every feature and then drops files when `owner.slug !== opts.feature`; therefore this feature's commit omits its `lib/pi-tools.mjs` review-all command mapping and `test/pi-tools.test.mjs` change because those paths are declared by `implement-ready-feature-wave`. Since `feature: all` composes focused rounds, those commit changes appear in no feature's diff. In commit-backed focused/consolidated review, attribute every changed file from the selected feature's commits to that selected feature (declared when it matches, otherwise incidental), and add a regression where one feature's commit changes a path declared only by another feature. diff --git a/memory/index.md b/memory/index.md index 669e858..8f129b2 100644 --- a/memory/index.md +++ b/memory/index.md @@ -10,8 +10,8 @@ last_memorized_commit: a5d59c50453e568ec486a3c9c017c2506898700e * [Design](design.md) - A compact dark developer control plane with clear workflow state. * [Backlog](backlog/index.md) - Saved ideas and bugs outside active plan features. * [Settings](settings.md) - Project settings (auto mode, models, git flow). +* [Plan](plan.md) - Keep filesystem-backed backlog work available during active agent flows, and add dependency-ready implementation waves with consolidated review. * [Features](features/) - One document per implementation feature. -* [Plan](plan.md) - Allow durable backlog planning during active work and add wave-level implementation and consolidated review controls. * [Architecture](/architecture/) - How the system is structured. * [Decisions](/decisions/) - Durable product and implementation choices agents should preserve. * [Patterns & Conventions](/patterns/) - How code and workflows are written in this repo. diff --git a/memory/log.md b/memory/log.md index c433c34..cf13f0c 100644 --- a/memory/log.md +++ b/memory/log.md @@ -2,6 +2,26 @@ ## 2026-07-17 * **Plan review**: Whole-plan review recorded (agent). +* **Review**: Reviewed [Review all implemented features together](/features/review-multiple-implemented-features.md); approved (agent). +* **Review**: Accepted [Review all implemented features together](/features/review-multiple-implemented-features.md) (committed as feature(review-multiple-implemented-features)). +* **Implementation**: Committed feature(review-multiple-implemented-features) on branch iterator/always-available-backlog; awaiting review. +* **Review**: Reviewed [Review all implemented features together](/features/review-multiple-implemented-features.md); changes (agent). +* Reconciled the pre-existing consolidated multi-feature review implementation. +* **Review**: Reviewed [Implement a fixed dependency-ready feature wave](/features/implement-ready-feature-wave.md); approved (agent). +* **Review**: Accepted [Implement a fixed dependency-ready feature wave](/features/implement-ready-feature-wave.md) (committed as feature(implement-ready-feature-wave)). +* Recorded the pre-existing serialized wave-resume implementation. +* Recorded the pre-existing wave pause/continue implementation. +* Reconciled the pre-existing dependency-ready wave implementation. +* **Review**: Reviewed [Keep the idea backlog editable during agent work](/features/always-available-backlog.md); approved (agent). +* **Review**: Accepted [Keep the idea backlog editable during agent work](/features/always-available-backlog.md) (committed as feature(always-available-backlog)). +* **Implementation**: Committed feature(always-available-backlog) on branch iterator/always-available-backlog; awaiting review. +* **Review**: Reviewed [Keep the idea backlog editable during agent work](/features/always-available-backlog.md); changes (agent). +* Reconciled pre-existing implementation commit for duplicate feature. +* **Update**: Applied 3 feature adjustment(s). +* **Creation**: 3 feature(s) written. +* **Creation**: Plan "Keep backlog planning available and support parallel feature waves" approved on branch main. Consumed 2 selected backlog candidate(s). +* **Retirement**: Plan "Keep backlog planning available and support parallel feature waves" condensed into [Parallel feature waves and consolidated review](/decisions/parallel-feature-waves-and-consolidated-review.md). +* **Plan review**: Whole-plan review recorded (agent). * **Review**: Reviewed [Review implemented features together](/features/review-multiple-implemented-features.md); approved (agent). * **Review**: Accepted [Review implemented features together](/features/review-multiple-implemented-features.md) (committed as feature(review-multiple-implemented-features)). * **Implementation**: Committed feature(review-multiple-implemented-features) on branch iterator/consume-accepted-backlog-ideas; awaiting review. diff --git a/memory/plan.md b/memory/plan.md index b87eb7d..b6798f9 100644 --- a/memory/plan.md +++ b/memory/plan.md @@ -1,25 +1,25 @@ --- type: Plan title: Keep backlog planning available and support parallel feature waves -description: Allow durable backlog planning during active work and add wave-level implementation and consolidated review controls. +description: Keep filesystem-backed backlog work available during active agent flows, and add dependency-ready implementation waves with consolidated review. status: approved -branch: iterator/consume-accepted-backlog-ideas +branch: iterator/keep-backlog-planning-available-and-support-parallel-feature-waves +worktree: /Volumes/Extern/Projects/iterator-iterator-keep-backlog-planning-available-and-support-parallel-feature-waves created: 2026-07-17 -timestamp: "2026-07-17T14:42:24.417Z" +timestamp: "2026-07-17T15:28:32.570Z" plan_reviewed: 2026-07-17 --- # Goal -Keep saved ideas available for planning while other agent work is in progress, and let users implement every dependency-ready feature as a wave with a consolidated, selectable review experience. This reduces workflow idle time without weakening deterministic state or the explicit acceptance gate. +Allow users to continue reading and editing the filesystem-backed idea backlog while an agent is working, without permitting a second model flow. Add a dependency-ready “Implement next wave” workflow and a consolidated “Review all” experience so users can inspect each implemented feature’s attributable diff before explicitly accepting it. # Architecture -- Extend the Planning payload and view so saved filesystem-backed backlog candidates remain readable and selectable independently of an active plan; preserve the approved-plan-only consumption boundary from `decisions/consume-accepted-backlog-ideas` and `decisions/iterator-dashboard-feature-workflow`. -- Add server-derived wave orchestration contracts around existing feature readiness: `lib/status.mjs` remains the authority for dependency satisfaction, while gather supplies the ready feature set and views only render it (`architecture/workflow-state-ownership`). -- Build wave implementation as a deterministic sequence/coordination flow that runs each initially dependency-ready feature independently, records per-feature results, and does not infer or mutate lifecycle state in the browser. -- Extend review payloads and the dashboard review UI to scope multiple implemented features and offer a left-hand feature selector whose selected diff is shown on the right; keep session-server output and interaction contracts machine-readable (`architecture/browser-server-contract`). -- Keep canonical changes in `lib/` and synchronize shipped skill copies after implementation, following the established package-and-skill layout. +- Extend `architecture/workflow-state-ownership`: derive the fixed dependency-ready wave and review scope in `lib/status.mjs`/`lib/gather.mjs`; views consume supplied readiness and scope rather than recalculating them. +- Extend `architecture/browser-server-contract`: carry backlog CRUD, wave-control, and consolidated-review actions through the session server's single-pending-round protocol, retaining machine-readable results. +- Keep the Planning and Work dashboard views compact and responsive under `memory/design.md`, with controls stacking below 640px and selectable feature diffs remaining usable on narrow screens. +- Route Pi tools, the extension, and implement/review skills through the same deterministic gather/write contracts; canonical `lib/` changes are synchronized to shipped skill copies. # Dependencies @@ -27,26 +27,26 @@ Keep saved ideas available for planning while other agent work is in progress, a # Key decisions -- Backlog candidates remain active planning inputs throughout implementation and are removed only by successful deterministic plan approval (`decisions/consume-accepted-backlog-ideas`). -- “Implement next wave” targets only features that are dependency-ready at the wave's start; it must surface individual failures and never treat newly unblocked features as part of that same wave. -- Automated wave implementation may run its own checks/review preparation, but it must retain Pi's explicit user-controlled review and acceptance gate rather than silently marking features done (`decisions/polish-dashboard-and-multi-agent-workflows`). -- “Review all” aggregates only implemented wave/plan features into one review session, with per-feature diffs and findings kept attributable to the selected feature. -- The dashboard additions follow `memory/design.md`: compact dark controls, blue only for primary/progress actions, responsive stacking below 640px, and no workflow-state derivation in client code. +- Follow `decisions/parallel-feature-waves-and-consolidated-review`: backlog CRUD stays available during agent work, but any action that starts another model flow remains blocked. +- Snapshot the pending, dependency-ready features at wave start; implement only that fixed set and never add features that become ready later. +- Preserve the explicit review/acceptance gate: automated implementation may run checks and prepare review, but it must not self-accept features or mark them done. +- Build consolidated review diffs independently from each feature’s recorded commits, keeping feature selection and findings attributable. +- Follow `decisions/consume-accepted-backlog-ideas`: selected candidates remain in the backlog until deterministic plan approval consumes them. +- Follow `decisions/synced-droppable-skill-libs`: update root shared code, run `npm run sync`, and test the synchronized copies rather than hand-editing them. +- Avoid `pitfalls/client-js-template-literal-escaping` when adding view scripts: static JavaScript escapes in backtick templates use doubled backslashes and client-script parse tests cover the payload. # Features -* [Keep the backlog available during active work](/features/always-available-backlog.md) - Planning continues to show and accept saved filesystem backlog candidates while a plan is active. -* [Implement a dependency-ready feature wave](/features/implement-ready-feature-wave.md) - A Work action launches implementation for every feature ready at the start of the wave and reports each result. -* [Review implemented features together](/features/review-multiple-implemented-features.md) - A consolidated review lets users select an implemented feature and inspect only that feature’s diff and findings. +* [Keep the idea backlog editable during agent work](/features/always-available-backlog.md) - Let users create, edit, delete, and select backlog candidates while an implementation turn is running. +* [Implement a fixed dependency-ready feature wave](/features/implement-ready-feature-wave.md) - Start and advance a snapshot of every pending feature that is ready when the user clicks Implement next wave. +* [Review all implemented features together](/features/review-multiple-implemented-features.md) - Open one selectable, commit-backed review for every implemented feature that has recorded commits. # Plan review ## 2026-07-17 _(agent review: openai-codex/gpt-5.6-sol)_ -## Verdict +## Clean bill -The five feature commits deliver the approved goal and follow the recorded architecture and key decisions: backlog CRUD/selection remains available during agent work; the dependency-ready set is derived server-side and frozen per implementation wave; wave implementation stops at `implemented` for explicit review; and consolidated review rebuilds each implemented feature from its own commits with selectable, attributable diffs. The dashboard controls follow the saved compact/responsive design, shared `lib/` changes were synchronized into shipped skill copies, all three features are done with approved reviews, and the full test suite passed (346 tests). - -## Loose end - -- **Working tree outside the bundle is not clean.** Uncommitted changes remain in `lib/gather.mjs`, `lib/pi-tools.mjs`, `lib/session-server.mjs`, `test/gather.test.mjs`, `test/pi-tools.test.mjs`, and `test/session-server.test.mjs`. They are not part of the reviewed feature commits and should be inspected, discarded, or committed before retiring/merging the plan. `git diff --check` reports no whitespace errors. +- **Goal coverage:** Complete. Backlog CRUD remains available during active agent work while unrelated submissions stay blocked; `Implement next wave` uses a fixed server-derived ready snapshot and stops features at `implemented`; `Review all` presents independently rebuilt, selectable commit-backed feature diffs before explicit acceptance. +- **Architecture and decisions:** The implementation follows centralized gather/status readiness, the persistent session-server contract, explicit review gates, responsive dashboard design parameters, and synchronized root/skill library copies. No new dependency or decision contradiction was introduced. +- **Scope and loose ends:** All three features are accepted (`done`), both review findings were addressed by follow-up commits, the complete suite passes, and no unexplained functional scope drift or TODO was found. diff --git a/memory/state.md b/memory/state.md index 73f7444..78147f2 100644 --- a/memory/state.md +++ b/memory/state.md @@ -6,9 +6,9 @@ mode: auto paused: false phase: done active_feature: null -strikes: "{\"implement-ready-feature-wave\":2}" +strikes: "{\"implement-ready-feature-wave\":2,\"always-available-backlog\":1,\"review-multiple-implemented-features\":1}" escalation: null -timestamp: 2026-07-17T14:42:34.686Z +timestamp: 2026-07-17T15:28:48.345Z --- Runtime flow state; read via gather, written only by the state op. diff --git a/memory/usage.md b/memory/usage.md index cb4185c..cc31f83 100644 --- a/memory/usage.md +++ b/memory/usage.md @@ -2,8 +2,8 @@ type: Usage title: Token usage description: Per-step model/token ledger for the active plan — written only by the usage op. -totals: "{\"steps\":{\"hub\":{\"openai-codex/gpt-5.6-terra\":{\"input\":175681,\"output\":515,\"cacheRead\":308736,\"cacheWrite\":0,\"turns\":5}},\"plan\":{\"openai-codex/gpt-5.6-terra\":{\"input\":58734,\"output\":3635,\"cacheRead\":366080,\"cacheWrite\":0,\"turns\":19}},\"implement\":{\"openai-codex/gpt-5.6-terra\":{\"input\":235343,\"output\":6089,\"cacheRead\":2935808,\"cacheWrite\":0,\"turns\":43},\"openai-codex/gpt-5.6-sol\":{\"input\":4385841,\"output\":28968,\"cacheRead\":23019008,\"cacheWrite\":0,\"turns\":126}},\"review\":{\"openai-codex/gpt-5.6-sol\":{\"input\":2129601,\"output\":5671,\"cacheRead\":3197952,\"cacheWrite\":0,\"turns\":22}}},\"features\":{\"retire-plan\":{\"input\":175681,\"output\":515,\"cacheRead\":308736,\"cacheWrite\":0,\"turns\":5},\"always-available-backlog\":{\"input\":436207,\"output\":6962,\"cacheRead\":3155456,\"cacheWrite\":0,\"turns\":47},\"implement-ready-feature-wave\":{\"input\":3803315,\"output\":22133,\"cacheRead\":14354944,\"cacheWrite\":0,\"turns\":95},\"review-multiple-implemented-features\":{\"input\":2483284,\"output\":10394,\"cacheRead\":9985024,\"cacheWrite\":0,\"turns\":44}}}" -timestamp: 2026-07-17T14:42:34.466Z +totals: "{\"steps\":{\"hub\":{\"openai-codex/gpt-5.6-terra\":{\"input\":342718,\"output\":807,\"cacheRead\":2367488,\"cacheWrite\":0,\"turns\":8}},\"plan\":{\"openai-codex/gpt-5.6-terra\":{\"input\":67425,\"output\":6184,\"cacheRead\":585216,\"cacheWrite\":0,\"turns\":20}},\"implement\":{\"openai-codex/gpt-5.6-terra\":{\"input\":110063,\"output\":5537,\"cacheRead\":2072064,\"cacheWrite\":0,\"turns\":27},\"openai-codex/gpt-5.6-sol\":{\"input\":720890,\"output\":9622,\"cacheRead\":9267200,\"cacheWrite\":0,\"turns\":60}},\"review\":{\"openai-codex/gpt-5.6-sol\":{\"input\":939632,\"output\":7482,\"cacheRead\":9461248,\"cacheWrite\":0,\"turns\":56}}},\"features\":{\"retire-plan\":{\"input\":342718,\"output\":807,\"cacheRead\":2367488,\"cacheWrite\":0,\"turns\":8},\"always-available-backlog\":{\"input\":878061,\"output\":13393,\"cacheRead\":6248448,\"cacheWrite\":0,\"turns\":68},\"implement-ready-feature-wave\":{\"input\":170659,\"output\":2421,\"cacheRead\":3658240,\"cacheWrite\":0,\"turns\":23},\"review-multiple-implemented-features\":{\"input\":416010,\"output\":6129,\"cacheRead\":9406976,\"cacheWrite\":0,\"turns\":45}}}" +timestamp: 2026-07-17T15:28:48.101Z --- # Usage @@ -12,34 +12,34 @@ timestamp: 2026-07-17T14:42:34.466Z | model | input | output | cache read | cache write | turns | | --- | ---: | ---: | ---: | ---: | ---: | -| openai-codex/gpt-5.6-terra | 175681 | 515 | 308736 | 0 | 5 | +| openai-codex/gpt-5.6-terra | 342718 | 807 | 2367488 | 0 | 8 | ## plan | model | input | output | cache read | cache write | turns | | --- | ---: | ---: | ---: | ---: | ---: | -| openai-codex/gpt-5.6-terra | 58734 | 3635 | 366080 | 0 | 19 | +| openai-codex/gpt-5.6-terra | 67425 | 6184 | 585216 | 0 | 20 | ## implement | model | input | output | cache read | cache write | turns | | --- | ---: | ---: | ---: | ---: | ---: | -| openai-codex/gpt-5.6-terra | 235343 | 6089 | 2935808 | 0 | 43 | -| openai-codex/gpt-5.6-sol | 4385841 | 28968 | 23019008 | 0 | 126 | +| openai-codex/gpt-5.6-terra | 110063 | 5537 | 2072064 | 0 | 27 | +| openai-codex/gpt-5.6-sol | 720890 | 9622 | 9267200 | 0 | 60 | ## review | model | input | output | cache read | cache write | turns | | --- | ---: | ---: | ---: | ---: | ---: | -| openai-codex/gpt-5.6-sol | 2129601 | 5671 | 3197952 | 0 | 22 | +| openai-codex/gpt-5.6-sol | 939632 | 7482 | 9461248 | 0 | 56 | ## Per feature | feature | input | output | cache read | cache write | turns | | --- | ---: | ---: | ---: | ---: | ---: | -| retire-plan | 175681 | 515 | 308736 | 0 | 5 | -| always-available-backlog | 436207 | 6962 | 3155456 | 0 | 47 | -| implement-ready-feature-wave | 3803315 | 22133 | 14354944 | 0 | 95 | -| review-multiple-implemented-features | 2483284 | 10394 | 9985024 | 0 | 44 | +| retire-plan | 342718 | 807 | 2367488 | 0 | 8 | +| always-available-backlog | 878061 | 13393 | 6248448 | 0 | 68 | +| implement-ready-feature-wave | 170659 | 2421 | 3658240 | 0 | 23 | +| review-multiple-implemented-features | 416010 | 6129 | 9406976 | 0 | 45 | -Total: 6985200 in / 44878 out / 29827584 cache-read / 0 cache-write over 215 turns. +Total: 2180728 in / 29632 out / 23753216 cache-read / 0 cache-write over 171 turns. diff --git a/skills/iterator/lib/gather.mjs b/skills/iterator/lib/gather.mjs index 6d8350b..bdaf3dd 100644 --- a/skills/iterator/lib/gather.mjs +++ b/skills/iterator/lib/gather.mjs @@ -1550,13 +1550,25 @@ export function gatherReview(startDir, opts = {}) { const matches = (o) => o.tests.has(f.path) || o.res.some((re) => re.test(f.path)); let owner = owners.find(matches); - if (opts.feature) { - const selectedOwner = owners.find( - (o) => o.slug === opts.feature && matches(o), - ); - if (selectedOwner) owner = selectedOwner; - } - if (owner) { + const selectedOwner = opts.feature + ? owners.find((o) => o.slug === opts.feature) + : null; + if (opts.feature && source === "commits") { + // The requested feature's commits are the attribution boundary. A path + // declared only by another feature still belongs in this commit-backed + // review as incidental; otherwise focused/consolidated review silently + // drops part of the selected feature's diff. + if (selectedOwner && matches(selectedOwner)) { + owner = selectedOwner; + f.group = selectedOwner.tests.has(f.path) ? "tests" : "declared"; + } else { + owner = { slug: opts.feature }; + f.group = "incidental"; + } + } else if (opts.feature && selectedOwner && matches(selectedOwner)) { + owner = selectedOwner; + f.group = selectedOwner.tests.has(f.path) ? "tests" : "declared"; + } else if (owner) { f.group = owner.tests.has(f.path) ? "tests" : "declared"; } else if (source === "commits") { // Already committed with the focused feature — nothing will be diff --git a/skills/iterator/lib/ui.mjs b/skills/iterator/lib/ui.mjs index 5b6ac6f..77a935e 100644 --- a/skills/iterator/lib/ui.mjs +++ b/skills/iterator/lib/ui.mjs @@ -186,7 +186,7 @@ function primaryClick(){ if(typeof onPrimary==='function') onPrimary(); } async function post(payload, okMsg, options){ if(document.body.classList.contains('iterator-ro') && !options?.allowWhileWorking){ alert('Claude is working — actions are disabled until it finishes.'); - return; + return false; } var btn=document.getElementById('primary'); __submitted = true; @@ -198,13 +198,15 @@ async function post(payload, okMsg, options){ __submitted = false; if(btn){ btn.disabled=false; if(typeof refresh==='function') refresh(); else btn.textContent=btn.dataset.prev||'Accept'; } alert('Not sent — Claude is still working (or this view is stale). Try again when the dashboard refreshes.'); - return; + return false; } if(btn) btn.textContent = '✓ ' + (okMsg||'Sent to Claude'); + return true; }catch(e){ __submitted=false; if(btn){ btn.disabled=false; if(typeof refresh==='function') refresh(); else btn.textContent=btn.dataset.prev||'Accept'; } alert('Could not reach local server: ' + e.message); + return false; } } diff --git a/skills/iterator/lib/views/planning.mjs b/skills/iterator/lib/views/planning.mjs index c24e2c3..21614bf 100644 --- a/skills/iterator/lib/views/planning.mjs +++ b/skills/iterator/lib/views/planning.mjs @@ -54,9 +54,13 @@ const BODY = ` const JS = ` const CH = D.features || []; -function backlogAction(payload, button, message){ +async function backlogAction(payload, button, message){ if(button){ button.disabled = true; button.dataset.label = button.textContent; button.textContent = 'Saving…'; } - post({ type:'backlog', ...payload }, message || 'Saved', { allowWhileWorking:true }); + await post({ type:'backlog', ...payload }, message || 'Saved', { allowWhileWorking:true }); + // During an agent turn the server deliberately does not refresh this view: + // doing so would clear the model-working guard. Restore the local control so + // more filesystem-only backlog edits remain possible until turn-end refresh. + if(button){ button.disabled = false; if(button.dataset.label) button.textContent = button.dataset.label; delete button.dataset.label; } } function selectedBacklogGoal(){ const selected = (D.backlog || []).filter(item => item.selected); diff --git a/test/gather.test.mjs b/test/gather.test.mjs index 8745af2..ba428c9 100644 --- a/test/gather.test.mjs +++ b/test/gather.test.mjs @@ -306,6 +306,10 @@ test("consolidated review rebuilds each implemented feature from its own commits "status: implemented", ), ); + // The auth commit also changes a path declared only by config-module. + // Commit-backed review must keep it under auth as incidental rather than + // dropping it because another feature owns the path contract. + writeFileSync(join(root, "src", "config.ts"), "export const cfg = 2;\n"); git(root, "add", "."); git( root, @@ -329,8 +333,9 @@ test("consolidated review rebuilds each implemented feature from its own commits ); assert.deepEqual( p.features[1].files.map((file) => file.path), - ["src/auth/index.ts"], + ["src/auth/index.ts", "src/config.ts"], ); + assert.equal(p.features[1].files[1].group, "incidental"); } finally { rmSync(root, { recursive: true, force: true }); } diff --git a/test/session-server.test.mjs b/test/session-server.test.mjs index fcd6fcc..bcaf7dc 100644 --- a/test/session-server.test.mjs +++ b/test/session-server.test.mjs @@ -246,6 +246,17 @@ test("backlog writes remain available while an agent is working", async () => { action: "create", title: "Next idea", }); + assert.equal( + session.isWorking(), + true, + "backlog writes preserve the active model guard", + ); + const blocked = await fetch(`${origin}/submit?r=${srvMod.RUN_ID}`, { + method: "POST", + body: '{"type":"action","action":"implement","feature":"other"}', + }); + assert.equal(blocked.status, 409); + assert.deepEqual(await blocked.json(), { busy: true }); } finally { await session.stop(); }