Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions extensions/iterator.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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);
}
}
};

Expand Down
26 changes: 19 additions & 7 deletions lib/gather.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion lib/session-server.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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) };
Expand Down
6 changes: 4 additions & 2 deletions lib/ui.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
}
}

Expand Down
8 changes: 6 additions & 2 deletions lib/views/planning.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
7 changes: 3 additions & 4 deletions memory/backlog/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
1 change: 1 addition & 0 deletions memory/decisions/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
26 changes: 26 additions & 0 deletions memory/decisions/parallel-feature-waves-and-consolidated-review.md
Original file line number Diff line number Diff line change
@@ -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).
32 changes: 22 additions & 10 deletions memory/features/always-available-backlog.md
Original file line number Diff line number Diff line change
@@ -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.
Original file line number Diff line number Diff line change
@@ -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.
Original file line number Diff line number Diff line change
@@ -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.
Original file line number Diff line number Diff line change
@@ -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.
Loading
Loading