From 30c6a6b70a54d59a72a79ba2f864a9b993006262 Mon Sep 17 00:00:00 2001 From: Christoph Maier Date: Fri, 17 Jul 2026 18:19:19 +0200 Subject: [PATCH 01/22] feature(preserve-review-across-planning): preserve active reviews during tab navigation Feature: preserve-review-across-planning --- lib/session-server.mjs | 4 ++ lib/ui.mjs | 7 ++- memory/backlog/index.md | 2 +- .../backlog-planning-and-feature-waves.md | 28 ++++++++++ memory/decisions/index.md | 1 + .../always-available-backlog.md | 0 .../implement-ready-feature-wave.md | 0 .../index.md | 5 ++ .../plan.md | 52 +++++++++++++++++++ .../review-multiple-implemented-features.md | 0 .../usage.md | 45 ++++++++++++++++ memory/features/index.md | 6 +-- .../preserve-review-across-planning.md | 26 ++++++++++ memory/features/show-active-work-in-work.md | 26 ++++++++++ .../features/streamline-review-interface.md | 30 +++++++++++ memory/index.md | 2 +- memory/log.md | 15 ++++++ memory/plan.md | 50 +++++++----------- memory/state.md | 6 +-- memory/usage.md | 28 +++------- skills/iterator/lib/ui.mjs | 7 ++- test/session-server.test.mjs | 4 ++ test/ui.test.mjs | 4 ++ 23 files changed, 283 insertions(+), 65 deletions(-) create mode 100644 memory/decisions/backlog-planning-and-feature-waves.md rename memory/features/{ => archive/2026-07-17-backlog-planning-and-feature-waves}/always-available-backlog.md (100%) rename memory/features/{ => archive/2026-07-17-backlog-planning-and-feature-waves}/implement-ready-feature-wave.md (100%) create mode 100644 memory/features/archive/2026-07-17-backlog-planning-and-feature-waves/index.md create mode 100644 memory/features/archive/2026-07-17-backlog-planning-and-feature-waves/plan.md rename memory/features/{ => archive/2026-07-17-backlog-planning-and-feature-waves}/review-multiple-implemented-features.md (100%) create mode 100644 memory/features/archive/2026-07-17-backlog-planning-and-feature-waves/usage.md create mode 100644 memory/features/preserve-review-across-planning.md create mode 100644 memory/features/show-active-work-in-work.md create mode 100644 memory/features/streamline-review-interface.md diff --git a/lib/session-server.mjs b/lib/session-server.mjs index f365051..68757f0 100644 --- a/lib/session-server.mjs +++ b/lib/session-server.mjs @@ -217,6 +217,10 @@ function applyWorking() { overlay.style.display = 'flex'; } function setTab(t, v) { + // A tab switch replaces the iframe, which fires pagehide in an active + // review. Tell that document this is in-shell navigation before replacing + // it so its cancel beacon cannot settle the pending review round. + try { frame.contentWindow.postMessage({ iterator: 'navigate' }, '*'); } catch (e) {} tab = t; document.getElementById('context').textContent = './' + tab; document.querySelectorAll('#tabs button.tab').forEach(b => b.classList.toggle('sel', b.dataset.tab === t)); diff --git a/lib/ui.mjs b/lib/ui.mjs index 77a935e..000e29b 100644 --- a/lib/ui.mjs +++ b/lib/ui.mjs @@ -156,7 +156,8 @@ const SHARED_JS = ` // server ignore /cancel-/submit from tabs that belong to an earlier round. function __q(path){ return path + (path.indexOf('?')>=0 ? '&' : '?') + 'r=' + __RUN; } let __submitted = false; -function sendCancel(){ if(__submitted) return; __submitted = true; +let __navigating = false; +function sendCancel(){ if(__submitted || __navigating) return; __submitted = true; try{ navigator.sendBeacon(__q('/cancel'),'{}'); }catch(e){} } window.addEventListener('pagehide', sendCancel); async function cancelFlow(){ __submitted = true; @@ -171,7 +172,9 @@ function esc(s){ return String(s==null?'':s) // { iterator:'working', working:bool } into this iframe — non-Work tabs stay // viewable but their mutating controls go inert (see BASE_CSS .iterator-ro). window.addEventListener('message', function(e){ - if(e.source !== window.parent || !e.data || e.data.iterator !== 'working') return; + if(e.source !== window.parent || !e.data) return; + if(e.data.iterator === 'navigate') { __navigating = true; return; } + if(e.data.iterator !== 'working') return; document.body.classList.toggle('iterator-ro', !!e.data.working); }); diff --git a/memory/backlog/index.md b/memory/backlog/index.md index 61faf3c..ded2482 100644 --- a/memory/backlog/index.md +++ b/memory/backlog/index.md @@ -3,7 +3,7 @@ type: Backlog title: Iterator backlog description: Saved ideas and bugs kept separate from active plan features. items: "[]" -timestamp: 2026-07-17T15:00:30.011Z +timestamp: 2026-07-17T16:15:52.935Z --- # Backlog diff --git a/memory/decisions/backlog-planning-and-feature-waves.md b/memory/decisions/backlog-planning-and-feature-waves.md new file mode 100644 index 0000000..46a5ea8 --- /dev/null +++ b/memory/decisions/backlog-planning-and-feature-waves.md @@ -0,0 +1,28 @@ +--- +type: Decision +title: Backlog planning and parallel feature waves +description: Keep low-risk backlog editing available during active work while implementing a fixed ready-feature wave and reviewing its commit-backed results together. +status: accepted +date: 2026-07-17 +tags: [workflow, backlog, parallelism, review] +files: ["lib/session-server.mjs", "lib/gather.mjs", "lib/pi-tools.mjs", "lib/views/planning.mjs", "lib/views/hub.mjs", "lib/views/review.mjs", "extensions/iterator.js", "skills/iterator-implement/SKILL.md", "skills/iterator-review/SKILL.md"] +timestamp: 2026-07-17T16:12:46.582Z +--- + +## Outcome + +The dashboard now permits filesystem-backed idea backlog CRUD while an agent turn is active, but preserves the single-model-flow guard: backlog saves must not clear the existing working state or unblock unrelated submissions. + +Implementation can snapshot every dependency-ready pending feature at wave start and advance only that immutable set. Features that become ready later wait for a later wave, and completed implementation remains at `implemented` until explicit user review and acceptance. + +Consolidated review derives its scope from implemented features with recorded commits and rebuilds a selectable diff for each feature independently. Every file changed by a selected feature's commits stays attributable to that feature; paths not declared by it are shown as incidental rather than omitted. + +## Constraints + +Readiness and review scope remain server-derived through the gather/status contracts, session-server actions retain machine-readable results, and root shared-library changes are synchronized to shipped skill copies. Dashboard controls remain 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-backlog-planning-and-feature-waves/). + +Token usage: 2180728 in / 29632 out / 23753216 cache-read / 0 cache-write over 171 turns (per-step breakdown in the archived usage.md). diff --git a/memory/decisions/index.md b/memory/decisions/index.md index a1de1a5..f8b2944 100644 --- a/memory/decisions/index.md +++ b/memory/decisions/index.md @@ -2,6 +2,7 @@ Durable product and implementation choices agents should preserve. +* [Backlog planning and parallel feature waves](/decisions/backlog-planning-and-feature-waves.md) - Keep low-risk backlog editing available during active work while implementing a fixed ready-feature wave and reviewing its commit-backed results together. * [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. diff --git a/memory/features/always-available-backlog.md b/memory/features/archive/2026-07-17-backlog-planning-and-feature-waves/always-available-backlog.md similarity index 100% rename from memory/features/always-available-backlog.md rename to memory/features/archive/2026-07-17-backlog-planning-and-feature-waves/always-available-backlog.md diff --git a/memory/features/implement-ready-feature-wave.md b/memory/features/archive/2026-07-17-backlog-planning-and-feature-waves/implement-ready-feature-wave.md similarity index 100% rename from memory/features/implement-ready-feature-wave.md rename to memory/features/archive/2026-07-17-backlog-planning-and-feature-waves/implement-ready-feature-wave.md diff --git a/memory/features/archive/2026-07-17-backlog-planning-and-feature-waves/index.md b/memory/features/archive/2026-07-17-backlog-planning-and-feature-waves/index.md new file mode 100644 index 0000000..82e8c58 --- /dev/null +++ b/memory/features/archive/2026-07-17-backlog-planning-and-feature-waves/index.md @@ -0,0 +1,5 @@ +# Features + +* [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/archive/2026-07-17-backlog-planning-and-feature-waves/plan.md b/memory/features/archive/2026-07-17-backlog-planning-and-feature-waves/plan.md new file mode 100644 index 0000000..b6798f9 --- /dev/null +++ b/memory/features/archive/2026-07-17-backlog-planning-and-feature-waves/plan.md @@ -0,0 +1,52 @@ +--- +type: Plan +title: Keep backlog planning available and support parallel feature waves +description: Keep filesystem-backed backlog work available during active agent flows, and add dependency-ready implementation waves with consolidated review. +status: approved +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-17T15:28:32.570Z" +plan_reviewed: 2026-07-17 +--- + +# Goal + +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 `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 + +(none) + +# Key decisions + +- 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 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)_ + +## Clean bill + +- **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/features/review-multiple-implemented-features.md b/memory/features/archive/2026-07-17-backlog-planning-and-feature-waves/review-multiple-implemented-features.md similarity index 100% rename from memory/features/review-multiple-implemented-features.md rename to memory/features/archive/2026-07-17-backlog-planning-and-feature-waves/review-multiple-implemented-features.md diff --git a/memory/features/archive/2026-07-17-backlog-planning-and-feature-waves/usage.md b/memory/features/archive/2026-07-17-backlog-planning-and-feature-waves/usage.md new file mode 100644 index 0000000..cc31f83 --- /dev/null +++ b/memory/features/archive/2026-07-17-backlog-planning-and-feature-waves/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\":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 + +## hub + +| model | input | output | cache read | cache write | turns | +| --- | ---: | ---: | ---: | ---: | ---: | +| 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 | 67425 | 6184 | 585216 | 0 | 20 | + +## implement + +| model | input | output | cache read | cache write | turns | +| --- | ---: | ---: | ---: | ---: | ---: | +| 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 | 939632 | 7482 | 9461248 | 0 | 56 | + +## Per feature + +| feature | input | output | cache read | cache write | turns | +| --- | ---: | ---: | ---: | ---: | ---: | +| 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: 2180728 in / 29632 out / 23753216 cache-read / 0 cache-write over 171 turns. diff --git a/memory/features/index.md b/memory/features/index.md index 82e8c58..5a2f2b5 100644 --- a/memory/features/index.md +++ b/memory/features/index.md @@ -1,5 +1,5 @@ # Features -* [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. +* [Preserve reviews across Planning navigation](preserve-review-across-planning.md) - ⬜ pending · medium · Keep an active review open while users manage backlog items on the Planning tab and return to it. +* [Show active plan context on Work](show-active-work-in-work.md) - ⬜ pending · medium · Make Work the home for the active plan, its feature set, and the dependency graph. +* [Keep review controls fully readable](streamline-review-interface.md) - ⬜ pending · medium · Show complete feature titles in review and remove the unused Feedback panel. diff --git a/memory/features/preserve-review-across-planning.md b/memory/features/preserve-review-across-planning.md new file mode 100644 index 0000000..3cdf0d9 --- /dev/null +++ b/memory/features/preserve-review-across-planning.md @@ -0,0 +1,26 @@ +--- +type: Feature +title: Preserve reviews across Planning navigation +description: Keep an active review open while users manage backlog items on the Planning tab and return to it. +status: implemented +size: medium +depends_on: [] +files: ["lib/session-server.mjs", "extensions/iterator.js", "test/session-server.test.mjs", "test/nonblocking-working-overlay.test.mjs"] +memories: [pitfalls/cancel-now-after-grace-timer, architecture/browser-server-contract, architecture/package-and-skill-layout, decisions/backlog-planning-and-feature-waves, 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-17T16:19:19.010Z" +tags: [] +--- + +# Implementation notes + +Separate view/tab navigation from pending-round cancellation in the persistent session server. Ensure backlog writes and idle refreshes preserve the active review document and its run/result ownership; cover the Planning → Work return path and verify unrelated submissions remain guarded while the review is pending. + +# Snippets + +```js +const settle = (result) => { if (!pending) return false; /* resolve the active round once */ };\n\nshowStep({ step, render, signal }) {\n settle({ type: 'cancel' }); // only a new round supersedes a pending one\n activeTab = tabFor(step);\n} +``` + +# Blast radius + +The session shell's one-pending-round lifecycle, tab switching, backlog CRUD, and every interactive workflow that uses the persistent dashboard. diff --git a/memory/features/show-active-work-in-work.md b/memory/features/show-active-work-in-work.md new file mode 100644 index 0000000..6ac64b9 --- /dev/null +++ b/memory/features/show-active-work-in-work.md @@ -0,0 +1,26 @@ +--- +type: Feature +title: Show active plan context on Work +description: Make Work the home for the active plan, its feature set, and the dependency graph. +status: pending +size: medium +depends_on: [] +files: ["lib/views/hub.mjs", "lib/views/planning.mjs", "lib/views/graph.mjs", "test/ui.test.mjs"] +memories: [pitfalls/client-js-template-literal-escaping, architecture/workflow-state-ownership, patterns/safe-browser-rendering, decisions/backlog-planning-and-feature-waves, 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-17T16:17:17.903Z" +tags: [] +--- + +# Implementation notes + +Relocate active-plan feature/graph presentation from Planning to Work without reimplementing lifecycle logic in either view. Continue rendering the shared gather snapshot and keep Planning focused on backlog and plan-management controls; retain responsive graph usability and update UI coverage. + +# Snippets + +```js +const CH = D.features || [];\n// Readiness and plan stage arrive precomputed in the gather payload.\n\nrenderGraphInto(g, cw, CH, 'fix depends-on in /iterator-feature before implementing.'); +``` + +# Blast radius + +The Planning and Work dashboard surfaces, including all feature-action controls and the graph's server-derived readiness display. diff --git a/memory/features/streamline-review-interface.md b/memory/features/streamline-review-interface.md new file mode 100644 index 0000000..3800ac6 --- /dev/null +++ b/memory/features/streamline-review-interface.md @@ -0,0 +1,30 @@ +--- +type: Feature +title: Keep review controls fully readable +description: Show complete feature titles in review and remove the unused Feedback panel. +status: pending +size: medium +depends_on: [] +files: ["lib/views/review.mjs", "test/ui.test.mjs", "test/client-js-parse.test.mjs"] +memories: [pitfalls/client-js-template-literal-escaping, patterns/safe-browser-rendering, decisions/backlog-planning-and-feature-waves, decisions/iterator-dashboard-feature-workflow, decisions/parallel-feature-waves-and-consolidated-review] +timestamp: "2026-07-17T16:17:17.904Z" +tags: [] +--- + +# Implementation notes + +Replace sidebar title truncation with responsive wrapping or overflow that preserves every title, including long feature names. Remove the fixed lower-right Feedback panel and its client-side bookkeeping while preserving feature status, notes, line comments, and header submission actions. Extend UI and inline-script parse coverage. + +# Snippets + +```css +.fn{font-size:13px;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}\n.fb{position:fixed;bottom:0;right:0;width:400px;...} +``` + +```html +
\n
\n
+``` + +# Blast radius + +The review sidebar and narrow-screen review layout; reviewer feedback submission must continue to use the existing header controls and collected feature/line comments. diff --git a/memory/index.md b/memory/index.md index 8f129b2..b8d16f2 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) - Preserve active reviews across Planning navigation, move active work context to Work, and polish 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 cf13f0c..2b940e2 100644 --- a/memory/log.md +++ b/memory/log.md @@ -1,6 +1,21 @@ # iterator update log ## 2026-07-17 +* **Implementation**: Committed feature(preserve-review-across-planning) on branch iterator/always-available-backlog; awaiting review. +* **Update**: Applied 3 feature adjustment(s). +* **Creation**: 3 feature(s) written. +* **Creation**: Plan "Keep active review stable and clarify Planning versus Work" approved on branch iterator/always-available-backlog. Consumed 4 selected backlog candidate(s). +* **Backlog**: select remove-right-lower-feedback-button (selected). +* **Backlog**: select feature-review-titles-are-cut-off (selected). +* **Backlog**: select move-active-plan-and-features-to-work (selected). +* **Backlog**: select review-aborded-after-going-to-planning-during-review (selected). +* **Backlog**: create remove-right-lower-feedback-button. +* **Backlog**: create feature-review-titles-are-cut-off. +* **Backlog**: create move-active-plan-and-features-to-work. +* **Retirement**: Plan "Keep backlog planning available and support parallel feature waves" condensed into [Backlog planning and parallel feature waves](/decisions/backlog-planning-and-feature-waves.md). +* **Backlog**: select review-aborded-after-going-to-planning-during-review (deselected). +* **Backlog**: select review-aborded-after-going-to-planning-during-review (selected). +* **Backlog**: create review-aborded-after-going-to-planning-during-review. * **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)). diff --git a/memory/plan.md b/memory/plan.md index b6798f9..a344d28 100644 --- a/memory/plan.md +++ b/memory/plan.md @@ -1,25 +1,23 @@ --- type: Plan -title: Keep backlog planning available and support parallel feature waves -description: Keep filesystem-backed backlog work available during active agent flows, and add dependency-ready implementation waves with consolidated review. +title: Keep active review stable and clarify Planning versus Work +description: Preserve active reviews across Planning navigation, move active work context to Work, and polish review controls. status: approved -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 +branch: iterator/always-available-backlog created: 2026-07-17 -timestamp: "2026-07-17T15:28:32.570Z" -plan_reviewed: 2026-07-17 +timestamp: 2026-07-17T16:15:52.935Z --- # Goal -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. +Prevent an in-progress review from being aborted when users visit Planning to manage backlog ideas, while making Work the clear home for the active plan, its features, and their dependency graph. Fix long feature-review titles so they remain fully readable, and remove the unused lower-right Feedback control. # Architecture -- 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. +- Extend `architecture/browser-server-contract` and the persistent session shell so navigation and filesystem-only backlog work do not destroy an existing pending review round; its eventual result remains the sole stdout outcome. +- Extend `architecture/workflow-state-ownership`: gather supplies active-plan, feature, and dependency-graph data to Work, while Planning remains focused on backlog and plan-management surfaces rather than reconstructing workflow state locally. +- Update the dashboard views and extension through their established contracts; preserve the compact responsive rules in `memory/design.md`, including wrapping or horizontal overflow instead of clipping titles. +- Update canonical `lib/` sources and run `npm run sync` so shipped skill copies remain aligned, with session/UI/client-script regression coverage. # Dependencies @@ -27,26 +25,16 @@ Allow users to continue reading and editing the filesystem-backed idea backlog w # Key decisions -- 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. +- Follow `decisions/backlog-planning-and-feature-waves`: backlog CRUD may remain available during an active model round, but it must preserve the active review's pending state and must not unblock unrelated model-flow actions. +- Follow `architecture/browser-server-contract`: a review round continues to own its one machine-readable result even when the user navigates to Planning and returns; navigation is not a cancellation signal. +- Keep all active-plan lifecycle data and graph state server-derived per `architecture/workflow-state-ownership`; views only relocate and render the supplied data. +- Follow `decisions/consume-accepted-backlog-ideas`: consume these four selected backlog candidates only when this plan is approved. +- Long review feature titles must remain fully accessible at every breakpoint; prioritize wrapping or overflow over ellipsis/clipping, consistent with `memory/design.md`. +- Remove the unused Feedback control and its obsolete client wiring without adding a replacement feedback path in this scope. +- Follow `pitfalls/client-js-template-literal-escaping` for any changed inline view scripts, and cover their parseability in client-script tests. # Features -* [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)_ - -## Clean bill - -- **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. +* [Preserve reviews across Planning navigation](/features/preserve-review-across-planning.md) - Keep an active review open while users manage backlog items on the Planning tab and return to it. +* [Show active plan context on Work](/features/show-active-work-in-work.md) - Make Work the home for the active plan, its feature set, and the dependency graph. +* [Keep review controls fully readable](/features/streamline-review-interface.md) - Show complete feature titles in review and remove the unused Feedback panel. diff --git a/memory/state.md b/memory/state.md index 78147f2..ba68aab 100644 --- a/memory/state.md +++ b/memory/state.md @@ -4,11 +4,11 @@ title: Runtime state description: Machine-owned iterator flow state — never hand-edited. mode: auto paused: false -phase: done -active_feature: null +phase: implementing +active_feature: preserve-review-across-planning strikes: "{\"implement-ready-feature-wave\":2,\"always-available-backlog\":1,\"review-multiple-implemented-features\":1}" escalation: null -timestamp: 2026-07-17T15:28:48.345Z +timestamp: 2026-07-17T16:17:22.939Z --- Runtime flow state; read via gather, written only by the state op. diff --git a/memory/usage.md b/memory/usage.md index cc31f83..847a194 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\":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 +totals: "{\"steps\":{\"hub\":{\"openai-codex/gpt-5.6-terra\":{\"input\":16969,\"output\":1048,\"cacheRead\":53248,\"cacheWrite\":0,\"turns\":5}},\"plan\":{\"openai-codex/gpt-5.6-terra\":{\"input\":61028,\"output\":5028,\"cacheRead\":668672,\"cacheWrite\":0,\"turns\":17}}},\"features\":{\"retire-plan\":{\"input\":16969,\"output\":1048,\"cacheRead\":53248,\"cacheWrite\":0,\"turns\":5}}}" +timestamp: 2026-07-17T16:17:22.753Z --- # Usage @@ -12,34 +12,18 @@ timestamp: 2026-07-17T15:28:48.101Z | model | input | output | cache read | cache write | turns | | --- | ---: | ---: | ---: | ---: | ---: | -| openai-codex/gpt-5.6-terra | 342718 | 807 | 2367488 | 0 | 8 | +| openai-codex/gpt-5.6-terra | 16969 | 1048 | 53248 | 0 | 5 | ## plan | model | input | output | cache read | cache write | turns | | --- | ---: | ---: | ---: | ---: | ---: | -| 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 | 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 | 939632 | 7482 | 9461248 | 0 | 56 | +| openai-codex/gpt-5.6-terra | 61028 | 5028 | 668672 | 0 | 17 | ## Per feature | feature | input | output | cache read | cache write | turns | | --- | ---: | ---: | ---: | ---: | ---: | -| 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 | +| retire-plan | 16969 | 1048 | 53248 | 0 | 5 | -Total: 2180728 in / 29632 out / 23753216 cache-read / 0 cache-write over 171 turns. +Total: 77997 in / 6076 out / 721920 cache-read / 0 cache-write over 22 turns. diff --git a/skills/iterator/lib/ui.mjs b/skills/iterator/lib/ui.mjs index 77a935e..000e29b 100644 --- a/skills/iterator/lib/ui.mjs +++ b/skills/iterator/lib/ui.mjs @@ -156,7 +156,8 @@ const SHARED_JS = ` // server ignore /cancel-/submit from tabs that belong to an earlier round. function __q(path){ return path + (path.indexOf('?')>=0 ? '&' : '?') + 'r=' + __RUN; } let __submitted = false; -function sendCancel(){ if(__submitted) return; __submitted = true; +let __navigating = false; +function sendCancel(){ if(__submitted || __navigating) return; __submitted = true; try{ navigator.sendBeacon(__q('/cancel'),'{}'); }catch(e){} } window.addEventListener('pagehide', sendCancel); async function cancelFlow(){ __submitted = true; @@ -171,7 +172,9 @@ function esc(s){ return String(s==null?'':s) // { iterator:'working', working:bool } into this iframe — non-Work tabs stay // viewable but their mutating controls go inert (see BASE_CSS .iterator-ro). window.addEventListener('message', function(e){ - if(e.source !== window.parent || !e.data || e.data.iterator !== 'working') return; + if(e.source !== window.parent || !e.data) return; + if(e.data.iterator === 'navigate') { __navigating = true; return; } + if(e.data.iterator !== 'working') return; document.body.classList.toggle('iterator-ro', !!e.data.working); }); diff --git a/test/session-server.test.mjs b/test/session-server.test.mjs index bcaf7dc..04833a2 100644 --- a/test/session-server.test.mjs +++ b/test/session-server.test.mjs @@ -342,6 +342,10 @@ test("the shell scopes the overlay to the Work tab and posts read-only state int shell.includes("tab === 'work' && working"), "overlay guarded by active tab", ); + assert.ok( + shell.includes("postMessage({ iterator: 'navigate'"), + "tab switches mark the outgoing iframe as navigation before pagehide", + ); assert.ok( shell.includes("postMessage({ iterator: 'working'"), "read-only state posted into the iframe", diff --git a/test/ui.test.mjs b/test/ui.test.mjs index 16f43d8..1c7d879 100644 --- a/test/ui.test.mjs +++ b/test/ui.test.mjs @@ -106,6 +106,10 @@ test("shared client JS posts to the server endpoints with the run id", () => { test("shared client JS wires read-only mode while the agent works", () => { const html = renderPage({ step: "t", data: {}, body: "", clientJs: "" }); + assert.ok( + html.includes("e.data.iterator === 'navigate'"), + "suppresses pagehide cancellation during shell tab navigation", + ); assert.ok( html.includes("e.data.iterator !== 'working'"), "listens for the shell working message", From 47646dec33a5152cdcd389b23c10214b8f6ca179 Mon Sep 17 00:00:00 2001 From: Christoph Maier Date: Fri, 17 Jul 2026 18:19:19 +0200 Subject: [PATCH 02/22] chore(iterator): record feature commit --- memory/features/preserve-review-across-planning.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/memory/features/preserve-review-across-planning.md b/memory/features/preserve-review-across-planning.md index 3cdf0d9..784cab6 100644 --- a/memory/features/preserve-review-across-planning.md +++ b/memory/features/preserve-review-across-planning.md @@ -7,8 +7,12 @@ size: medium depends_on: [] files: ["lib/session-server.mjs", "extensions/iterator.js", "test/session-server.test.mjs", "test/nonblocking-working-overlay.test.mjs"] memories: [pitfalls/cancel-now-after-grace-timer, architecture/browser-server-contract, architecture/package-and-skill-layout, decisions/backlog-planning-and-feature-waves, 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-17T16:19:19.010Z" +timestamp: "2026-07-17T16:19:19.091Z" tags: [] +commits: + - sha: 30c6a6b70a54d59a72a79ba2f864a9b993006262 + kind: implement + date: 2026-07-17 --- # Implementation notes From dd02899ab64b86edef1ab064bd6a3e156eb5a222 Mon Sep 17 00:00:00 2001 From: Christoph Maier Date: Fri, 17 Jul 2026 18:22:39 +0200 Subject: [PATCH 03/22] feature(preserve-review-across-planning): make review navigation cancellation deterministic Feature: preserve-review-across-planning --- lib/session-server.mjs | 14 ++-- lib/ui.mjs | 10 +-- .../preserve-review-across-planning.md | 8 +- memory/log.md | 2 + memory/state.md | 4 +- memory/usage.md | 19 ++++- skills/iterator/lib/ui.mjs | 10 +-- test/session-server.test.mjs | 76 ++++++++++++++++++- test/ui.test.mjs | 4 +- 9 files changed, 122 insertions(+), 25 deletions(-) diff --git a/lib/session-server.mjs b/lib/session-server.mjs index 68757f0..9d07161 100644 --- a/lib/session-server.mjs +++ b/lib/session-server.mjs @@ -217,10 +217,6 @@ function applyWorking() { overlay.style.display = 'flex'; } function setTab(t, v) { - // A tab switch replaces the iframe, which fires pagehide in an active - // review. Tell that document this is in-shell navigation before replacing - // it so its cancel beacon cannot settle the pending review round. - try { frame.contentWindow.postMessage({ iterator: 'navigate' }, '*'); } catch (e) {} tab = t; document.getElementById('context').textContent = './' + tab; document.querySelectorAll('#tabs button.tab').forEach(b => b.classList.toggle('sel', b.dataset.tab === t)); @@ -296,6 +292,12 @@ document.getElementById('ov-pause').addEventListener('click', () => control(paus document.getElementById('ov-abort').addEventListener('click', () => control('abort')); es.onopen = () => { conn.style.display = 'none'; }; es.onerror = () => { conn.style.display = 'block'; }; +// Embedded views never own pagehide cancellation: switching tabs replaces the +// iframe. The persistent shell owns cancellation and beacons only when the +// whole dashboard unloads; reload grace on the server preserves normal reloads. +window.addEventListener('pagehide', () => { + try { navigator.sendBeacon('/cancel', '{}'); } catch (e) {} +}); setTab(tab, 0); @@ -501,11 +503,13 @@ export function createSessionServer({ onUnsolicited, onControl, log } = {}) { res.writeHead(204); res.end(); if (r && r !== RUN_ID) return; // an outgoing iframe's pagehide beacon - if (!pending || cancelTimer) return; + if (!pending) return; if (url.searchParams.get("now") === "1") { + clearCancelGrace(); settle({ type: "cancel" }); return; } + if (cancelTimer) return; // Held: a reload (GET / or /view) within the grace window keeps the // round alive; a really-closed tab lets the timer fire. cancelTimer = setTimeout(() => { diff --git a/lib/ui.mjs b/lib/ui.mjs index 000e29b..c4eac2a 100644 --- a/lib/ui.mjs +++ b/lib/ui.mjs @@ -156,8 +156,10 @@ const SHARED_JS = ` // server ignore /cancel-/submit from tabs that belong to an earlier round. function __q(path){ return path + (path.indexOf('?')>=0 ? '&' : '?') + 'r=' + __RUN; } let __submitted = false; -let __navigating = false; -function sendCancel(){ if(__submitted || __navigating) return; __submitted = true; +function sendCancel(){ + // In Pi's persistent session, the parent shell owns unload cancellation. + // An embedded view unloads on every tab switch and must never end the round. + if(__submitted || window.parent !== window) return; __submitted = true; try{ navigator.sendBeacon(__q('/cancel'),'{}'); }catch(e){} } window.addEventListener('pagehide', sendCancel); async function cancelFlow(){ __submitted = true; @@ -172,9 +174,7 @@ function esc(s){ return String(s==null?'':s) // { iterator:'working', working:bool } into this iframe — non-Work tabs stay // viewable but their mutating controls go inert (see BASE_CSS .iterator-ro). window.addEventListener('message', function(e){ - if(e.source !== window.parent || !e.data) return; - if(e.data.iterator === 'navigate') { __navigating = true; return; } - if(e.data.iterator !== 'working') return; + if(e.source !== window.parent || !e.data || e.data.iterator !== 'working') return; document.body.classList.toggle('iterator-ro', !!e.data.working); }); diff --git a/memory/features/preserve-review-across-planning.md b/memory/features/preserve-review-across-planning.md index 784cab6..11a1d0d 100644 --- a/memory/features/preserve-review-across-planning.md +++ b/memory/features/preserve-review-across-planning.md @@ -7,12 +7,13 @@ size: medium depends_on: [] files: ["lib/session-server.mjs", "extensions/iterator.js", "test/session-server.test.mjs", "test/nonblocking-working-overlay.test.mjs"] memories: [pitfalls/cancel-now-after-grace-timer, architecture/browser-server-contract, architecture/package-and-skill-layout, decisions/backlog-planning-and-feature-waves, 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-17T16:19:19.091Z" +timestamp: "2026-07-17T16:22:39.656Z" tags: [] commits: - sha: 30c6a6b70a54d59a72a79ba2f864a9b993006262 kind: implement date: 2026-07-17 +reviewed: 2026-07-17 --- # Implementation notes @@ -28,3 +29,8 @@ const settle = (result) => { if (!pending) return false; /* resolve the active r # Blast radius The session shell's one-pending-round lifecycle, tab switching, backlog CRUD, and every interactive workflow that uses the persistent dashboard. + +# Review + +## 2026-07-17 +* **Needs changes** _(agent review: openai-codex/gpt-5.6-sol)_ — The fix is race-prone and the tests do not exercise the behavior. `setTab()` queues a `postMessage({iterator:'navigate'})` and immediately replaces `frame.src`; delivery of that queued message is not guaranteed before the outgoing iframe's `pagehide`, so `sendCancel()` can still beacon `/cancel` and abort the pending review. Make cancellation ownership deterministic (for example, suppress iframe pagehide beacons in the persistent session and let the parent shell beacon `/cancel` when the whole dashboard unloads), then add a behavioral regression that performs review → Planning → Work and proves the original review round remains pending and can still submit. diff --git a/memory/log.md b/memory/log.md index 2b940e2..fc7c265 100644 --- a/memory/log.md +++ b/memory/log.md @@ -2,6 +2,8 @@ ## 2026-07-17 * **Implementation**: Committed feature(preserve-review-across-planning) on branch iterator/always-available-backlog; awaiting review. +* **Review**: Reviewed [Preserve reviews across Planning navigation](/features/preserve-review-across-planning.md); changes (agent). +* **Implementation**: Committed feature(preserve-review-across-planning) on branch iterator/always-available-backlog; awaiting review. * **Update**: Applied 3 feature adjustment(s). * **Creation**: 3 feature(s) written. * **Creation**: Plan "Keep active review stable and clarify Planning versus Work" approved on branch iterator/always-available-backlog. Consumed 4 selected backlog candidate(s). diff --git a/memory/state.md b/memory/state.md index ba68aab..9503d9d 100644 --- a/memory/state.md +++ b/memory/state.md @@ -6,9 +6,9 @@ mode: auto paused: false phase: implementing active_feature: preserve-review-across-planning -strikes: "{\"implement-ready-feature-wave\":2,\"always-available-backlog\":1,\"review-multiple-implemented-features\":1}" +strikes: "{\"implement-ready-feature-wave\":2,\"always-available-backlog\":1,\"review-multiple-implemented-features\":1,\"preserve-review-across-planning\":1}" escalation: null -timestamp: 2026-07-17T16:17:22.939Z +timestamp: 2026-07-17T16:20:14.771Z --- Runtime flow state; read via gather, written only by the state op. diff --git a/memory/usage.md b/memory/usage.md index 847a194..f1ea254 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\":16969,\"output\":1048,\"cacheRead\":53248,\"cacheWrite\":0,\"turns\":5}},\"plan\":{\"openai-codex/gpt-5.6-terra\":{\"input\":61028,\"output\":5028,\"cacheRead\":668672,\"cacheWrite\":0,\"turns\":17}}},\"features\":{\"retire-plan\":{\"input\":16969,\"output\":1048,\"cacheRead\":53248,\"cacheWrite\":0,\"turns\":5}}}" -timestamp: 2026-07-17T16:17:22.753Z +totals: "{\"steps\":{\"hub\":{\"openai-codex/gpt-5.6-terra\":{\"input\":16969,\"output\":1048,\"cacheRead\":53248,\"cacheWrite\":0,\"turns\":5}},\"plan\":{\"openai-codex/gpt-5.6-terra\":{\"input\":61028,\"output\":5028,\"cacheRead\":668672,\"cacheWrite\":0,\"turns\":17}},\"implement\":{\"openai-codex/gpt-5.6-terra\":{\"input\":119901,\"output\":3970,\"cacheRead\":1700864,\"cacheWrite\":0,\"turns\":19}},\"review\":{\"openai-codex/gpt-5.6-sol\":{\"input\":115474,\"output\":925,\"cacheRead\":333312,\"cacheWrite\":0,\"turns\":4}}},\"features\":{\"retire-plan\":{\"input\":16969,\"output\":1048,\"cacheRead\":53248,\"cacheWrite\":0,\"turns\":5},\"preserve-review-across-planning\":{\"input\":235375,\"output\":4895,\"cacheRead\":2034176,\"cacheWrite\":0,\"turns\":23}}}" +timestamp: 2026-07-17T16:20:14.502Z --- # Usage @@ -20,10 +20,23 @@ timestamp: 2026-07-17T16:17:22.753Z | --- | ---: | ---: | ---: | ---: | ---: | | openai-codex/gpt-5.6-terra | 61028 | 5028 | 668672 | 0 | 17 | +## implement + +| model | input | output | cache read | cache write | turns | +| --- | ---: | ---: | ---: | ---: | ---: | +| openai-codex/gpt-5.6-terra | 119901 | 3970 | 1700864 | 0 | 19 | + +## review + +| model | input | output | cache read | cache write | turns | +| --- | ---: | ---: | ---: | ---: | ---: | +| openai-codex/gpt-5.6-sol | 115474 | 925 | 333312 | 0 | 4 | + ## Per feature | feature | input | output | cache read | cache write | turns | | --- | ---: | ---: | ---: | ---: | ---: | | retire-plan | 16969 | 1048 | 53248 | 0 | 5 | +| preserve-review-across-planning | 235375 | 4895 | 2034176 | 0 | 23 | -Total: 77997 in / 6076 out / 721920 cache-read / 0 cache-write over 22 turns. +Total: 313372 in / 10971 out / 2756096 cache-read / 0 cache-write over 45 turns. diff --git a/skills/iterator/lib/ui.mjs b/skills/iterator/lib/ui.mjs index 000e29b..c4eac2a 100644 --- a/skills/iterator/lib/ui.mjs +++ b/skills/iterator/lib/ui.mjs @@ -156,8 +156,10 @@ const SHARED_JS = ` // server ignore /cancel-/submit from tabs that belong to an earlier round. function __q(path){ return path + (path.indexOf('?')>=0 ? '&' : '?') + 'r=' + __RUN; } let __submitted = false; -let __navigating = false; -function sendCancel(){ if(__submitted || __navigating) return; __submitted = true; +function sendCancel(){ + // In Pi's persistent session, the parent shell owns unload cancellation. + // An embedded view unloads on every tab switch and must never end the round. + if(__submitted || window.parent !== window) return; __submitted = true; try{ navigator.sendBeacon(__q('/cancel'),'{}'); }catch(e){} } window.addEventListener('pagehide', sendCancel); async function cancelFlow(){ __submitted = true; @@ -172,9 +174,7 @@ function esc(s){ return String(s==null?'':s) // { iterator:'working', working:bool } into this iframe — non-Work tabs stay // viewable but their mutating controls go inert (see BASE_CSS .iterator-ro). window.addEventListener('message', function(e){ - if(e.source !== window.parent || !e.data) return; - if(e.data.iterator === 'navigate') { __navigating = true; return; } - if(e.data.iterator !== 'working') return; + if(e.source !== window.parent || !e.data || e.data.iterator !== 'working') return; document.body.classList.toggle('iterator-ro', !!e.data.working); }); diff --git a/test/session-server.test.mjs b/test/session-server.test.mjs index 04833a2..0024cc9 100644 --- a/test/session-server.test.mjs +++ b/test/session-server.test.mjs @@ -343,8 +343,8 @@ test("the shell scopes the overlay to the Work tab and posts read-only state int "overlay guarded by active tab", ); assert.ok( - shell.includes("postMessage({ iterator: 'navigate'"), - "tab switches mark the outgoing iframe as navigation before pagehide", + shell.includes("navigator.sendBeacon('/cancel'"), + "the persistent shell owns cancellation when the dashboard unloads", ); assert.ok( shell.includes("postMessage({ iterator: 'working'"), @@ -424,6 +424,33 @@ test("cancel grace: a reload (GET /view) keeps the round; ?now=1 cancels immedia } }); +test("explicit cancel pre-empts a pending pagehide grace timer", async () => { + const { session, origin } = await startSession(); + try { + const round = session.showStep({ + step: "review", + render: () => viewHtml("REVIEW"), + }); + await fetch(`${origin}/cancel?r=${srvMod.RUN_ID}`, { + method: "POST", + body: "{}", + }); + await fetch(`${origin}/cancel?r=${srvMod.RUN_ID}&now=1`, { + method: "POST", + body: "{}", + }); + assert.deepEqual( + await Promise.race([ + round, + sleep(100).then(() => ({ type: "too-slow" })), + ]), + { type: "cancel" }, + ); + } finally { + await session.stop(); + } +}); + test("an aborted signal resolves the round as cancel", async () => { const { session } = await startSession(); try { @@ -512,6 +539,51 @@ test("tabs: steps render into their tab; inactive-tab refreshes are stored silen } }); +test("review survives Planning navigation and can submit after returning to Work", async () => { + const { session, origin } = await startSession(); + try { + // Seed the inactive Planning tab, then open an interactive review round + // on Work. Switching the shell's iframe between them is navigation only. + session.showView({ step: "planning", render: () => viewHtml("PLANNING") }); + const round = session.showStep({ + step: "review", + render: () => viewHtml("ACTIVE-REVIEW"), + }); + const reviewRun = srvMod.RUN_ID; + + await fetch(origin + "/tab", { + method: "POST", + body: '{"tab":"planning"}', + }); + assert.match( + await (await fetch(origin + "/view?tab=planning")).text(), + /PLANNING/, + ); + await fetch(origin + "/tab", { + method: "POST", + body: '{"tab":"work"}', + }); + assert.match( + await (await fetch(origin + "/view?tab=work")).text(), + /ACTIVE-REVIEW/, + ); + assert.equal(srvMod.RUN_ID, reviewRun, "navigation preserves round identity"); + assert.equal(session.hasPending(), true, "review remains pending"); + + const response = await fetch(`${origin}/submit?r=${reviewRun}`, { + method: "POST", + body: '{"type":"review-feedback","features":[]}', + }); + assert.equal(response.status, 200); + assert.deepEqual(await round, { + type: "review-feedback", + features: [], + }); + } finally { + await session.stop(); + } +}); + test("an idle submit from a stale-run view still dispatches as unsolicited", async () => { let unsolicited = null; const { session, origin } = await startSession({ diff --git a/test/ui.test.mjs b/test/ui.test.mjs index 1c7d879..0d9cd99 100644 --- a/test/ui.test.mjs +++ b/test/ui.test.mjs @@ -107,8 +107,8 @@ test("shared client JS posts to the server endpoints with the run id", () => { test("shared client JS wires read-only mode while the agent works", () => { const html = renderPage({ step: "t", data: {}, body: "", clientJs: "" }); assert.ok( - html.includes("e.data.iterator === 'navigate'"), - "suppresses pagehide cancellation during shell tab navigation", + html.includes("window.parent !== window"), + "embedded session views never own pagehide cancellation", ); assert.ok( html.includes("e.data.iterator !== 'working'"), From 4d4477e94c49032bb90a4ae0563aed5f179e1aec Mon Sep 17 00:00:00 2001 From: Christoph Maier Date: Fri, 17 Jul 2026 18:22:39 +0200 Subject: [PATCH 04/22] chore(iterator): record feature commit --- memory/features/preserve-review-across-planning.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/memory/features/preserve-review-across-planning.md b/memory/features/preserve-review-across-planning.md index 11a1d0d..69c120c 100644 --- a/memory/features/preserve-review-across-planning.md +++ b/memory/features/preserve-review-across-planning.md @@ -7,12 +7,15 @@ size: medium depends_on: [] files: ["lib/session-server.mjs", "extensions/iterator.js", "test/session-server.test.mjs", "test/nonblocking-working-overlay.test.mjs"] memories: [pitfalls/cancel-now-after-grace-timer, architecture/browser-server-contract, architecture/package-and-skill-layout, decisions/backlog-planning-and-feature-waves, 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-17T16:22:39.656Z" +timestamp: "2026-07-17T16:22:39.727Z" tags: [] commits: - sha: 30c6a6b70a54d59a72a79ba2f864a9b993006262 kind: implement date: 2026-07-17 + - sha: dd02899ab64b86edef1ab064bd6a3e156eb5a222 + kind: implement + date: 2026-07-17 reviewed: 2026-07-17 --- From 43d0fcb540ff56ac59d6f347443a6e3e7487c0fc Mon Sep 17 00:00:00 2001 From: Christoph Maier Date: Fri, 17 Jul 2026 18:23:12 +0200 Subject: [PATCH 05/22] chore(iterator): record feature commits and memory updates --- memory/features/index.md | 2 +- memory/features/preserve-review-across-planning.md | 5 +++-- memory/log.md | 1 + memory/state.md | 4 ++-- memory/usage.md | 9 +++++---- 5 files changed, 12 insertions(+), 9 deletions(-) diff --git a/memory/features/index.md b/memory/features/index.md index 5a2f2b5..99924f5 100644 --- a/memory/features/index.md +++ b/memory/features/index.md @@ -1,5 +1,5 @@ # Features -* [Preserve reviews across Planning navigation](preserve-review-across-planning.md) - ⬜ pending · medium · Keep an active review open while users manage backlog items on the Planning tab and return to it. +* [Preserve reviews across Planning navigation](preserve-review-across-planning.md) - ✅ done · medium · Keep an active review open while users manage backlog items on the Planning tab and return to it. * [Show active plan context on Work](show-active-work-in-work.md) - ⬜ pending · medium · Make Work the home for the active plan, its feature set, and the dependency graph. * [Keep review controls fully readable](streamline-review-interface.md) - ⬜ pending · medium · Show complete feature titles in review and remove the unused Feedback panel. diff --git a/memory/features/preserve-review-across-planning.md b/memory/features/preserve-review-across-planning.md index 69c120c..0a82213 100644 --- a/memory/features/preserve-review-across-planning.md +++ b/memory/features/preserve-review-across-planning.md @@ -2,12 +2,12 @@ type: Feature title: Preserve reviews across Planning navigation description: Keep an active review open while users manage backlog items on the Planning tab and return to it. -status: implemented +status: done size: medium depends_on: [] files: ["lib/session-server.mjs", "extensions/iterator.js", "test/session-server.test.mjs", "test/nonblocking-working-overlay.test.mjs"] memories: [pitfalls/cancel-now-after-grace-timer, architecture/browser-server-contract, architecture/package-and-skill-layout, decisions/backlog-planning-and-feature-waves, 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-17T16:22:39.727Z" +timestamp: "2026-07-17T16:23:12.103Z" tags: [] commits: - sha: 30c6a6b70a54d59a72a79ba2f864a9b993006262 @@ -17,6 +17,7 @@ commits: kind: implement date: 2026-07-17 reviewed: 2026-07-17 +done: 2026-07-17 --- # Implementation notes diff --git a/memory/log.md b/memory/log.md index fc7c265..527dce5 100644 --- a/memory/log.md +++ b/memory/log.md @@ -1,6 +1,7 @@ # iterator update log ## 2026-07-17 +* **Review**: Accepted [Preserve reviews across Planning navigation](/features/preserve-review-across-planning.md) (committed as feature(preserve-review-across-planning)). * **Implementation**: Committed feature(preserve-review-across-planning) on branch iterator/always-available-backlog; awaiting review. * **Review**: Reviewed [Preserve reviews across Planning navigation](/features/preserve-review-across-planning.md); changes (agent). * **Implementation**: Committed feature(preserve-review-across-planning) on branch iterator/always-available-backlog; awaiting review. diff --git a/memory/state.md b/memory/state.md index 9503d9d..c032b49 100644 --- a/memory/state.md +++ b/memory/state.md @@ -4,11 +4,11 @@ title: Runtime state description: Machine-owned iterator flow state — never hand-edited. mode: auto paused: false -phase: implementing +phase: reviewing active_feature: preserve-review-across-planning strikes: "{\"implement-ready-feature-wave\":2,\"always-available-backlog\":1,\"review-multiple-implemented-features\":1,\"preserve-review-across-planning\":1}" escalation: null -timestamp: 2026-07-17T16:20:14.771Z +timestamp: 2026-07-17T16:22:50.662Z --- Runtime flow state; read via gather, written only by the state op. diff --git a/memory/usage.md b/memory/usage.md index f1ea254..4f93b21 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\":16969,\"output\":1048,\"cacheRead\":53248,\"cacheWrite\":0,\"turns\":5}},\"plan\":{\"openai-codex/gpt-5.6-terra\":{\"input\":61028,\"output\":5028,\"cacheRead\":668672,\"cacheWrite\":0,\"turns\":17}},\"implement\":{\"openai-codex/gpt-5.6-terra\":{\"input\":119901,\"output\":3970,\"cacheRead\":1700864,\"cacheWrite\":0,\"turns\":19}},\"review\":{\"openai-codex/gpt-5.6-sol\":{\"input\":115474,\"output\":925,\"cacheRead\":333312,\"cacheWrite\":0,\"turns\":4}}},\"features\":{\"retire-plan\":{\"input\":16969,\"output\":1048,\"cacheRead\":53248,\"cacheWrite\":0,\"turns\":5},\"preserve-review-across-planning\":{\"input\":235375,\"output\":4895,\"cacheRead\":2034176,\"cacheWrite\":0,\"turns\":23}}}" -timestamp: 2026-07-17T16:20:14.502Z +totals: "{\"steps\":{\"hub\":{\"openai-codex/gpt-5.6-terra\":{\"input\":16969,\"output\":1048,\"cacheRead\":53248,\"cacheWrite\":0,\"turns\":5}},\"plan\":{\"openai-codex/gpt-5.6-terra\":{\"input\":61028,\"output\":5028,\"cacheRead\":668672,\"cacheWrite\":0,\"turns\":17}},\"implement\":{\"openai-codex/gpt-5.6-terra\":{\"input\":119901,\"output\":3970,\"cacheRead\":1700864,\"cacheWrite\":0,\"turns\":19},\"openai-codex/gpt-5.6-sol\":{\"input\":147159,\"output\":3764,\"cacheRead\":2008064,\"cacheWrite\":0,\"turns\":17}},\"review\":{\"openai-codex/gpt-5.6-sol\":{\"input\":115474,\"output\":925,\"cacheRead\":333312,\"cacheWrite\":0,\"turns\":4}}},\"features\":{\"retire-plan\":{\"input\":16969,\"output\":1048,\"cacheRead\":53248,\"cacheWrite\":0,\"turns\":5},\"preserve-review-across-planning\":{\"input\":382534,\"output\":8659,\"cacheRead\":4042240,\"cacheWrite\":0,\"turns\":40}}}" +timestamp: 2026-07-17T16:22:50.432Z --- # Usage @@ -25,6 +25,7 @@ timestamp: 2026-07-17T16:20:14.502Z | model | input | output | cache read | cache write | turns | | --- | ---: | ---: | ---: | ---: | ---: | | openai-codex/gpt-5.6-terra | 119901 | 3970 | 1700864 | 0 | 19 | +| openai-codex/gpt-5.6-sol | 147159 | 3764 | 2008064 | 0 | 17 | ## review @@ -37,6 +38,6 @@ timestamp: 2026-07-17T16:20:14.502Z | feature | input | output | cache read | cache write | turns | | --- | ---: | ---: | ---: | ---: | ---: | | retire-plan | 16969 | 1048 | 53248 | 0 | 5 | -| preserve-review-across-planning | 235375 | 4895 | 2034176 | 0 | 23 | +| preserve-review-across-planning | 382534 | 8659 | 4042240 | 0 | 40 | -Total: 313372 in / 10971 out / 2756096 cache-read / 0 cache-write over 45 turns. +Total: 460531 in / 14735 out / 4764160 cache-read / 0 cache-write over 62 turns. From e0b0453cbc024caa1cfdcbe079767214f4d8c6d8 Mon Sep 17 00:00:00 2001 From: Christoph Maier Date: Fri, 17 Jul 2026 18:25:55 +0200 Subject: [PATCH 06/22] feature(show-active-work-in-work): move active feature context to Work Feature: show-active-work-in-work --- lib/views/hub.mjs | 33 +++++++--- lib/views/planning.mjs | 65 ++----------------- .../preserve-review-across-planning.md | 3 +- memory/features/show-active-work-in-work.md | 4 +- memory/log.md | 2 + memory/state.md | 6 +- memory/usage.md | 10 +-- skills/iterator/lib/views/hub.mjs | 33 +++++++--- skills/iterator/lib/views/planning.mjs | 65 ++----------------- test/ui.test.mjs | 10 ++- 10 files changed, 81 insertions(+), 150 deletions(-) diff --git a/lib/views/hub.mjs b/lib/views/hub.mjs index 6c64059..90dad21 100644 --- a/lib/views/hub.mjs +++ b/lib/views/hub.mjs @@ -1,11 +1,10 @@ /** * iterator: Work dashboard UI on the shared shell (../ui.mjs, * ../server.mjs). The execution surface of the flow: the active plan's - * progress, the escalation banner, and the feature cards with their - * Test / Implement / Review actions. Plan management — backlog, plan - * creation/revision/retirement, the dependency graph, feature cancellation — - * lives on the planning surface (./planning.mjs); both render from the same - * gather payload so they can never disagree about state. + * progress, dependency graph, and feature cards with their Test / Implement / + * Review actions. Planning owns backlog and plan-lifecycle controls; active + * plan context and feature management live here. Both surfaces render from + * the same gather payload so they can never disagree about state. * * input: { step:"hub", branch, * plan: { title, status } | null, // null = no bundle yet @@ -21,12 +20,13 @@ * retired: [ { name, title, created } ] // archived plans, newest first * output: one JSON line to stdout — * { type:"action", action:"planning"|"test"|"implement"|"review"|"review-all"|"implement-wave"|"auto-implement" - * |"escalation-restart"|"escalation-guide", + * |"cancel-feature"|"escalation-restart"|"escalation-guide", * feature:""|null, * prompt:""|null } // escalation-guide only * plus the shared { type:"cancel" } / { type:"timeout" }. */ import { renderPage } from "../ui.mjs"; +import { GRAPH_CSS, GRAPH_JS } from "./graph.mjs"; import { WIDGETS_CSS, WIDGETS_JS } from "./widgets.mjs"; const CSS = ` @@ -147,6 +147,14 @@ function render(){ } w.appendChild(bar); + // Active dependency structure belongs beside execution status. Readiness and + // dependencies are supplied by gather; this view only renders them. + const gt = document.createElement('div'); gt.className='sec-title'; gt.textContent='Dependency graph'; + const cw = document.createElement('div'); cw.id='cyclewarn'; + const g = document.createElement('div'); g.className='graph'; g.id='graph'; + w.appendChild(gt); w.appendChild(cw); w.appendChild(g); + renderGraphInto(g, cw, CH, 'fix depends-on in /iterator-feature before implementing.'); + // cards const ct = document.createElement('div'); ct.className='sec-title'; ct.textContent='Features'; w.appendChild(ct); @@ -214,7 +222,14 @@ function makeCard(c){ else { rev.disabled = true; rev.title = 'Implement first — review unlocks once the feature is implemented'; } rev.addEventListener('click', () => action('review', c.name, 'Starting /iterator-review')); - btns.appendChild(impl); btns.appendChild(test); btns.appendChild(rev); + const cancel = document.createElement('button'); + cancel.className = 'act danger'; + cancel.textContent = 'Cancel feature'; + cancel.title = 'Remove this feature from the plan (its file is archived; dependents are unwired)'; + confirmButton(cancel, 'Really cancel \\u2014 click again', () => + action('cancel-feature', c.name, 'Cancelling feature')); + + btns.appendChild(impl); btns.appendChild(test); btns.appendChild(rev); btns.appendChild(cancel); card.appendChild(btns); return card; } @@ -227,9 +242,9 @@ export function render(data) { branch: data.branch, title: data.plan && data.plan.title, data, - css: WIDGETS_CSS + CSS, + css: WIDGETS_CSS + GRAPH_CSS + CSS, body: BODY, - clientJs: WIDGETS_JS + JS, + clientJs: WIDGETS_JS + GRAPH_JS + JS, primary: false, // the per-card action buttons are the primaries here cancel: false, // idle dashboard — there is no round to cancel }); diff --git a/lib/views/planning.mjs b/lib/views/planning.mjs index 21614bf..ee9d53c 100644 --- a/lib/views/planning.mjs +++ b/lib/views/planning.mjs @@ -1,10 +1,9 @@ /** * iterator: planning UI on the shared shell (../ui.mjs, ../server.mjs). * The plan-management surface: where ideas and bugs are collected (backlog), - * plans are created, revised, featured, reviewed, retired, or cancelled, and - * the feature set is inspected (read-only cards + dependency graph). The - * execution side — Test / Implement / Review — lives on the Work surface - * (./hub.mjs). + * plans are created, revised, featured, reviewed, retired, or cancelled. + * Active plan features, dependency structure, and execution controls live on + * the Work surface (./hub.mjs). * * Rendered from the SAME gather payload as the hub (gather --step planning * just stamps step:"planning"), so the two surfaces can never disagree about @@ -13,13 +12,12 @@ * * output: one JSON line to stdout — * { type:"action", action:"plan"|"feature"|"review-plan"|"retire"| - * "cancel-plan"|"cancel-feature"|"iterator-init"|"view-archive", + * "cancel-plan"|"iterator-init"|"view-archive", * feature, prompt } * { type:"backlog", action:"create"|"edit"|"delete"|"select", ... } * plus the shared { type:"cancel" } / { type:"timeout" }. */ import { renderPage } from "../ui.mjs"; -import { GRAPH_CSS, GRAPH_JS } from "./graph.mjs"; import { WIDGETS_CSS, WIDGETS_JS } from "./widgets.mjs"; const PLANNING_CSS = ` @@ -243,28 +241,6 @@ function render(){ bar.appendChild(cancelPlan); w.appendChild(bar); renderBacklog(w); - - // graph - const gt = document.createElement('div'); gt.className='sec-title'; gt.textContent='Dependency graph'; - const cw = document.createElement('div'); cw.id='cyclewarn'; - const g = document.createElement('div'); g.className='graph'; g.id='graph'; - w.appendChild(gt); w.appendChild(cw); w.appendChild(g); - renderGraphInto(g, cw, CH, 'fix depends-on in /iterator-feature before implementing.'); - - // Read-only feature cards: state at a glance + Cancel; the action buttons - // (Test / Implement / Review) live on the Work tab. - const ct = document.createElement('div'); ct.className='sec-title'; ct.textContent='Features'; - w.appendChild(ct); - if(!CH.length){ - const e = document.createElement('div'); e.className='hero'; - e.innerHTML = '

No features yet

The plan has not been broken into features.

'; - const b = document.createElement('button'); - b.className='act primary-act'; b.textContent='Feature the plan'; - b.addEventListener('click', () => action('feature', null, 'Starting /iterator-feature')); - e.appendChild(b); w.appendChild(e); - return; - } - CH.forEach(c => w.appendChild(makeCard(c))); renderRetired(w); } @@ -340,35 +316,6 @@ function renderRetired(w){ w.appendChild(section); } -function makeCard(c){ - const card = document.createElement('div'); - card.className = 'card' + (c.status==='done'?' done':''); - const draft = c.status==='draft'; - const implemented = c.status==='implemented'; - const icon = ''; - card.innerHTML = - '
'+icon+esc(c.title||c.name)+''+ - ''+esc(c.name)+''+ - (draft?'draft':'')+ - (implemented?'implemented \\u2014 awaiting review':'')+ - sizeChip(c)+testBadge(c)+ - (c.conflicts?'\\u26a0 '+c.conflicts+' decision conflict'+(c.conflicts!==1?'s':'')+'':'')+'
'+ - '
'+esc(c.description||'')+'
'+ - ((c.dependsOn&&c.dependsOn.length)?'
depends on '+c.dependsOn.map(d=>''+esc(d)+'').join(' ')+ - ((c.ready===false)?' waiting on '+esc((c.waitingOn||[]).join(', '))+'':'')+'
':''); - - const btns = document.createElement('div'); - btns.className = 'btns'; - const cancel = document.createElement('button'); - cancel.className = 'act danger'; - cancel.textContent = 'Cancel feature'; - cancel.title = 'Remove this feature from the plan (its file is archived; dependents are unwired)'; - confirmButton(cancel, 'Really cancel \\u2014 click again', () => - action('cancel-feature', c.name, 'Cancelling feature')); - btns.appendChild(cancel); - card.appendChild(btns); - return card; -} `; export function render(data) { @@ -378,9 +325,9 @@ export function render(data) { branch: data.branch, title: data.plan && data.plan.title, data, - css: WIDGETS_CSS + PLANNING_CSS + GRAPH_CSS, + css: WIDGETS_CSS + PLANNING_CSS, body: BODY, - clientJs: WIDGETS_JS + GRAPH_JS + JS, + clientJs: WIDGETS_JS + JS, primary: false, // the inline buttons are the primaries here cancel: false, // idle dashboard — there is no round to cancel }); diff --git a/memory/features/preserve-review-across-planning.md b/memory/features/preserve-review-across-planning.md index 0a82213..5a805c6 100644 --- a/memory/features/preserve-review-across-planning.md +++ b/memory/features/preserve-review-across-planning.md @@ -7,7 +7,7 @@ size: medium depends_on: [] files: ["lib/session-server.mjs", "extensions/iterator.js", "test/session-server.test.mjs", "test/nonblocking-working-overlay.test.mjs"] memories: [pitfalls/cancel-now-after-grace-timer, architecture/browser-server-contract, architecture/package-and-skill-layout, decisions/backlog-planning-and-feature-waves, 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-17T16:23:12.103Z" +timestamp: "2026-07-17T16:23:18.304Z" tags: [] commits: - sha: 30c6a6b70a54d59a72a79ba2f864a9b993006262 @@ -37,4 +37,5 @@ The session shell's one-pending-round lifecycle, tab switching, backlog CRUD, an # Review ## 2026-07-17 +* **Approved** _(agent review: openai-codex/gpt-5.6-sol)_ — Approved: the persistent shell now deterministically owns unload cancellation, embedded view tab switches cannot abort the pending review, explicit cancel pre-empts grace timers, and the Review → Planning → Work regression plus full suite pass. * **Needs changes** _(agent review: openai-codex/gpt-5.6-sol)_ — The fix is race-prone and the tests do not exercise the behavior. `setTab()` queues a `postMessage({iterator:'navigate'})` and immediately replaces `frame.src`; delivery of that queued message is not guaranteed before the outgoing iframe's `pagehide`, so `sendCancel()` can still beacon `/cancel` and abort the pending review. Make cancellation ownership deterministic (for example, suppress iframe pagehide beacons in the persistent session and let the parent shell beacon `/cancel` when the whole dashboard unloads), then add a behavioral regression that performs review → Planning → Work and proves the original review round remains pending and can still submit. diff --git a/memory/features/show-active-work-in-work.md b/memory/features/show-active-work-in-work.md index 6ac64b9..5947253 100644 --- a/memory/features/show-active-work-in-work.md +++ b/memory/features/show-active-work-in-work.md @@ -2,12 +2,12 @@ type: Feature title: Show active plan context on Work description: Make Work the home for the active plan, its feature set, and the dependency graph. -status: pending +status: implemented size: medium depends_on: [] files: ["lib/views/hub.mjs", "lib/views/planning.mjs", "lib/views/graph.mjs", "test/ui.test.mjs"] memories: [pitfalls/client-js-template-literal-escaping, architecture/workflow-state-ownership, patterns/safe-browser-rendering, decisions/backlog-planning-and-feature-waves, 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-17T16:17:17.903Z" +timestamp: "2026-07-17T16:25:55.035Z" tags: [] --- diff --git a/memory/log.md b/memory/log.md index 527dce5..4be709a 100644 --- a/memory/log.md +++ b/memory/log.md @@ -1,6 +1,8 @@ # iterator update log ## 2026-07-17 +* **Implementation**: Committed feature(show-active-work-in-work) on branch iterator/always-available-backlog; awaiting review. +* **Review**: Reviewed [Preserve reviews across Planning navigation](/features/preserve-review-across-planning.md); approved (agent). * **Review**: Accepted [Preserve reviews across Planning navigation](/features/preserve-review-across-planning.md) (committed as feature(preserve-review-across-planning)). * **Implementation**: Committed feature(preserve-review-across-planning) on branch iterator/always-available-backlog; awaiting review. * **Review**: Reviewed [Preserve reviews across Planning navigation](/features/preserve-review-across-planning.md); changes (agent). diff --git a/memory/state.md b/memory/state.md index c032b49..ff011ee 100644 --- a/memory/state.md +++ b/memory/state.md @@ -4,11 +4,11 @@ title: Runtime state description: Machine-owned iterator flow state — never hand-edited. mode: auto paused: false -phase: reviewing -active_feature: preserve-review-across-planning +phase: implementing +active_feature: show-active-work-in-work strikes: "{\"implement-ready-feature-wave\":2,\"always-available-backlog\":1,\"review-multiple-implemented-features\":1,\"preserve-review-across-planning\":1}" escalation: null -timestamp: 2026-07-17T16:22:50.662Z +timestamp: 2026-07-17T16:23:23.152Z --- Runtime flow state; read via gather, written only by the state op. diff --git a/memory/usage.md b/memory/usage.md index 4f93b21..64831d6 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\":16969,\"output\":1048,\"cacheRead\":53248,\"cacheWrite\":0,\"turns\":5}},\"plan\":{\"openai-codex/gpt-5.6-terra\":{\"input\":61028,\"output\":5028,\"cacheRead\":668672,\"cacheWrite\":0,\"turns\":17}},\"implement\":{\"openai-codex/gpt-5.6-terra\":{\"input\":119901,\"output\":3970,\"cacheRead\":1700864,\"cacheWrite\":0,\"turns\":19},\"openai-codex/gpt-5.6-sol\":{\"input\":147159,\"output\":3764,\"cacheRead\":2008064,\"cacheWrite\":0,\"turns\":17}},\"review\":{\"openai-codex/gpt-5.6-sol\":{\"input\":115474,\"output\":925,\"cacheRead\":333312,\"cacheWrite\":0,\"turns\":4}}},\"features\":{\"retire-plan\":{\"input\":16969,\"output\":1048,\"cacheRead\":53248,\"cacheWrite\":0,\"turns\":5},\"preserve-review-across-planning\":{\"input\":382534,\"output\":8659,\"cacheRead\":4042240,\"cacheWrite\":0,\"turns\":40}}}" -timestamp: 2026-07-17T16:22:50.432Z +totals: "{\"steps\":{\"hub\":{\"openai-codex/gpt-5.6-terra\":{\"input\":16969,\"output\":1048,\"cacheRead\":53248,\"cacheWrite\":0,\"turns\":5}},\"plan\":{\"openai-codex/gpt-5.6-terra\":{\"input\":61028,\"output\":5028,\"cacheRead\":668672,\"cacheWrite\":0,\"turns\":17}},\"implement\":{\"openai-codex/gpt-5.6-terra\":{\"input\":119901,\"output\":3970,\"cacheRead\":1700864,\"cacheWrite\":0,\"turns\":19},\"openai-codex/gpt-5.6-sol\":{\"input\":147159,\"output\":3764,\"cacheRead\":2008064,\"cacheWrite\":0,\"turns\":17}},\"review\":{\"openai-codex/gpt-5.6-sol\":{\"input\":282795,\"output\":1756,\"cacheRead\":733184,\"cacheWrite\":0,\"turns\":8}}},\"features\":{\"retire-plan\":{\"input\":16969,\"output\":1048,\"cacheRead\":53248,\"cacheWrite\":0,\"turns\":5},\"preserve-review-across-planning\":{\"input\":549855,\"output\":9490,\"cacheRead\":4442112,\"cacheWrite\":0,\"turns\":44}}}" +timestamp: 2026-07-17T16:23:22.922Z --- # Usage @@ -31,13 +31,13 @@ timestamp: 2026-07-17T16:22:50.432Z | model | input | output | cache read | cache write | turns | | --- | ---: | ---: | ---: | ---: | ---: | -| openai-codex/gpt-5.6-sol | 115474 | 925 | 333312 | 0 | 4 | +| openai-codex/gpt-5.6-sol | 282795 | 1756 | 733184 | 0 | 8 | ## Per feature | feature | input | output | cache read | cache write | turns | | --- | ---: | ---: | ---: | ---: | ---: | | retire-plan | 16969 | 1048 | 53248 | 0 | 5 | -| preserve-review-across-planning | 382534 | 8659 | 4042240 | 0 | 40 | +| preserve-review-across-planning | 549855 | 9490 | 4442112 | 0 | 44 | -Total: 460531 in / 14735 out / 4764160 cache-read / 0 cache-write over 62 turns. +Total: 627852 in / 15566 out / 5164032 cache-read / 0 cache-write over 66 turns. diff --git a/skills/iterator/lib/views/hub.mjs b/skills/iterator/lib/views/hub.mjs index 6c64059..90dad21 100644 --- a/skills/iterator/lib/views/hub.mjs +++ b/skills/iterator/lib/views/hub.mjs @@ -1,11 +1,10 @@ /** * iterator: Work dashboard UI on the shared shell (../ui.mjs, * ../server.mjs). The execution surface of the flow: the active plan's - * progress, the escalation banner, and the feature cards with their - * Test / Implement / Review actions. Plan management — backlog, plan - * creation/revision/retirement, the dependency graph, feature cancellation — - * lives on the planning surface (./planning.mjs); both render from the same - * gather payload so they can never disagree about state. + * progress, dependency graph, and feature cards with their Test / Implement / + * Review actions. Planning owns backlog and plan-lifecycle controls; active + * plan context and feature management live here. Both surfaces render from + * the same gather payload so they can never disagree about state. * * input: { step:"hub", branch, * plan: { title, status } | null, // null = no bundle yet @@ -21,12 +20,13 @@ * retired: [ { name, title, created } ] // archived plans, newest first * output: one JSON line to stdout — * { type:"action", action:"planning"|"test"|"implement"|"review"|"review-all"|"implement-wave"|"auto-implement" - * |"escalation-restart"|"escalation-guide", + * |"cancel-feature"|"escalation-restart"|"escalation-guide", * feature:""|null, * prompt:""|null } // escalation-guide only * plus the shared { type:"cancel" } / { type:"timeout" }. */ import { renderPage } from "../ui.mjs"; +import { GRAPH_CSS, GRAPH_JS } from "./graph.mjs"; import { WIDGETS_CSS, WIDGETS_JS } from "./widgets.mjs"; const CSS = ` @@ -147,6 +147,14 @@ function render(){ } w.appendChild(bar); + // Active dependency structure belongs beside execution status. Readiness and + // dependencies are supplied by gather; this view only renders them. + const gt = document.createElement('div'); gt.className='sec-title'; gt.textContent='Dependency graph'; + const cw = document.createElement('div'); cw.id='cyclewarn'; + const g = document.createElement('div'); g.className='graph'; g.id='graph'; + w.appendChild(gt); w.appendChild(cw); w.appendChild(g); + renderGraphInto(g, cw, CH, 'fix depends-on in /iterator-feature before implementing.'); + // cards const ct = document.createElement('div'); ct.className='sec-title'; ct.textContent='Features'; w.appendChild(ct); @@ -214,7 +222,14 @@ function makeCard(c){ else { rev.disabled = true; rev.title = 'Implement first — review unlocks once the feature is implemented'; } rev.addEventListener('click', () => action('review', c.name, 'Starting /iterator-review')); - btns.appendChild(impl); btns.appendChild(test); btns.appendChild(rev); + const cancel = document.createElement('button'); + cancel.className = 'act danger'; + cancel.textContent = 'Cancel feature'; + cancel.title = 'Remove this feature from the plan (its file is archived; dependents are unwired)'; + confirmButton(cancel, 'Really cancel \\u2014 click again', () => + action('cancel-feature', c.name, 'Cancelling feature')); + + btns.appendChild(impl); btns.appendChild(test); btns.appendChild(rev); btns.appendChild(cancel); card.appendChild(btns); return card; } @@ -227,9 +242,9 @@ export function render(data) { branch: data.branch, title: data.plan && data.plan.title, data, - css: WIDGETS_CSS + CSS, + css: WIDGETS_CSS + GRAPH_CSS + CSS, body: BODY, - clientJs: WIDGETS_JS + JS, + clientJs: WIDGETS_JS + GRAPH_JS + JS, primary: false, // the per-card action buttons are the primaries here cancel: false, // idle dashboard — there is no round to cancel }); diff --git a/skills/iterator/lib/views/planning.mjs b/skills/iterator/lib/views/planning.mjs index 21614bf..ee9d53c 100644 --- a/skills/iterator/lib/views/planning.mjs +++ b/skills/iterator/lib/views/planning.mjs @@ -1,10 +1,9 @@ /** * iterator: planning UI on the shared shell (../ui.mjs, ../server.mjs). * The plan-management surface: where ideas and bugs are collected (backlog), - * plans are created, revised, featured, reviewed, retired, or cancelled, and - * the feature set is inspected (read-only cards + dependency graph). The - * execution side — Test / Implement / Review — lives on the Work surface - * (./hub.mjs). + * plans are created, revised, featured, reviewed, retired, or cancelled. + * Active plan features, dependency structure, and execution controls live on + * the Work surface (./hub.mjs). * * Rendered from the SAME gather payload as the hub (gather --step planning * just stamps step:"planning"), so the two surfaces can never disagree about @@ -13,13 +12,12 @@ * * output: one JSON line to stdout — * { type:"action", action:"plan"|"feature"|"review-plan"|"retire"| - * "cancel-plan"|"cancel-feature"|"iterator-init"|"view-archive", + * "cancel-plan"|"iterator-init"|"view-archive", * feature, prompt } * { type:"backlog", action:"create"|"edit"|"delete"|"select", ... } * plus the shared { type:"cancel" } / { type:"timeout" }. */ import { renderPage } from "../ui.mjs"; -import { GRAPH_CSS, GRAPH_JS } from "./graph.mjs"; import { WIDGETS_CSS, WIDGETS_JS } from "./widgets.mjs"; const PLANNING_CSS = ` @@ -243,28 +241,6 @@ function render(){ bar.appendChild(cancelPlan); w.appendChild(bar); renderBacklog(w); - - // graph - const gt = document.createElement('div'); gt.className='sec-title'; gt.textContent='Dependency graph'; - const cw = document.createElement('div'); cw.id='cyclewarn'; - const g = document.createElement('div'); g.className='graph'; g.id='graph'; - w.appendChild(gt); w.appendChild(cw); w.appendChild(g); - renderGraphInto(g, cw, CH, 'fix depends-on in /iterator-feature before implementing.'); - - // Read-only feature cards: state at a glance + Cancel; the action buttons - // (Test / Implement / Review) live on the Work tab. - const ct = document.createElement('div'); ct.className='sec-title'; ct.textContent='Features'; - w.appendChild(ct); - if(!CH.length){ - const e = document.createElement('div'); e.className='hero'; - e.innerHTML = '

No features yet

The plan has not been broken into features.

'; - const b = document.createElement('button'); - b.className='act primary-act'; b.textContent='Feature the plan'; - b.addEventListener('click', () => action('feature', null, 'Starting /iterator-feature')); - e.appendChild(b); w.appendChild(e); - return; - } - CH.forEach(c => w.appendChild(makeCard(c))); renderRetired(w); } @@ -340,35 +316,6 @@ function renderRetired(w){ w.appendChild(section); } -function makeCard(c){ - const card = document.createElement('div'); - card.className = 'card' + (c.status==='done'?' done':''); - const draft = c.status==='draft'; - const implemented = c.status==='implemented'; - const icon = ''; - card.innerHTML = - '
'+icon+esc(c.title||c.name)+''+ - ''+esc(c.name)+''+ - (draft?'draft':'')+ - (implemented?'implemented \\u2014 awaiting review':'')+ - sizeChip(c)+testBadge(c)+ - (c.conflicts?'\\u26a0 '+c.conflicts+' decision conflict'+(c.conflicts!==1?'s':'')+'':'')+'
'+ - '
'+esc(c.description||'')+'
'+ - ((c.dependsOn&&c.dependsOn.length)?'
depends on '+c.dependsOn.map(d=>''+esc(d)+'').join(' ')+ - ((c.ready===false)?' waiting on '+esc((c.waitingOn||[]).join(', '))+'':'')+'
':''); - - const btns = document.createElement('div'); - btns.className = 'btns'; - const cancel = document.createElement('button'); - cancel.className = 'act danger'; - cancel.textContent = 'Cancel feature'; - cancel.title = 'Remove this feature from the plan (its file is archived; dependents are unwired)'; - confirmButton(cancel, 'Really cancel \\u2014 click again', () => - action('cancel-feature', c.name, 'Cancelling feature')); - btns.appendChild(cancel); - card.appendChild(btns); - return card; -} `; export function render(data) { @@ -378,9 +325,9 @@ export function render(data) { branch: data.branch, title: data.plan && data.plan.title, data, - css: WIDGETS_CSS + PLANNING_CSS + GRAPH_CSS, + css: WIDGETS_CSS + PLANNING_CSS, body: BODY, - clientJs: WIDGETS_JS + GRAPH_JS + JS, + clientJs: WIDGETS_JS + JS, primary: false, // the inline buttons are the primaries here cancel: false, // idle dashboard — there is no round to cancel }); diff --git a/test/ui.test.mjs b/test/ui.test.mjs index 0d9cd99..9873b0a 100644 --- a/test/ui.test.mjs +++ b/test/ui.test.mjs @@ -413,6 +413,9 @@ test("hub gates Implement/Review on status and renders escalation + review-plan assert.match(html, /Review all/); assert.match(html, /action\('review-all'/); assert.match(html, /Array\.isArray\(D\.reviewWave\)/); + // Active feature context and management live on Work. + assert.match(html, /renderGraphInto/); + assert.match(html, /action\('cancel-feature'/); // Plan-lifecycle controls live on the Planning surface, not Work. assert.doesNotMatch(html, /action\('review-plan'/); assert.doesNotMatch(html, /Retires the plan/); @@ -465,12 +468,13 @@ test("planning drives plan-lifecycle controls from the server-derived stage", as assert.match(html, /action\('review-plan'/); assert.match(html, /Retires the plan/); assert.match(html, /action\('cancel-plan'/); - assert.match(html, /action\('cancel-feature'/); + // Active features and their dependency graph live on Work, not Planning. + assert.doesNotMatch(html, /action\('cancel-feature'/); + assert.doesNotMatch(html, /renderGraphInto/); // The execution controls live on Work, not here. assert.doesNotMatch(html, /action\('implement'/); assert.doesNotMatch(html, /auto-implement/); - // Dependency graph + retired-plan browser render here. - assert.match(html, /renderGraphInto/); + // Retired-plan browsing remains a planning concern. assert.match(html, /view-archive/); }); From 8f90ea0e9a4f75b24d2915bc6119a6b37a716092 Mon Sep 17 00:00:00 2001 From: Christoph Maier Date: Fri, 17 Jul 2026 18:25:55 +0200 Subject: [PATCH 07/22] chore(iterator): record feature commit --- memory/features/show-active-work-in-work.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/memory/features/show-active-work-in-work.md b/memory/features/show-active-work-in-work.md index 5947253..8588579 100644 --- a/memory/features/show-active-work-in-work.md +++ b/memory/features/show-active-work-in-work.md @@ -7,8 +7,12 @@ size: medium depends_on: [] files: ["lib/views/hub.mjs", "lib/views/planning.mjs", "lib/views/graph.mjs", "test/ui.test.mjs"] memories: [pitfalls/client-js-template-literal-escaping, architecture/workflow-state-ownership, patterns/safe-browser-rendering, decisions/backlog-planning-and-feature-waves, 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-17T16:25:55.035Z" +timestamp: "2026-07-17T16:25:55.104Z" tags: [] +commits: + - sha: e0b0453cbc024caa1cfdcbe079767214f4d8c6d8 + kind: implement + date: 2026-07-17 --- # Implementation notes From e0fba14b9073630c2162042e60ff32b3051edbac Mon Sep 17 00:00:00 2001 From: Christoph Maier Date: Fri, 17 Jul 2026 18:26:20 +0200 Subject: [PATCH 08/22] chore(iterator): record feature commits and memory updates --- memory/features/index.md | 2 +- memory/features/show-active-work-in-work.md | 5 +++-- memory/log.md | 1 + memory/state.md | 4 ++-- memory/usage.md | 9 +++++---- 5 files changed, 12 insertions(+), 9 deletions(-) diff --git a/memory/features/index.md b/memory/features/index.md index 99924f5..20a75a8 100644 --- a/memory/features/index.md +++ b/memory/features/index.md @@ -1,5 +1,5 @@ # Features * [Preserve reviews across Planning navigation](preserve-review-across-planning.md) - ✅ done · medium · Keep an active review open while users manage backlog items on the Planning tab and return to it. -* [Show active plan context on Work](show-active-work-in-work.md) - ⬜ pending · medium · Make Work the home for the active plan, its feature set, and the dependency graph. +* [Show active plan context on Work](show-active-work-in-work.md) - ✅ done · medium · Make Work the home for the active plan, its feature set, and the dependency graph. * [Keep review controls fully readable](streamline-review-interface.md) - ⬜ pending · medium · Show complete feature titles in review and remove the unused Feedback panel. diff --git a/memory/features/show-active-work-in-work.md b/memory/features/show-active-work-in-work.md index 8588579..c4a8b19 100644 --- a/memory/features/show-active-work-in-work.md +++ b/memory/features/show-active-work-in-work.md @@ -2,17 +2,18 @@ type: Feature title: Show active plan context on Work description: Make Work the home for the active plan, its feature set, and the dependency graph. -status: implemented +status: done size: medium depends_on: [] files: ["lib/views/hub.mjs", "lib/views/planning.mjs", "lib/views/graph.mjs", "test/ui.test.mjs"] memories: [pitfalls/client-js-template-literal-escaping, architecture/workflow-state-ownership, patterns/safe-browser-rendering, decisions/backlog-planning-and-feature-waves, 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-17T16:25:55.104Z" +timestamp: "2026-07-17T16:26:20.623Z" tags: [] commits: - sha: e0b0453cbc024caa1cfdcbe079767214f4d8c6d8 kind: implement date: 2026-07-17 +done: 2026-07-17 --- # Implementation notes diff --git a/memory/log.md b/memory/log.md index 4be709a..2bc26c9 100644 --- a/memory/log.md +++ b/memory/log.md @@ -1,6 +1,7 @@ # iterator update log ## 2026-07-17 +* **Review**: Accepted [Show active plan context on Work](/features/show-active-work-in-work.md) (committed as feature(show-active-work-in-work)). * **Implementation**: Committed feature(show-active-work-in-work) on branch iterator/always-available-backlog; awaiting review. * **Review**: Reviewed [Preserve reviews across Planning navigation](/features/preserve-review-across-planning.md); approved (agent). * **Review**: Accepted [Preserve reviews across Planning navigation](/features/preserve-review-across-planning.md) (committed as feature(preserve-review-across-planning)). diff --git a/memory/state.md b/memory/state.md index ff011ee..74b84e1 100644 --- a/memory/state.md +++ b/memory/state.md @@ -4,11 +4,11 @@ title: Runtime state description: Machine-owned iterator flow state — never hand-edited. mode: auto paused: false -phase: implementing +phase: reviewing active_feature: show-active-work-in-work strikes: "{\"implement-ready-feature-wave\":2,\"always-available-backlog\":1,\"review-multiple-implemented-features\":1,\"preserve-review-across-planning\":1}" escalation: null -timestamp: 2026-07-17T16:23:23.152Z +timestamp: 2026-07-17T16:26:06.658Z --- Runtime flow state; read via gather, written only by the state op. diff --git a/memory/usage.md b/memory/usage.md index 64831d6..b286921 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\":16969,\"output\":1048,\"cacheRead\":53248,\"cacheWrite\":0,\"turns\":5}},\"plan\":{\"openai-codex/gpt-5.6-terra\":{\"input\":61028,\"output\":5028,\"cacheRead\":668672,\"cacheWrite\":0,\"turns\":17}},\"implement\":{\"openai-codex/gpt-5.6-terra\":{\"input\":119901,\"output\":3970,\"cacheRead\":1700864,\"cacheWrite\":0,\"turns\":19},\"openai-codex/gpt-5.6-sol\":{\"input\":147159,\"output\":3764,\"cacheRead\":2008064,\"cacheWrite\":0,\"turns\":17}},\"review\":{\"openai-codex/gpt-5.6-sol\":{\"input\":282795,\"output\":1756,\"cacheRead\":733184,\"cacheWrite\":0,\"turns\":8}}},\"features\":{\"retire-plan\":{\"input\":16969,\"output\":1048,\"cacheRead\":53248,\"cacheWrite\":0,\"turns\":5},\"preserve-review-across-planning\":{\"input\":549855,\"output\":9490,\"cacheRead\":4442112,\"cacheWrite\":0,\"turns\":44}}}" -timestamp: 2026-07-17T16:23:22.922Z +totals: "{\"steps\":{\"hub\":{\"openai-codex/gpt-5.6-terra\":{\"input\":16969,\"output\":1048,\"cacheRead\":53248,\"cacheWrite\":0,\"turns\":5}},\"plan\":{\"openai-codex/gpt-5.6-terra\":{\"input\":61028,\"output\":5028,\"cacheRead\":668672,\"cacheWrite\":0,\"turns\":17}},\"implement\":{\"openai-codex/gpt-5.6-terra\":{\"input\":119901,\"output\":3970,\"cacheRead\":1700864,\"cacheWrite\":0,\"turns\":19},\"openai-codex/gpt-5.6-sol\":{\"input\":196441,\"output\":8227,\"cacheRead\":4274176,\"cacheWrite\":0,\"turns\":31}},\"review\":{\"openai-codex/gpt-5.6-sol\":{\"input\":282795,\"output\":1756,\"cacheRead\":733184,\"cacheWrite\":0,\"turns\":8}}},\"features\":{\"retire-plan\":{\"input\":16969,\"output\":1048,\"cacheRead\":53248,\"cacheWrite\":0,\"turns\":5},\"preserve-review-across-planning\":{\"input\":549855,\"output\":9490,\"cacheRead\":4442112,\"cacheWrite\":0,\"turns\":44},\"show-active-work-in-work\":{\"input\":49282,\"output\":4463,\"cacheRead\":2266112,\"cacheWrite\":0,\"turns\":14}}}" +timestamp: 2026-07-17T16:26:06.426Z --- # Usage @@ -25,7 +25,7 @@ timestamp: 2026-07-17T16:23:22.922Z | model | input | output | cache read | cache write | turns | | --- | ---: | ---: | ---: | ---: | ---: | | openai-codex/gpt-5.6-terra | 119901 | 3970 | 1700864 | 0 | 19 | -| openai-codex/gpt-5.6-sol | 147159 | 3764 | 2008064 | 0 | 17 | +| openai-codex/gpt-5.6-sol | 196441 | 8227 | 4274176 | 0 | 31 | ## review @@ -39,5 +39,6 @@ timestamp: 2026-07-17T16:23:22.922Z | --- | ---: | ---: | ---: | ---: | ---: | | retire-plan | 16969 | 1048 | 53248 | 0 | 5 | | preserve-review-across-planning | 549855 | 9490 | 4442112 | 0 | 44 | +| show-active-work-in-work | 49282 | 4463 | 2266112 | 0 | 14 | -Total: 627852 in / 15566 out / 5164032 cache-read / 0 cache-write over 66 turns. +Total: 677134 in / 20029 out / 7430144 cache-read / 0 cache-write over 80 turns. From e7d5824e2ac431640e6a1abc6169dfcc6121b43b Mon Sep 17 00:00:00 2001 From: Christoph Maier Date: Fri, 17 Jul 2026 18:29:00 +0200 Subject: [PATCH 09/22] feature(streamline-review-interface): keep review labels readable and remove feedback panel Feature: streamline-review-interface --- lib/views/review.mjs | 52 +++---------------- memory/features/show-active-work-in-work.md | 8 ++- .../features/streamline-review-interface.md | 4 +- memory/log.md | 2 + memory/state.md | 6 +-- memory/usage.md | 10 ++-- skills/iterator/lib/views/review.mjs | 52 +++---------------- test/client-js-parse.test.mjs | 15 +++++- test/ui.test.mjs | 8 +++ 9 files changed, 55 insertions(+), 102 deletions(-) diff --git a/lib/views/review.mjs b/lib/views/review.mjs index 70c2f7a..690b59a 100644 --- a/lib/views/review.mjs +++ b/lib/views/review.mjs @@ -51,18 +51,18 @@ body{height:100vh;overflow:hidden;display:flex;flex-direction:column} .dot{width:8px;height:8px;border-radius:50%;margin-top:4px;flex-shrink:0} .dg{background:var(--dot-green)}.dy{background:var(--dot-yellow)}.dr{background:var(--dot-red)} .fm{flex:1;min-width:0} -.fn{font-size:13px;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis} +.fn{font-size:13px;font-weight:500;white-space:normal;overflow-wrap:anywhere} .fs{font-size:11px;color:var(--text-muted);margin-top:2px} .sa{color:var(--add-fg)}.sd{color:var(--del-fg)} .sbadge{font-size:10px;border-radius:3px;padding:1px 5px;margin-top:3px;display:inline-block} .s-app{background:var(--bg-green);color:var(--dot-green)} .s-chg{background:var(--bg-red);color:var(--dot-red)} .s-qst{background:var(--bg-yellow);color:var(--dot-yellow)} -.detail{flex:1;overflow-y:auto;padding:var(--sp-5);padding-bottom:130px} +.detail{flex:1;overflow-y:auto;padding:var(--sp-5)} .fc{overflow:visible} .fch{position:sticky;top:0;z-index:5;border-radius:var(--radius-card) var(--radius-card) 0 0} .fh{margin-bottom:var(--sp-4);padding-bottom:var(--sp-4);border-bottom:1px solid var(--border)} -.ftitle{font-family:var(--font-display);font-size:var(--fs-xl);font-weight:600;margin-bottom:6px} +.ftitle{font-family:var(--font-display);font-size:var(--fs-xl);font-weight:600;margin-bottom:6px;overflow-wrap:anywhere} .fdesc{color:var(--text-muted);font-size:var(--fs-sm);margin-bottom:12px} button.note-btn{font-size:12px;background:none;border:1px dashed var(--border);color:var(--text-muted);border-radius:4px;padding:3px 8px;cursor:pointer} button.note-btn:hover{border-color:var(--accent);color:var(--accent)} @@ -139,24 +139,11 @@ button.cs{background:var(--accent);border-color:var(--accent);color:var(--accent .muted{color:var(--text-muted)} .empty{text-align:center;padding:60px 20px;color:var(--text-muted)} .empty h3{font-size:16px;margin-bottom:8px} -.fb{position:fixed;bottom:0;right:0;width:400px;background:var(--fb-bg);border-top:1px solid var(--border); - border-left:1px solid var(--border);border-radius:8px 0 0 0;z-index:100;transition:transform .2s} -.fb.col{transform:translateY(calc(100% - 36px))} -.fbh{display:flex;align-items:center;justify-content:space-between;padding:8px 12px;cursor:pointer;user-select:none} -.fbt{font-size:13px;font-weight:500} -.fbc{font-size:var(--fs-xs);background:var(--accent);color:var(--accent-fg);border-radius:10px;padding:1px 7px;display:none} -.fbc.vis{display:inline} -.fbtog{font-size:11px;color:var(--text-muted)} -.fbb{padding:0 12px 12px} -.fbo{background:var(--bg);border:1px solid var(--border);border-radius:4px;padding:10px;font-family:var(--font-mono); - font-size:var(--fs-xs);color:var(--text);max-height:160px;overflow-y:auto;white-space:pre-wrap;word-break:break-word;min-height:48px} -.fbe{color:var(--text-muted);font-style:italic} -.fbhint{margin-top:8px;font-size:var(--fs-xs);color:var(--text-muted)} .sdot{display:inline-block;width:8px;height:8px;border-radius:50%;margin-right:4px;vertical-align:0} .sdot.g{background:var(--dot-green)}.sdot.r{background:var(--dot-red)} @media(max-width:640px){ .main{flex-direction:column}.sidebar{width:100%;max-height:34vh;border-right:0;border-bottom:1px solid var(--border)} - .detail{padding:var(--sp-3);padding-bottom:130px}.fb{width:100%;border-left:0;border-radius:0} + .detail{padding:var(--sp-3)} .fi{min-height:44px}.sbtns{display:flex}.sbtns .sb{min-height:44px;flex:1} } `; @@ -166,16 +153,6 @@ const BODY = `

Select a feature to review

-
-
-
Feedback0
- ▲ expand -
-
-
Add comments or mark features to generate feedback…
-
Use Accept / Send review in the header to submit.
-
-
`; const JS = ` @@ -499,8 +476,8 @@ function testBadge(f){ return dot + (t.passing!=null && t.total!=null ? t.passing+'/'+t.total+' passing' : 'tests '+t.status); } function toggleNote(){ const el=document.getElementById('note-area'); if(el) el.classList.toggle('open'); } -function saveNote(name){ S.notes[name] = (document.getElementById('note-ta')||{}).value||''; updateFb(); renderSidebar(); selectFeature(name); } -function setSt(name, val){ S.statuses[name] = S.statuses[name]===val ? null : val; renderSidebar(); selectFeature(name); updateFb(); } +function saveNote(name){ S.notes[name] = (document.getElementById('note-ta')||{}).value||''; refresh(); renderSidebar(); selectFeature(name); } +function setSt(name, val){ S.statuses[name] = S.statuses[name]===val ? null : val; renderSidebar(); selectFeature(name); refresh(); } function toggleComment(row, cr, ta){ const wasOpen = cr.classList.contains('open'); document.querySelectorAll('.cr.open').forEach(r=>r.classList.remove('open')); @@ -512,7 +489,7 @@ function saveComment(id, feat, path, line, val){ if(t) S.comments[id] = { feature: feat.name==='__unc__'?'uncategorized':feat.name, file: path, content: (line.content||'').trim(), type: line.type, comment: t }; else delete S.comments[id]; - updateFb(); selectFeature(S.active); + refresh(); selectFeature(S.active); } function buildFeedbackObj(){ @@ -526,21 +503,6 @@ function buildFeedbackObj(){ ({ feature: c.feature, file: c.file, content: c.content, type: c.type, comment: c.comment })); return { type:'review-feedback', branch: D.branch||'HEAD', features, lineComments }; } -function updateFb(){ - const obj = buildFeedbackObj(); - const count = obj.features.length + obj.lineComments.length; - const fbo = document.getElementById('fbo'); const fbc = document.getElementById('fbc'); - if(!count){ fbo.innerHTML='Add comments or mark features to generate feedback…'; fbc.classList.remove('vis'); } - else { - fbo.textContent = JSON.stringify(obj, null, 2); - fbc.textContent = count; fbc.classList.add('vis'); - document.getElementById('fbpanel').classList.remove('col'); - document.getElementById('fbtog').textContent = '▼ collapse'; - } - refresh(); -} -function toggleFb(){ const p=document.getElementById('fbpanel'); const t=document.getElementById('fbtog'); p.classList.toggle('col'); t.textContent = p.classList.contains('col')?'▲ expand':'▼ collapse'; } - function hasChanges(){ const o=buildFeedbackObj(); return o.features.length>0 || o.lineComments.length>0; } function onPrimary(){ if(MODE==='commit' && !hasChanges()){ diff --git a/memory/features/show-active-work-in-work.md b/memory/features/show-active-work-in-work.md index c4a8b19..f42e61f 100644 --- a/memory/features/show-active-work-in-work.md +++ b/memory/features/show-active-work-in-work.md @@ -7,13 +7,14 @@ size: medium depends_on: [] files: ["lib/views/hub.mjs", "lib/views/planning.mjs", "lib/views/graph.mjs", "test/ui.test.mjs"] memories: [pitfalls/client-js-template-literal-escaping, architecture/workflow-state-ownership, patterns/safe-browser-rendering, decisions/backlog-planning-and-feature-waves, 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-17T16:26:20.623Z" +timestamp: "2026-07-17T16:26:25.040Z" tags: [] commits: - sha: e0b0453cbc024caa1cfdcbe079767214f4d8c6d8 kind: implement date: 2026-07-17 done: 2026-07-17 +reviewed: 2026-07-17 --- # Implementation notes @@ -29,3 +30,8 @@ const CH = D.features || [];\n// Readiness and plan stage arrive precomputed in # Blast radius The Planning and Work dashboard surfaces, including all feature-action controls and the graph's server-derived readiness display. + +# Review + +## 2026-07-17 +* **Approved** _(agent review: openai-codex/gpt-5.6-sol)_ — Approved: Work now owns the server-derived dependency graph, feature cards, execution controls, and feature cancellation; Planning retains backlog and lifecycle controls, responsive graph behavior is reused, and the full suite passes. diff --git a/memory/features/streamline-review-interface.md b/memory/features/streamline-review-interface.md index 3800ac6..09d8bb7 100644 --- a/memory/features/streamline-review-interface.md +++ b/memory/features/streamline-review-interface.md @@ -2,12 +2,12 @@ type: Feature title: Keep review controls fully readable description: Show complete feature titles in review and remove the unused Feedback panel. -status: pending +status: implemented size: medium depends_on: [] files: ["lib/views/review.mjs", "test/ui.test.mjs", "test/client-js-parse.test.mjs"] memories: [pitfalls/client-js-template-literal-escaping, patterns/safe-browser-rendering, decisions/backlog-planning-and-feature-waves, decisions/iterator-dashboard-feature-workflow, decisions/parallel-feature-waves-and-consolidated-review] -timestamp: "2026-07-17T16:17:17.904Z" +timestamp: "2026-07-17T16:29:00.013Z" tags: [] --- diff --git a/memory/log.md b/memory/log.md index 2bc26c9..f735546 100644 --- a/memory/log.md +++ b/memory/log.md @@ -1,6 +1,8 @@ # iterator update log ## 2026-07-17 +* **Implementation**: Committed feature(streamline-review-interface) on branch iterator/always-available-backlog; awaiting review. +* **Review**: Reviewed [Show active plan context on Work](/features/show-active-work-in-work.md); approved (agent). * **Review**: Accepted [Show active plan context on Work](/features/show-active-work-in-work.md) (committed as feature(show-active-work-in-work)). * **Implementation**: Committed feature(show-active-work-in-work) on branch iterator/always-available-backlog; awaiting review. * **Review**: Reviewed [Preserve reviews across Planning navigation](/features/preserve-review-across-planning.md); approved (agent). diff --git a/memory/state.md b/memory/state.md index 74b84e1..ca27e54 100644 --- a/memory/state.md +++ b/memory/state.md @@ -4,11 +4,11 @@ title: Runtime state description: Machine-owned iterator flow state — never hand-edited. mode: auto paused: false -phase: reviewing -active_feature: show-active-work-in-work +phase: implementing +active_feature: streamline-review-interface strikes: "{\"implement-ready-feature-wave\":2,\"always-available-backlog\":1,\"review-multiple-implemented-features\":1,\"preserve-review-across-planning\":1}" escalation: null -timestamp: 2026-07-17T16:26:06.658Z +timestamp: 2026-07-17T16:26:28.622Z --- Runtime flow state; read via gather, written only by the state op. diff --git a/memory/usage.md b/memory/usage.md index b286921..f793adb 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\":16969,\"output\":1048,\"cacheRead\":53248,\"cacheWrite\":0,\"turns\":5}},\"plan\":{\"openai-codex/gpt-5.6-terra\":{\"input\":61028,\"output\":5028,\"cacheRead\":668672,\"cacheWrite\":0,\"turns\":17}},\"implement\":{\"openai-codex/gpt-5.6-terra\":{\"input\":119901,\"output\":3970,\"cacheRead\":1700864,\"cacheWrite\":0,\"turns\":19},\"openai-codex/gpt-5.6-sol\":{\"input\":196441,\"output\":8227,\"cacheRead\":4274176,\"cacheWrite\":0,\"turns\":31}},\"review\":{\"openai-codex/gpt-5.6-sol\":{\"input\":282795,\"output\":1756,\"cacheRead\":733184,\"cacheWrite\":0,\"turns\":8}}},\"features\":{\"retire-plan\":{\"input\":16969,\"output\":1048,\"cacheRead\":53248,\"cacheWrite\":0,\"turns\":5},\"preserve-review-across-planning\":{\"input\":549855,\"output\":9490,\"cacheRead\":4442112,\"cacheWrite\":0,\"turns\":44},\"show-active-work-in-work\":{\"input\":49282,\"output\":4463,\"cacheRead\":2266112,\"cacheWrite\":0,\"turns\":14}}}" -timestamp: 2026-07-17T16:26:06.426Z +totals: "{\"steps\":{\"hub\":{\"openai-codex/gpt-5.6-terra\":{\"input\":16969,\"output\":1048,\"cacheRead\":53248,\"cacheWrite\":0,\"turns\":5}},\"plan\":{\"openai-codex/gpt-5.6-terra\":{\"input\":61028,\"output\":5028,\"cacheRead\":668672,\"cacheWrite\":0,\"turns\":17}},\"implement\":{\"openai-codex/gpt-5.6-terra\":{\"input\":119901,\"output\":3970,\"cacheRead\":1700864,\"cacheWrite\":0,\"turns\":19},\"openai-codex/gpt-5.6-sol\":{\"input\":196441,\"output\":8227,\"cacheRead\":4274176,\"cacheWrite\":0,\"turns\":31}},\"review\":{\"openai-codex/gpt-5.6-sol\":{\"input\":326661,\"output\":2132,\"cacheRead\":1419264,\"cacheWrite\":0,\"turns\":12}}},\"features\":{\"retire-plan\":{\"input\":16969,\"output\":1048,\"cacheRead\":53248,\"cacheWrite\":0,\"turns\":5},\"preserve-review-across-planning\":{\"input\":549855,\"output\":9490,\"cacheRead\":4442112,\"cacheWrite\":0,\"turns\":44},\"show-active-work-in-work\":{\"input\":93148,\"output\":4839,\"cacheRead\":2952192,\"cacheWrite\":0,\"turns\":18}}}" +timestamp: 2026-07-17T16:26:28.355Z --- # Usage @@ -31,7 +31,7 @@ timestamp: 2026-07-17T16:26:06.426Z | model | input | output | cache read | cache write | turns | | --- | ---: | ---: | ---: | ---: | ---: | -| openai-codex/gpt-5.6-sol | 282795 | 1756 | 733184 | 0 | 8 | +| openai-codex/gpt-5.6-sol | 326661 | 2132 | 1419264 | 0 | 12 | ## Per feature @@ -39,6 +39,6 @@ timestamp: 2026-07-17T16:26:06.426Z | --- | ---: | ---: | ---: | ---: | ---: | | retire-plan | 16969 | 1048 | 53248 | 0 | 5 | | preserve-review-across-planning | 549855 | 9490 | 4442112 | 0 | 44 | -| show-active-work-in-work | 49282 | 4463 | 2266112 | 0 | 14 | +| show-active-work-in-work | 93148 | 4839 | 2952192 | 0 | 18 | -Total: 677134 in / 20029 out / 7430144 cache-read / 0 cache-write over 80 turns. +Total: 721000 in / 20405 out / 8116224 cache-read / 0 cache-write over 84 turns. diff --git a/skills/iterator/lib/views/review.mjs b/skills/iterator/lib/views/review.mjs index 70c2f7a..690b59a 100644 --- a/skills/iterator/lib/views/review.mjs +++ b/skills/iterator/lib/views/review.mjs @@ -51,18 +51,18 @@ body{height:100vh;overflow:hidden;display:flex;flex-direction:column} .dot{width:8px;height:8px;border-radius:50%;margin-top:4px;flex-shrink:0} .dg{background:var(--dot-green)}.dy{background:var(--dot-yellow)}.dr{background:var(--dot-red)} .fm{flex:1;min-width:0} -.fn{font-size:13px;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis} +.fn{font-size:13px;font-weight:500;white-space:normal;overflow-wrap:anywhere} .fs{font-size:11px;color:var(--text-muted);margin-top:2px} .sa{color:var(--add-fg)}.sd{color:var(--del-fg)} .sbadge{font-size:10px;border-radius:3px;padding:1px 5px;margin-top:3px;display:inline-block} .s-app{background:var(--bg-green);color:var(--dot-green)} .s-chg{background:var(--bg-red);color:var(--dot-red)} .s-qst{background:var(--bg-yellow);color:var(--dot-yellow)} -.detail{flex:1;overflow-y:auto;padding:var(--sp-5);padding-bottom:130px} +.detail{flex:1;overflow-y:auto;padding:var(--sp-5)} .fc{overflow:visible} .fch{position:sticky;top:0;z-index:5;border-radius:var(--radius-card) var(--radius-card) 0 0} .fh{margin-bottom:var(--sp-4);padding-bottom:var(--sp-4);border-bottom:1px solid var(--border)} -.ftitle{font-family:var(--font-display);font-size:var(--fs-xl);font-weight:600;margin-bottom:6px} +.ftitle{font-family:var(--font-display);font-size:var(--fs-xl);font-weight:600;margin-bottom:6px;overflow-wrap:anywhere} .fdesc{color:var(--text-muted);font-size:var(--fs-sm);margin-bottom:12px} button.note-btn{font-size:12px;background:none;border:1px dashed var(--border);color:var(--text-muted);border-radius:4px;padding:3px 8px;cursor:pointer} button.note-btn:hover{border-color:var(--accent);color:var(--accent)} @@ -139,24 +139,11 @@ button.cs{background:var(--accent);border-color:var(--accent);color:var(--accent .muted{color:var(--text-muted)} .empty{text-align:center;padding:60px 20px;color:var(--text-muted)} .empty h3{font-size:16px;margin-bottom:8px} -.fb{position:fixed;bottom:0;right:0;width:400px;background:var(--fb-bg);border-top:1px solid var(--border); - border-left:1px solid var(--border);border-radius:8px 0 0 0;z-index:100;transition:transform .2s} -.fb.col{transform:translateY(calc(100% - 36px))} -.fbh{display:flex;align-items:center;justify-content:space-between;padding:8px 12px;cursor:pointer;user-select:none} -.fbt{font-size:13px;font-weight:500} -.fbc{font-size:var(--fs-xs);background:var(--accent);color:var(--accent-fg);border-radius:10px;padding:1px 7px;display:none} -.fbc.vis{display:inline} -.fbtog{font-size:11px;color:var(--text-muted)} -.fbb{padding:0 12px 12px} -.fbo{background:var(--bg);border:1px solid var(--border);border-radius:4px;padding:10px;font-family:var(--font-mono); - font-size:var(--fs-xs);color:var(--text);max-height:160px;overflow-y:auto;white-space:pre-wrap;word-break:break-word;min-height:48px} -.fbe{color:var(--text-muted);font-style:italic} -.fbhint{margin-top:8px;font-size:var(--fs-xs);color:var(--text-muted)} .sdot{display:inline-block;width:8px;height:8px;border-radius:50%;margin-right:4px;vertical-align:0} .sdot.g{background:var(--dot-green)}.sdot.r{background:var(--dot-red)} @media(max-width:640px){ .main{flex-direction:column}.sidebar{width:100%;max-height:34vh;border-right:0;border-bottom:1px solid var(--border)} - .detail{padding:var(--sp-3);padding-bottom:130px}.fb{width:100%;border-left:0;border-radius:0} + .detail{padding:var(--sp-3)} .fi{min-height:44px}.sbtns{display:flex}.sbtns .sb{min-height:44px;flex:1} } `; @@ -166,16 +153,6 @@ const BODY = `

Select a feature to review

-
-
-
Feedback0
- ▲ expand -
-
-
Add comments or mark features to generate feedback…
-
Use Accept / Send review in the header to submit.
-
-
`; const JS = ` @@ -499,8 +476,8 @@ function testBadge(f){ return dot + (t.passing!=null && t.total!=null ? t.passing+'/'+t.total+' passing' : 'tests '+t.status); } function toggleNote(){ const el=document.getElementById('note-area'); if(el) el.classList.toggle('open'); } -function saveNote(name){ S.notes[name] = (document.getElementById('note-ta')||{}).value||''; updateFb(); renderSidebar(); selectFeature(name); } -function setSt(name, val){ S.statuses[name] = S.statuses[name]===val ? null : val; renderSidebar(); selectFeature(name); updateFb(); } +function saveNote(name){ S.notes[name] = (document.getElementById('note-ta')||{}).value||''; refresh(); renderSidebar(); selectFeature(name); } +function setSt(name, val){ S.statuses[name] = S.statuses[name]===val ? null : val; renderSidebar(); selectFeature(name); refresh(); } function toggleComment(row, cr, ta){ const wasOpen = cr.classList.contains('open'); document.querySelectorAll('.cr.open').forEach(r=>r.classList.remove('open')); @@ -512,7 +489,7 @@ function saveComment(id, feat, path, line, val){ if(t) S.comments[id] = { feature: feat.name==='__unc__'?'uncategorized':feat.name, file: path, content: (line.content||'').trim(), type: line.type, comment: t }; else delete S.comments[id]; - updateFb(); selectFeature(S.active); + refresh(); selectFeature(S.active); } function buildFeedbackObj(){ @@ -526,21 +503,6 @@ function buildFeedbackObj(){ ({ feature: c.feature, file: c.file, content: c.content, type: c.type, comment: c.comment })); return { type:'review-feedback', branch: D.branch||'HEAD', features, lineComments }; } -function updateFb(){ - const obj = buildFeedbackObj(); - const count = obj.features.length + obj.lineComments.length; - const fbo = document.getElementById('fbo'); const fbc = document.getElementById('fbc'); - if(!count){ fbo.innerHTML='Add comments or mark features to generate feedback…'; fbc.classList.remove('vis'); } - else { - fbo.textContent = JSON.stringify(obj, null, 2); - fbc.textContent = count; fbc.classList.add('vis'); - document.getElementById('fbpanel').classList.remove('col'); - document.getElementById('fbtog').textContent = '▼ collapse'; - } - refresh(); -} -function toggleFb(){ const p=document.getElementById('fbpanel'); const t=document.getElementById('fbtog'); p.classList.toggle('col'); t.textContent = p.classList.contains('col')?'▲ expand':'▼ collapse'; } - function hasChanges(){ const o=buildFeedbackObj(); return o.features.length>0 || o.lineComments.length>0; } function onPrimary(){ if(MODE==='commit' && !hasChanges()){ diff --git a/test/client-js-parse.test.mjs b/test/client-js-parse.test.mjs index 67f6a5d..7eeafb6 100644 --- a/test/client-js-parse.test.mjs +++ b/test/client-js-parse.test.mjs @@ -63,7 +63,20 @@ const MIN_DATA = { }, plan: { branch: "main", title: "P", plan: {}, knowledge: {} }, question: { branch: "main", title: "Q", question: { text: "?" } }, - review: { branch: "main", plan: "P", features: [], mode: "review" }, + review: { + branch: "main", + plan: "P", + mode: "review", + features: [ + { + name: "a-feature-name-that-must-remain-fully-readable-in-the-sidebar", + description: "Long labels wrap without changing review behavior.", + dependsOn: [], + stats: { added: 0, removed: 0, files: 0, complexity: "green" }, + files: [], + }, + ], + }, settings: { branch: "main", plan: "P", settings: {}, schema: [] }, test: { branch: "main", feature: { name: "f" }, tests: [] }, usage: { branch: "main", plan: "P", usage: { steps: [] } }, diff --git a/test/ui.test.mjs b/test/ui.test.mjs index 9873b0a..f29a588 100644 --- a/test/ui.test.mjs +++ b/test/ui.test.mjs @@ -520,6 +520,14 @@ test("review view groups files by Declared/Tests/Incidental with pre-seeded disp assert.match(html, /review all/); assert.match(html, /@media\(max-width:640px\)/); assert.match(html, /\.main\{flex-direction:column\}/); + // Long feature labels wrap instead of being ellipsized at any breakpoint. + assert.match(html, /\.fn\{[^}]*white-space:normal;overflow-wrap:anywhere/); + assert.doesNotMatch(html, /\.fn\{[^}]*text-overflow:ellipsis/); + // Feedback still submits through the header; the obsolete fixed panel and + // its JSON-preview bookkeeping are gone. + assert.doesNotMatch(html, /id="fbpanel"/); + assert.doesNotMatch(html, /toggleFb|updateFb/); + assert.match(html, /post\(buildFeedbackObj\(\), 'Review sent to Claude'\)/); }); test("planning hero goal box persists an unsent draft and clears it on plan start", async () => { From 9b0d606a504186e7c56389e09bdbf25e89ae420f Mon Sep 17 00:00:00 2001 From: Christoph Maier Date: Fri, 17 Jul 2026 18:29:00 +0200 Subject: [PATCH 10/22] chore(iterator): record feature commit --- memory/features/streamline-review-interface.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/memory/features/streamline-review-interface.md b/memory/features/streamline-review-interface.md index 09d8bb7..6ac88f2 100644 --- a/memory/features/streamline-review-interface.md +++ b/memory/features/streamline-review-interface.md @@ -7,8 +7,12 @@ size: medium depends_on: [] files: ["lib/views/review.mjs", "test/ui.test.mjs", "test/client-js-parse.test.mjs"] memories: [pitfalls/client-js-template-literal-escaping, patterns/safe-browser-rendering, decisions/backlog-planning-and-feature-waves, decisions/iterator-dashboard-feature-workflow, decisions/parallel-feature-waves-and-consolidated-review] -timestamp: "2026-07-17T16:29:00.013Z" +timestamp: "2026-07-17T16:29:00.112Z" tags: [] +commits: + - sha: e7d5824e2ac431640e6a1abc6169dfcc6121b43b + kind: implement + date: 2026-07-17 --- # Implementation notes From 216f360213779e50540781584e9399016a208a07 Mon Sep 17 00:00:00 2001 From: Christoph Maier Date: Fri, 17 Jul 2026 18:29:22 +0200 Subject: [PATCH 11/22] chore(iterator): record feature commits and memory updates --- memory/features/index.md | 2 +- memory/features/streamline-review-interface.md | 5 +++-- memory/log.md | 1 + memory/state.md | 4 ++-- memory/usage.md | 9 +++++---- 5 files changed, 12 insertions(+), 9 deletions(-) diff --git a/memory/features/index.md b/memory/features/index.md index 20a75a8..6a179cb 100644 --- a/memory/features/index.md +++ b/memory/features/index.md @@ -2,4 +2,4 @@ * [Preserve reviews across Planning navigation](preserve-review-across-planning.md) - ✅ done · medium · Keep an active review open while users manage backlog items on the Planning tab and return to it. * [Show active plan context on Work](show-active-work-in-work.md) - ✅ done · medium · Make Work the home for the active plan, its feature set, and the dependency graph. -* [Keep review controls fully readable](streamline-review-interface.md) - ⬜ pending · medium · Show complete feature titles in review and remove the unused Feedback panel. +* [Keep review controls fully readable](streamline-review-interface.md) - ✅ done · medium · Show complete feature titles in review and remove the unused Feedback panel. diff --git a/memory/features/streamline-review-interface.md b/memory/features/streamline-review-interface.md index 6ac88f2..8ad469e 100644 --- a/memory/features/streamline-review-interface.md +++ b/memory/features/streamline-review-interface.md @@ -2,17 +2,18 @@ type: Feature title: Keep review controls fully readable description: Show complete feature titles in review and remove the unused Feedback panel. -status: implemented +status: done size: medium depends_on: [] files: ["lib/views/review.mjs", "test/ui.test.mjs", "test/client-js-parse.test.mjs"] memories: [pitfalls/client-js-template-literal-escaping, patterns/safe-browser-rendering, decisions/backlog-planning-and-feature-waves, decisions/iterator-dashboard-feature-workflow, decisions/parallel-feature-waves-and-consolidated-review] -timestamp: "2026-07-17T16:29:00.112Z" +timestamp: "2026-07-17T16:29:22.606Z" tags: [] commits: - sha: e7d5824e2ac431640e6a1abc6169dfcc6121b43b kind: implement date: 2026-07-17 +done: 2026-07-17 --- # Implementation notes diff --git a/memory/log.md b/memory/log.md index f735546..285ca03 100644 --- a/memory/log.md +++ b/memory/log.md @@ -1,6 +1,7 @@ # iterator update log ## 2026-07-17 +* **Review**: Accepted [Keep review controls fully readable](/features/streamline-review-interface.md) (committed as feature(streamline-review-interface)). * **Implementation**: Committed feature(streamline-review-interface) on branch iterator/always-available-backlog; awaiting review. * **Review**: Reviewed [Show active plan context on Work](/features/show-active-work-in-work.md); approved (agent). * **Review**: Accepted [Show active plan context on Work](/features/show-active-work-in-work.md) (committed as feature(show-active-work-in-work)). diff --git a/memory/state.md b/memory/state.md index ca27e54..2553d51 100644 --- a/memory/state.md +++ b/memory/state.md @@ -4,11 +4,11 @@ title: Runtime state description: Machine-owned iterator flow state — never hand-edited. mode: auto paused: false -phase: implementing +phase: reviewing active_feature: streamline-review-interface strikes: "{\"implement-ready-feature-wave\":2,\"always-available-backlog\":1,\"review-multiple-implemented-features\":1,\"preserve-review-across-planning\":1}" escalation: null -timestamp: 2026-07-17T16:26:28.622Z +timestamp: 2026-07-17T16:29:12.961Z --- Runtime flow state; read via gather, written only by the state op. diff --git a/memory/usage.md b/memory/usage.md index f793adb..213936b 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\":16969,\"output\":1048,\"cacheRead\":53248,\"cacheWrite\":0,\"turns\":5}},\"plan\":{\"openai-codex/gpt-5.6-terra\":{\"input\":61028,\"output\":5028,\"cacheRead\":668672,\"cacheWrite\":0,\"turns\":17}},\"implement\":{\"openai-codex/gpt-5.6-terra\":{\"input\":119901,\"output\":3970,\"cacheRead\":1700864,\"cacheWrite\":0,\"turns\":19},\"openai-codex/gpt-5.6-sol\":{\"input\":196441,\"output\":8227,\"cacheRead\":4274176,\"cacheWrite\":0,\"turns\":31}},\"review\":{\"openai-codex/gpt-5.6-sol\":{\"input\":326661,\"output\":2132,\"cacheRead\":1419264,\"cacheWrite\":0,\"turns\":12}}},\"features\":{\"retire-plan\":{\"input\":16969,\"output\":1048,\"cacheRead\":53248,\"cacheWrite\":0,\"turns\":5},\"preserve-review-across-planning\":{\"input\":549855,\"output\":9490,\"cacheRead\":4442112,\"cacheWrite\":0,\"turns\":44},\"show-active-work-in-work\":{\"input\":93148,\"output\":4839,\"cacheRead\":2952192,\"cacheWrite\":0,\"turns\":18}}}" -timestamp: 2026-07-17T16:26:28.355Z +totals: "{\"steps\":{\"hub\":{\"openai-codex/gpt-5.6-terra\":{\"input\":16969,\"output\":1048,\"cacheRead\":53248,\"cacheWrite\":0,\"turns\":5}},\"plan\":{\"openai-codex/gpt-5.6-terra\":{\"input\":61028,\"output\":5028,\"cacheRead\":668672,\"cacheWrite\":0,\"turns\":17}},\"implement\":{\"openai-codex/gpt-5.6-terra\":{\"input\":119901,\"output\":3970,\"cacheRead\":1700864,\"cacheWrite\":0,\"turns\":19},\"openai-codex/gpt-5.6-sol\":{\"input\":239553,\"output\":12098,\"cacheRead\":8177664,\"cacheWrite\":0,\"turns\":51}},\"review\":{\"openai-codex/gpt-5.6-sol\":{\"input\":326661,\"output\":2132,\"cacheRead\":1419264,\"cacheWrite\":0,\"turns\":12}}},\"features\":{\"retire-plan\":{\"input\":16969,\"output\":1048,\"cacheRead\":53248,\"cacheWrite\":0,\"turns\":5},\"preserve-review-across-planning\":{\"input\":549855,\"output\":9490,\"cacheRead\":4442112,\"cacheWrite\":0,\"turns\":44},\"show-active-work-in-work\":{\"input\":93148,\"output\":4839,\"cacheRead\":2952192,\"cacheWrite\":0,\"turns\":18},\"streamline-review-interface\":{\"input\":43112,\"output\":3871,\"cacheRead\":3903488,\"cacheWrite\":0,\"turns\":20}}}" +timestamp: 2026-07-17T16:29:12.710Z --- # Usage @@ -25,7 +25,7 @@ timestamp: 2026-07-17T16:26:28.355Z | model | input | output | cache read | cache write | turns | | --- | ---: | ---: | ---: | ---: | ---: | | openai-codex/gpt-5.6-terra | 119901 | 3970 | 1700864 | 0 | 19 | -| openai-codex/gpt-5.6-sol | 196441 | 8227 | 4274176 | 0 | 31 | +| openai-codex/gpt-5.6-sol | 239553 | 12098 | 8177664 | 0 | 51 | ## review @@ -40,5 +40,6 @@ timestamp: 2026-07-17T16:26:28.355Z | retire-plan | 16969 | 1048 | 53248 | 0 | 5 | | preserve-review-across-planning | 549855 | 9490 | 4442112 | 0 | 44 | | show-active-work-in-work | 93148 | 4839 | 2952192 | 0 | 18 | +| streamline-review-interface | 43112 | 3871 | 3903488 | 0 | 20 | -Total: 721000 in / 20405 out / 8116224 cache-read / 0 cache-write over 84 turns. +Total: 764112 in / 24276 out / 12019712 cache-read / 0 cache-write over 104 turns. From c5b75e3e4b034bc80a1600e57249e162639d32a8 Mon Sep 17 00:00:00 2001 From: Christoph Maier Date: Fri, 17 Jul 2026 19:30:23 +0200 Subject: [PATCH 12/22] implemented bug fixes --- memory/decisions/index.md | 1 + .../review-navigation-and-work-context.md | 28 ++++++++++++ .../index.md | 0 .../plan.md | 19 +++++++- .../preserve-review-across-planning.md | 0 .../show-active-work-in-work.md | 0 .../streamline-review-interface.md | 8 +++- .../usage.md | 45 +++++++++++++++++++ memory/index.md | 2 - memory/log.md | 3 ++ memory/state.md | 6 +-- memory/usage.md | 32 +++---------- test/session-server.test.mjs | 6 ++- 13 files changed, 115 insertions(+), 35 deletions(-) create mode 100644 memory/decisions/review-navigation-and-work-context.md rename memory/features/{ => archive/2026-07-17-review-navigation-and-work-context}/index.md (100%) rename memory/{ => features/archive/2026-07-17-review-navigation-and-work-context}/plan.md (67%) rename memory/features/{ => archive/2026-07-17-review-navigation-and-work-context}/preserve-review-across-planning.md (100%) rename memory/features/{ => archive/2026-07-17-review-navigation-and-work-context}/show-active-work-in-work.md (100%) rename memory/features/{ => archive/2026-07-17-review-navigation-and-work-context}/streamline-review-interface.md (78%) create mode 100644 memory/features/archive/2026-07-17-review-navigation-and-work-context/usage.md diff --git a/memory/decisions/index.md b/memory/decisions/index.md index f8b2944..147e080 100644 --- a/memory/decisions/index.md +++ b/memory/decisions/index.md @@ -8,6 +8,7 @@ Durable product and implementation choices agents should preserve. * [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. +* [Review navigation and Work context](/decisions/review-navigation-and-work-context.md) - Keep interactive reviews stable across dashboard navigation, make Work the active feature surface, and simplify review controls without weakening the review gate. * [Sync shared libs into droppable skills](/decisions/synced-droppable-skill-libs.md) - Shared code is developed in root lib/ and copied into skill folders so skills work when installed together or copied manually. * [Unify Iterator dashboard and feature workflow](/decisions/iterator-dashboard-feature-workflow.md) - Dashboard workflows keep backlog candidates separate from active work until selected candidates are consumed by approved plan creation. * [Use an OKF markdown bundle in target repos](/decisions/okf-markdown-bundle.md) - Project memory is stored as markdown plus YAML frontmatter in each target repo instead of in an external database. diff --git a/memory/decisions/review-navigation-and-work-context.md b/memory/decisions/review-navigation-and-work-context.md new file mode 100644 index 0000000..98b5659 --- /dev/null +++ b/memory/decisions/review-navigation-and-work-context.md @@ -0,0 +1,28 @@ +--- +type: Decision +title: Review navigation and Work context +description: Keep interactive reviews stable across dashboard navigation, make Work the active feature surface, and simplify review controls without weakening the review gate. +status: accepted +date: 2026-07-17 +tags: [workflow, review, dashboard, navigation] +files: ["lib/session-server.mjs", "lib/ui.mjs", "lib/views/hub.mjs", "lib/views/planning.mjs", "lib/views/graph.mjs", "lib/views/review.mjs", "test/session-server.test.mjs", "test/ui.test.mjs", "test/client-js-parse.test.mjs"] +timestamp: 2026-07-17T16:38:22.809Z +--- + +## Decision + +The persistent session shell, not its replaceable iframe views, owns unload cancellation. Switching between Planning and Work must preserve a pending review round and its single eventual result; an explicit cancel still pre-empts any pending reload-grace timer. + +Work is the home for an active plan's progress, dependency graph, feature cards, execution controls, and feature cancellation. Planning remains focused on backlog collection and plan lifecycle actions. Both surfaces render the same server-derived gather data and never reconstruct readiness or lifecycle state locally. + +The review sidebar and detail headings wrap long feature names rather than clipping them. The obsolete lower-right Feedback panel and JSON-preview bookkeeping are removed; feature verdicts, notes, and line comments continue to drive the existing header submission flow. + +## Constraints + +Dashboard navigation must not weaken the single-model-flow guard or backlog-write allowance. Changes to root `lib/` are synchronized into shipped skill copies, and inline view scripts retain parse coverage. + +# Retired plan + +Condensed from plan "Keep active review stable and clarify Planning versus Work" (3 features, archived under /features/archive/2026-07-17-review-navigation-and-work-context/). + +Token usage: 817664 in / 25689 out / 13951488 cache-read / 0 cache-write over 113 turns (per-step breakdown in the archived usage.md). diff --git a/memory/features/index.md b/memory/features/archive/2026-07-17-review-navigation-and-work-context/index.md similarity index 100% rename from memory/features/index.md rename to memory/features/archive/2026-07-17-review-navigation-and-work-context/index.md diff --git a/memory/plan.md b/memory/features/archive/2026-07-17-review-navigation-and-work-context/plan.md similarity index 67% rename from memory/plan.md rename to memory/features/archive/2026-07-17-review-navigation-and-work-context/plan.md index a344d28..9f3e1b8 100644 --- a/memory/plan.md +++ b/memory/features/archive/2026-07-17-review-navigation-and-work-context/plan.md @@ -5,7 +5,8 @@ description: Preserve active reviews across Planning navigation, move active wor status: approved branch: iterator/always-available-backlog created: 2026-07-17 -timestamp: 2026-07-17T16:15:52.935Z +timestamp: "2026-07-17T16:30:16.999Z" +plan_reviewed: 2026-07-17 --- # Goal @@ -38,3 +39,19 @@ Prevent an in-progress review from being aborted when users visit Planning to ma * [Preserve reviews across Planning navigation](/features/preserve-review-across-planning.md) - Keep an active review open while users manage backlog items on the Planning tab and return to it. * [Show active plan context on Work](/features/show-active-work-in-work.md) - Make Work the home for the active plan, its feature set, and the dependency graph. * [Keep review controls fully readable](/features/streamline-review-interface.md) - Show complete feature titles in review and remove the unused Feedback panel. + +# Plan review + +## 2026-07-17 _(agent review: openai-codex/gpt-5.6-sol)_ + +## Goal and architecture coverage + +The four requested outcomes are implemented: `preserve-review-across-planning` gives the persistent shell deterministic cancellation ownership and covers Review → Planning → Work; `show-active-work-in-work` places the server-derived feature graph and active feature controls on Work while Planning keeps backlog/lifecycle management; `streamline-review-interface` wraps long review labels and removes the lower-right Feedback panel while retaining header submission. Canonical `lib/` changes are synchronized to shipped copies, and all 348 tests pass. + +## Decisions and scope + +The implementation preserves the single pending review result, keeps backlog/model-flow guards intact, consumes the selected backlog candidates through plan approval, renders gathered state rather than deriving lifecycle rules in views, and retains responsive horizontal graph overflow and wrapped review labels. No unexplained functional scope drift or introduced TODO was found. + +## Loose end + +- **Working-tree cleanup — `preserve-review-across-planning`, `test/session-server.test.mjs`:** the accepted feature's navigation assertion has an uncommitted formatting-only change (the single-line `assert.equal` is expanded across lines). It does not alter behavior and the suite passes, but it is outside the recorded feature commits; discard or commit it before retiring the plan so the working tree is clean. diff --git a/memory/features/preserve-review-across-planning.md b/memory/features/archive/2026-07-17-review-navigation-and-work-context/preserve-review-across-planning.md similarity index 100% rename from memory/features/preserve-review-across-planning.md rename to memory/features/archive/2026-07-17-review-navigation-and-work-context/preserve-review-across-planning.md diff --git a/memory/features/show-active-work-in-work.md b/memory/features/archive/2026-07-17-review-navigation-and-work-context/show-active-work-in-work.md similarity index 100% rename from memory/features/show-active-work-in-work.md rename to memory/features/archive/2026-07-17-review-navigation-and-work-context/show-active-work-in-work.md diff --git a/memory/features/streamline-review-interface.md b/memory/features/archive/2026-07-17-review-navigation-and-work-context/streamline-review-interface.md similarity index 78% rename from memory/features/streamline-review-interface.md rename to memory/features/archive/2026-07-17-review-navigation-and-work-context/streamline-review-interface.md index 8ad469e..8acd36e 100644 --- a/memory/features/streamline-review-interface.md +++ b/memory/features/archive/2026-07-17-review-navigation-and-work-context/streamline-review-interface.md @@ -7,13 +7,14 @@ size: medium depends_on: [] files: ["lib/views/review.mjs", "test/ui.test.mjs", "test/client-js-parse.test.mjs"] memories: [pitfalls/client-js-template-literal-escaping, patterns/safe-browser-rendering, decisions/backlog-planning-and-feature-waves, decisions/iterator-dashboard-feature-workflow, decisions/parallel-feature-waves-and-consolidated-review] -timestamp: "2026-07-17T16:29:22.606Z" +timestamp: "2026-07-17T16:29:28.160Z" tags: [] commits: - sha: e7d5824e2ac431640e6a1abc6169dfcc6121b43b kind: implement date: 2026-07-17 done: 2026-07-17 +reviewed: 2026-07-17 --- # Implementation notes @@ -33,3 +34,8 @@ Replace sidebar title truncation with responsive wrapping or overflow that prese # Blast radius The review sidebar and narrow-screen review layout; reviewer feedback submission must continue to use the existing header controls and collected feature/line comments. + +# Review + +## 2026-07-17 +* **Approved** _(agent review: openai-codex/gpt-5.6-sol)_ — Approved: review sidebar and detail labels now wrap without truncation, the obsolete fixed Feedback panel and preview bookkeeping are removed, header submission still derives from feature and line-comment state, and UI/client-script/full-suite tests pass. diff --git a/memory/features/archive/2026-07-17-review-navigation-and-work-context/usage.md b/memory/features/archive/2026-07-17-review-navigation-and-work-context/usage.md new file mode 100644 index 0000000..5fa5f86 --- /dev/null +++ b/memory/features/archive/2026-07-17-review-navigation-and-work-context/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\":16969,\"output\":1048,\"cacheRead\":53248,\"cacheWrite\":0,\"turns\":5}},\"plan\":{\"openai-codex/gpt-5.6-terra\":{\"input\":61028,\"output\":5028,\"cacheRead\":668672,\"cacheWrite\":0,\"turns\":17}},\"implement\":{\"openai-codex/gpt-5.6-terra\":{\"input\":119901,\"output\":3970,\"cacheRead\":1700864,\"cacheWrite\":0,\"turns\":19},\"openai-codex/gpt-5.6-sol\":{\"input\":239553,\"output\":12098,\"cacheRead\":8177664,\"cacheWrite\":0,\"turns\":51}},\"review\":{\"openai-codex/gpt-5.6-sol\":{\"input\":380213,\"output\":3545,\"cacheRead\":3351040,\"cacheWrite\":0,\"turns\":21}}},\"features\":{\"retire-plan\":{\"input\":16969,\"output\":1048,\"cacheRead\":53248,\"cacheWrite\":0,\"turns\":5},\"preserve-review-across-planning\":{\"input\":549855,\"output\":9490,\"cacheRead\":4442112,\"cacheWrite\":0,\"turns\":44},\"show-active-work-in-work\":{\"input\":93148,\"output\":4839,\"cacheRead\":2952192,\"cacheWrite\":0,\"turns\":18},\"streamline-review-interface\":{\"input\":75009,\"output\":4165,\"cacheRead\":4723712,\"cacheWrite\":0,\"turns\":24}}}" +timestamp: 2026-07-17T16:30:23.274Z +--- + +# Usage + +## hub + +| model | input | output | cache read | cache write | turns | +| --- | ---: | ---: | ---: | ---: | ---: | +| openai-codex/gpt-5.6-terra | 16969 | 1048 | 53248 | 0 | 5 | + +## plan + +| model | input | output | cache read | cache write | turns | +| --- | ---: | ---: | ---: | ---: | ---: | +| openai-codex/gpt-5.6-terra | 61028 | 5028 | 668672 | 0 | 17 | + +## implement + +| model | input | output | cache read | cache write | turns | +| --- | ---: | ---: | ---: | ---: | ---: | +| openai-codex/gpt-5.6-terra | 119901 | 3970 | 1700864 | 0 | 19 | +| openai-codex/gpt-5.6-sol | 239553 | 12098 | 8177664 | 0 | 51 | + +## review + +| model | input | output | cache read | cache write | turns | +| --- | ---: | ---: | ---: | ---: | ---: | +| openai-codex/gpt-5.6-sol | 380213 | 3545 | 3351040 | 0 | 21 | + +## Per feature + +| feature | input | output | cache read | cache write | turns | +| --- | ---: | ---: | ---: | ---: | ---: | +| retire-plan | 16969 | 1048 | 53248 | 0 | 5 | +| preserve-review-across-planning | 549855 | 9490 | 4442112 | 0 | 44 | +| show-active-work-in-work | 93148 | 4839 | 2952192 | 0 | 18 | +| streamline-review-interface | 75009 | 4165 | 4723712 | 0 | 24 | + +Total: 817664 in / 25689 out / 13951488 cache-read / 0 cache-write over 113 turns. diff --git a/memory/index.md b/memory/index.md index b8d16f2..1f5f167 100644 --- a/memory/index.md +++ b/memory/index.md @@ -10,8 +10,6 @@ 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). -* [Features](features/) - One document per implementation feature. -* [Plan](plan.md) - Preserve active reviews across Planning navigation, move active work context to Work, and polish 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 285ca03..399b8f7 100644 --- a/memory/log.md +++ b/memory/log.md @@ -1,6 +1,9 @@ # iterator update log ## 2026-07-17 +* **Retirement**: Plan "Keep active review stable and clarify Planning versus Work" condensed into [Review navigation and Work context](/decisions/review-navigation-and-work-context.md). +* **Plan review**: Whole-plan review recorded (agent). +* **Review**: Reviewed [Keep review controls fully readable](/features/streamline-review-interface.md); approved (agent). * **Review**: Accepted [Keep review controls fully readable](/features/streamline-review-interface.md) (committed as feature(streamline-review-interface)). * **Implementation**: Committed feature(streamline-review-interface) on branch iterator/always-available-backlog; awaiting review. * **Review**: Reviewed [Show active plan context on Work](/features/show-active-work-in-work.md); approved (agent). diff --git a/memory/state.md b/memory/state.md index 2553d51..d25d7d6 100644 --- a/memory/state.md +++ b/memory/state.md @@ -4,11 +4,11 @@ title: Runtime state description: Machine-owned iterator flow state — never hand-edited. mode: auto paused: false -phase: reviewing -active_feature: streamline-review-interface +phase: done +active_feature: null strikes: "{\"implement-ready-feature-wave\":2,\"always-available-backlog\":1,\"review-multiple-implemented-features\":1,\"preserve-review-across-planning\":1}" escalation: null -timestamp: 2026-07-17T16:29:12.961Z +timestamp: 2026-07-17T16:30:23.518Z --- Runtime flow state; read via gather, written only by the state op. diff --git a/memory/usage.md b/memory/usage.md index 213936b..96b7e2b 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\":16969,\"output\":1048,\"cacheRead\":53248,\"cacheWrite\":0,\"turns\":5}},\"plan\":{\"openai-codex/gpt-5.6-terra\":{\"input\":61028,\"output\":5028,\"cacheRead\":668672,\"cacheWrite\":0,\"turns\":17}},\"implement\":{\"openai-codex/gpt-5.6-terra\":{\"input\":119901,\"output\":3970,\"cacheRead\":1700864,\"cacheWrite\":0,\"turns\":19},\"openai-codex/gpt-5.6-sol\":{\"input\":239553,\"output\":12098,\"cacheRead\":8177664,\"cacheWrite\":0,\"turns\":51}},\"review\":{\"openai-codex/gpt-5.6-sol\":{\"input\":326661,\"output\":2132,\"cacheRead\":1419264,\"cacheWrite\":0,\"turns\":12}}},\"features\":{\"retire-plan\":{\"input\":16969,\"output\":1048,\"cacheRead\":53248,\"cacheWrite\":0,\"turns\":5},\"preserve-review-across-planning\":{\"input\":549855,\"output\":9490,\"cacheRead\":4442112,\"cacheWrite\":0,\"turns\":44},\"show-active-work-in-work\":{\"input\":93148,\"output\":4839,\"cacheRead\":2952192,\"cacheWrite\":0,\"turns\":18},\"streamline-review-interface\":{\"input\":43112,\"output\":3871,\"cacheRead\":3903488,\"cacheWrite\":0,\"turns\":20}}}" -timestamp: 2026-07-17T16:29:12.710Z +totals: "{\"steps\":{\"hub\":{\"openai-codex/gpt-5.6-terra\":{\"input\":170439,\"output\":1029,\"cacheRead\":1018880,\"cacheWrite\":0,\"turns\":5}}},\"features\":{\"retire-plan\":{\"input\":170439,\"output\":1029,\"cacheRead\":1018880,\"cacheWrite\":0,\"turns\":5}}}" +timestamp: 2026-07-17T16:38:28.072Z --- # Usage @@ -12,34 +12,12 @@ timestamp: 2026-07-17T16:29:12.710Z | model | input | output | cache read | cache write | turns | | --- | ---: | ---: | ---: | ---: | ---: | -| openai-codex/gpt-5.6-terra | 16969 | 1048 | 53248 | 0 | 5 | - -## plan - -| model | input | output | cache read | cache write | turns | -| --- | ---: | ---: | ---: | ---: | ---: | -| openai-codex/gpt-5.6-terra | 61028 | 5028 | 668672 | 0 | 17 | - -## implement - -| model | input | output | cache read | cache write | turns | -| --- | ---: | ---: | ---: | ---: | ---: | -| openai-codex/gpt-5.6-terra | 119901 | 3970 | 1700864 | 0 | 19 | -| openai-codex/gpt-5.6-sol | 239553 | 12098 | 8177664 | 0 | 51 | - -## review - -| model | input | output | cache read | cache write | turns | -| --- | ---: | ---: | ---: | ---: | ---: | -| openai-codex/gpt-5.6-sol | 326661 | 2132 | 1419264 | 0 | 12 | +| openai-codex/gpt-5.6-terra | 170439 | 1029 | 1018880 | 0 | 5 | ## Per feature | feature | input | output | cache read | cache write | turns | | --- | ---: | ---: | ---: | ---: | ---: | -| retire-plan | 16969 | 1048 | 53248 | 0 | 5 | -| preserve-review-across-planning | 549855 | 9490 | 4442112 | 0 | 44 | -| show-active-work-in-work | 93148 | 4839 | 2952192 | 0 | 18 | -| streamline-review-interface | 43112 | 3871 | 3903488 | 0 | 20 | +| retire-plan | 170439 | 1029 | 1018880 | 0 | 5 | -Total: 764112 in / 24276 out / 12019712 cache-read / 0 cache-write over 104 turns. +Total: 170439 in / 1029 out / 1018880 cache-read / 0 cache-write over 5 turns. diff --git a/test/session-server.test.mjs b/test/session-server.test.mjs index 0024cc9..94eac9c 100644 --- a/test/session-server.test.mjs +++ b/test/session-server.test.mjs @@ -567,7 +567,11 @@ test("review survives Planning navigation and can submit after returning to Work await (await fetch(origin + "/view?tab=work")).text(), /ACTIVE-REVIEW/, ); - assert.equal(srvMod.RUN_ID, reviewRun, "navigation preserves round identity"); + assert.equal( + srvMod.RUN_ID, + reviewRun, + "navigation preserves round identity", + ); assert.equal(session.hasPending(), true, "review remains pending"); const response = await fetch(`${origin}/submit?r=${reviewRun}`, { From ff508332da76a5774144131216caa81e069e2abf Mon Sep 17 00:00:00 2001 From: Christoph Maier Date: Fri, 17 Jul 2026 19:33:04 +0200 Subject: [PATCH 13/22] updated memroy --- memory/architecture/browser-server-contract.md | 6 ++++-- memory/architecture/index.md | 2 +- memory/index.md | 2 +- memory/log.md | 3 +++ memory/pitfalls/cancel-now-after-grace-timer.md | 12 ++++++------ memory/pitfalls/index.md | 2 +- memory/usage.md | 12 +++++++++--- 7 files changed, 25 insertions(+), 14 deletions(-) diff --git a/memory/architecture/browser-server-contract.md b/memory/architecture/browser-server-contract.md index e385615..a1f358c 100644 --- a/memory/architecture/browser-server-contract.md +++ b/memory/architecture/browser-server-contract.md @@ -1,7 +1,7 @@ --- type: Architecture title: Browser server contract -description: Interactive workflows run a local server that receives a JSON payload on stdin and returns exactly one JSON result on stdout. +description: "Interactive workflows use a one-result server contract; Pi's persistent shell owns cancellation across iframe navigation." tags: - browser-ui - server @@ -11,7 +11,7 @@ files: - lib/ui.mjs - lib/session-server.mjs - skills/iterator/server.mjs -timestamp: "2026-07-16T15:03:36.830Z" +timestamp: 2026-07-17T17:31:50.189Z --- # Contract @@ -21,3 +21,5 @@ A skill invokes the shared one-shot server (`skills/iterator/server.mjs`) with a `lib/server.mjs` owns the common lifecycle: remote-session detection, port binding (fixed 7777), takeover of stale servers, signal-to-cancel handling, `/submit` (with an optional `onSubmit` transform that applies mechanical results before the agent sees them), `/cancel` with a reload grace window, and the two-hour timeout. `lib/ui.mjs` owns the shared page shell and client helpers; views live in `lib/views/*.mjs`. In pi, `lib/session-server.mjs` replaces the per-question lifecycle: one persistent shell (Planning | Work | Knowledge | Usage tabs plus iframe) for the whole session, views swapped over SSE, and at most one pending round. The shell derives its centered project identity from the process working directory and pairs it with the active tab context; status events supply operational controls only. Keep stdout machine-readable: diagnostics belong on stderr. + +Iframe views never own ordinary `pagehide` cancellation in the persistent session, because switching tabs replaces the iframe. The parent shell sends the unload beacon only when the whole dashboard closes, so Planning ↔ Work navigation preserves the pending review and its one eventual result. Explicit `?now=1` cancellation clears any pending reload-grace timer before settling the round. diff --git a/memory/architecture/index.md b/memory/architecture/index.md index 1cd94f2..f9f86da 100644 --- a/memory/architecture/index.md +++ b/memory/architecture/index.md @@ -2,7 +2,7 @@ How the system is structured. -* [Browser server contract](/architecture/browser-server-contract.md) - Interactive workflows run a local server that receives a JSON payload on stdin and returns exactly one JSON result on stdout. +* [Browser server contract](/architecture/browser-server-contract.md) - Interactive workflows use a one-result server contract; Pi's persistent shell owns cancellation across iframe navigation. * [Centralized workflow state rules](/architecture/workflow-state-ownership.md) - lib/status.mjs owns feature transitions, dependency readiness, and derived plan stages; gather computes them and views render the supplied state. * [Knowledge lifecycle](/architecture/knowledge-lifecycle.md) - The knowledge skills manage the bundle's knowledge areas through init, knowledge view, consolidate, and memorize workflows. * [Package and skill layout](/architecture/package-and-skill-layout.md) - The repo is a pi package / Claude Code plugin: SKILL.md runbooks own the flows, deterministic scripts own the mechanics, the extension adds tools/hooks/dashboard. diff --git a/memory/index.md b/memory/index.md index 1f5f167..16d67f2 100644 --- a/memory/index.md +++ b/memory/index.md @@ -1,6 +1,6 @@ --- okf_version: "0.1" -last_memorized_commit: a5d59c50453e568ec486a3c9c017c2506898700e +last_memorized_commit: c5b75e3e4b034bc80a1600e57249e162639d32a8 --- # iterator memory diff --git a/memory/log.md b/memory/log.md index 399b8f7..6c93946 100644 --- a/memory/log.md +++ b/memory/log.md @@ -1,6 +1,9 @@ # iterator update log ## 2026-07-17 +* **Memorize**: Set last_memorized_commit to c5b75e3e4b03. +* **Update**: Memorized [Explicit cancel must pre-empt pagehide grace](/pitfalls/cancel-now-after-grace-timer.md). +* **Update**: Memorized [Browser server contract](/architecture/browser-server-contract.md). * **Retirement**: Plan "Keep active review stable and clarify Planning versus Work" condensed into [Review navigation and Work context](/decisions/review-navigation-and-work-context.md). * **Plan review**: Whole-plan review recorded (agent). * **Review**: Reviewed [Keep review controls fully readable](/features/streamline-review-interface.md); approved (agent). diff --git a/memory/pitfalls/cancel-now-after-grace-timer.md b/memory/pitfalls/cancel-now-after-grace-timer.md index 8fa68b0..f642728 100644 --- a/memory/pitfalls/cancel-now-after-grace-timer.md +++ b/memory/pitfalls/cancel-now-after-grace-timer.md @@ -1,23 +1,23 @@ --- type: Pitfall -title: Immediate cancel can be masked by a pending grace timer -description: "The servers' /cancel handlers return early when a cancel grace timer exists, so a later ?now=1 cancel may not pre-empt it." +title: Explicit cancel must pre-empt pagehide grace +description: A pending pagehide grace timer must never delay an explicit cancellation request. tags: - server - cancel - - known-bug + - lifecycle files: - lib/server.mjs - lib/session-server.mjs - test/server.test.mjs - test/session-server.test.mjs -timestamp: 2026-07-06T19:11:28.965Z +timestamp: 2026-07-17T17:31:50.190Z --- # Pitfall -Both `lib/server.mjs` and `lib/session-server.mjs` start a grace timer for ordinary `/cancel` pagehide requests so reloads do not cancel the workflow. The handlers check `if (done || cancelTimer) return;` (session: `if (!pending || cancelTimer) return;`) before checking `?now=1`, which means an explicit immediate cancel sent while a grace timer is pending can be ignored until the timer fires. +Ordinary `pagehide` cancellation beacons use a short grace timer so a reload does not end an interactive workflow. If the handler returns merely because that timer already exists, a subsequent explicit `?now=1` cancel is delayed until the grace timer fires. # How to handle it -If you touch cancel handling, make `?now=1` clear/pre-empt any pending grace timer before returning. Add a regression test that first sends `/cancel`, then sends `/cancel?now=1`, and expects immediate `{"type":"cancel"}`. +For an explicit cancel, first clear any pending grace timer and then settle the active round immediately. Only ordinary beacon cancellation should retain the existing timer. Cover the sequence with a regression test that sends `/cancel`, then `/cancel?now=1`, and expects an immediate `{ "type": "cancel" }` result. diff --git a/memory/pitfalls/index.md b/memory/pitfalls/index.md index 5d76b98..2af9f2c 100644 --- a/memory/pitfalls/index.md +++ b/memory/pitfalls/index.md @@ -4,4 +4,4 @@ Known bugs, portability hazards, and sharp edges. * [An IPv4-only bind breaks localhost behind a sandbox forward](/pitfalls/ipv4-only-bind-breaks-localhost.md) - A sandbox publishes both v4 and v6 loopback forwards; bound to 0.0.0.0 the v6 one resets, and a reset stops clients falling back — so localhost fails while 127.0.0.1 works. * [Client JS in view template literals needs double-backslash escapes](/pitfalls/client-js-template-literal-escaping.md) - The views' client scripts live inside backtick template literals, so a single \n is converted to a real newline at module load and breaks the served inline