diff --git a/extensions/iterator.js b/extensions/iterator.js index 30e5483..ec46fd3 100644 --- a/extensions/iterator.js +++ b/extensions/iterator.js @@ -65,6 +65,7 @@ import { nextFeatureWaveAction, pauseFeatureWave, projectRoot, + roleFromInput, roleModelSpec, runJson, scriptPath, @@ -511,6 +512,8 @@ export default function iteratorExtension(pi) { let autoSteps = 0; let featureWave = null; // fixed ready-feature snapshot; review stays manual let preAutoModel = null; // the user's model before the first role switch + let pendingRole = null; // exact role command captured from the current input + let manualRoleActive = false; // this turn switched a manual role model const notifyUi = (msg, level = "info") => { if (lastCtx?.hasUI) lastCtx.ui.notify(`iterator: ${msg}`, level); @@ -564,7 +567,7 @@ export default function iteratorExtension(pi) { } }; - /** Restore the user's pre-auto model on done/escalate/pause. */ + /** Restore the user's model after an automatic or manual role turn. */ const restoreModel = async () => { if (!preAutoModel) return; const m = preAutoModel; @@ -679,7 +682,12 @@ export default function iteratorExtension(pi) { if (!action) return; if (action.done) { - await writeState({ phase: "done", active_feature: null }); + await writeState({ + mode: "manual", + paused: false, + phase: "done", + active_feature: null, + }); autoSteps = 0; await restoreModel(); notifyUi( @@ -1449,8 +1457,14 @@ export default function iteratorExtension(pi) { pi.on("before_agent_start", async (_event, ctx) => { rememberCtx(ctx); try { + const { hub, implement, settings, state } = await gatherSession(ctx.cwd); + if (pendingRole && state?.mode !== "auto" && !featureWave) { + await applyRole(pendingRole, settings); + manualRoleActive = true; + } + // Model selection also applies to /iterator-plan before a bundle exists; + // only the ambient bundle context depends on durable plan state. if (!bundleExists(ctx.cwd)) return undefined; - const { hub, implement } = await gatherSession(ctx.cwd); let matched = []; if (recentFiles.size) { const knowledge = await gatherPayload(ctx.cwd, "knowledge"); @@ -1484,6 +1498,7 @@ export default function iteratorExtension(pi) { let usageBuffer = []; pi.on("input", async (event) => { + pendingRole = roleFromInput(event.text); const a = attributionFromInput(event.text); if (a) attribution = a; }); @@ -1567,6 +1582,10 @@ export default function iteratorExtension(pi) { rememberCtx(ctx); invalidateSession(); // the turn may have changed files/commits await flushUsage(ctx.cwd); + if (manualRoleActive) { + manualRoleActive = false; + await restoreModel(); + } await refreshHub(ctx.cwd); await refreshStatus(ctx); // Keep abortPending set until this stale agent_end reaches its final diff --git a/lib/pi-tools.mjs b/lib/pi-tools.mjs index 0afe97e..7aefe12 100644 --- a/lib/pi-tools.mjs +++ b/lib/pi-tools.mjs @@ -435,6 +435,23 @@ export function roleModelSpec(settings, role) { // --------------------------------------------------------------------------- // Token-usage attribution (pi turn_end capture → usage op rows) +const ROLE_MAP = { + "iterator-plan": "planner", + "iterator-test": "tester", + "iterator-implement": "implementer", + "iterator-next": "implementer", + "iterator-review": "reviewer", + "iterator-review-plan": "plan_reviewer", +}; + +/** The role model for an exact Iterator command, or null for other input. */ +export function roleFromInput(text) { + const match = String(text || "") + .trim() + .match(/^\/(?:skill:)?([a-z-]+)(?=\s|$)/); + return match ? (ROLE_MAP[match[1]] ?? null) : null; +} + const ATTRIBUTION_MAP = { "iterator-plan": "plan", "iterator-feature": "feature", diff --git a/lib/session-server.mjs b/lib/session-server.mjs index f365051..9d07161 100644 --- a/lib/session-server.mjs +++ b/lib/session-server.mjs @@ -292,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); @@ -497,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 77a935e..c4eac2a 100644 --- a/lib/ui.mjs +++ b/lib/ui.mjs @@ -156,7 +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; -function sendCancel(){ if(__submitted) 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; 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/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/lib/write.mjs b/lib/write.mjs index 6777f6c..978500a 100644 --- a/lib/write.mjs +++ b/lib/write.mjs @@ -397,6 +397,7 @@ function writePlan(payload, root) { const featuresSection = b.plan?.sections["Features"] || ""; + const approved = (payload.status || "approved") === "approved"; // Branch/worktree per plan (settings): approving a plan on main/master // moves the work onto iterator/ — in a separate git worktree by @@ -409,7 +410,7 @@ function writePlan(payload, root) { const curBranch = gitSoft(["rev-parse", "--abbrev-ref", "HEAD"], b.root); const hasHead = gitSoft(["rev-parse", "--verify", "HEAD"], b.root) !== ""; if ( - (payload.status || "approved") === "approved" && + approved && b.settings.branch_per_plan === "on" && hasHead && (curBranch === "main" || curBranch === "master") @@ -457,14 +458,31 @@ ${featuresSection} `.replace(/\n{3,}/g, "\n\n"); writeFileSync(join(b.memDir, "plan.md"), joinDoc(fm, bodyText)); + // Plan approval begins a fresh workflow. Never let auto-mode bookkeeping, + // an escalation, or feature strikes from a retired/replaced plan dispatch + // work into this new plan; drafts intentionally leave runtime state alone. + if (approved) { + writeState( + { + set: { + mode: "manual", + paused: false, + phase: "idle", + active_feature: null, + strikes: {}, + escalation: null, + }, + }, + root, + ); + } // A selected candidate is explicitly being handed to plan creation. Consume // it only after the approved plan has been written; drafts, feedback, and // cancelled review rounds never invoke this deterministic operation. const backlog = loadBacklogForWrite(b); - const consumedBacklog = - (payload.status || "approved") === "approved" - ? backlog.filter((item) => item.selected === true) - : []; + const consumedBacklog = approved + ? backlog.filter((item) => item.selected === true) + : []; if (consumedBacklog.length) { writeFileSync( backlogPath(b), @@ -561,6 +579,7 @@ ${featuresSection} "plan.md", "index.md", "log.md", + ...(approved ? ["state.md"] : []), ...(consumedBacklog.length ? ["backlog/index.md"] : []), ], consumedBacklog: consumedBacklog.map((item) => item.id), 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/backlog/index.md b/memory/backlog/index.md index 61faf3c..2dc6637 100644 --- a/memory/backlog/index.md +++ b/memory/backlog/index.md @@ -2,10 +2,14 @@ type: Backlog title: Iterator backlog description: Saved ideas and bugs kept separate from active plan features. -items: "[]" -timestamp: 2026-07-17T15:00:30.011Z +items: "[{\"id\":\"feature-plannning-memory-files\",\"title\":\"Feature plannning memory files\",\"details\":\"Too many memories are added to a feature which bloats it. it should only add the most relevant like with the files. and if really a lot are necessary maybe a consolidation might be useful.\",\"kind\":\"idea\",\"selected\":false,\"created\":\"2026-07-17T17:39:22.649Z\",\"updated\":\"2026-07-17T17:39:22.649Z\"},{\"id\":\"let-in-usage-add-a-price-for-the-models-ioptional\",\"title\":\"let in usage add a price for the models. (ioptional)\",\"details\":\"use the price and auto calculates the total price for each row.\",\"kind\":\"idea\",\"selected\":false,\"created\":\"2026-07-17T17:42:22.517Z\",\"updated\":\"2026-07-17T17:42:22.517Z\"},{\"id\":\"after-consolidating-the-memory-files-there-is-still-a-stale-memory\",\"title\":\"After consolidating the memory files there is still a stale memory\",\"details\":\"Make sure consolidation checks feature and if there are too many memories and looks if some can be consolidated and merged or delted or updated.\",\"kind\":\"bug\",\"selected\":false,\"created\":\"2026-07-17T17:44:48.215Z\",\"updated\":\"2026-07-17T17:47:50.563Z\"},{\"id\":\"settign-and-function-to-memorize-commits-in-retire-plan\",\"title\":\"Settign and function to memorize commits in retire plan.\",\"details\":\"Add a setting with default fdalse that memorizes the plan commits in retire .\",\"kind\":\"idea\",\"selected\":false,\"created\":\"2026-07-17T17:47:06.229Z\",\"updated\":\"2026-07-17T17:47:06.229Z\"},{\"id\":\"make-sure-work-state-is-working\",\"title\":\"Make sure work state is working\",\"details\":\"i hadf the issue that the agent was working but the work tab had no blocking overlay so the state was wrong\",\"kind\":\"bug\",\"selected\":false,\"created\":\"2026-07-17T17:47:39.784Z\",\"updated\":\"2026-07-17T17:47:39.784Z\"}]" +timestamp: 2026-07-17T17:47:50.563Z --- # Backlog -(empty) +* [idea] Feature plannning memory files +* [idea] let in usage add a price for the models. (ioptional) +* [bug] After consolidating the memory files there is still a stale memory +* [idea] Settign and function to memorize commits in retire plan. +* [bug] Make sure work state is working 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..5e0a059 100644 --- a/memory/decisions/index.md +++ b/memory/decisions/index.md @@ -2,11 +2,14 @@ Durable product and implementation choices agents should preserve. +* [Apply role models to manual turns and reset stale runtime state](/decisions/manual-role-models-and-runtime-reset.md) - Manual Iterator role commands temporarily select configured models, while approved plans and terminal auto runs reset runtime state deterministically. +* [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. * [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/manual-role-models-and-runtime-reset.md b/memory/decisions/manual-role-models-and-runtime-reset.md new file mode 100644 index 0000000..c5e0fa9 --- /dev/null +++ b/memory/decisions/manual-role-models-and-runtime-reset.md @@ -0,0 +1,26 @@ +--- +type: Decision +title: Apply role models to manual turns and reset stale runtime state +description: Manual Iterator role commands temporarily select configured models, while approved plans and terminal auto runs reset runtime state deterministically. +status: accepted +date: 2026-07-17 +tags: [models, workflow, runtime-state, pi] +files: ["lib/write.mjs", "extensions/iterator.js", "lib/pi-tools.mjs", "test/write.test.mjs", "test/pi-tools.test.mjs"] +timestamp: 2026-07-17T17:51:38.757Z +--- + +## Decision + +Parse exact manual Iterator commands into planner, implementer, tester, reviewer, or plan-reviewer roles without changing usage attribution. Before a manual turn starts, apply that role's configured model and restore the user's prior model at turn end; automatic runs and fixed feature waves retain their existing role-model ownership. + +Approved plan creation resets runtime state to manual and idle, clearing active feature, strikes, and escalation. A completed automatic run likewise returns to manual mode, while pause and escalation paths remain explicitly paused auto states. + +## Consequences + +Usage rows now reflect the model that executed a manual role command, and stale auto bookkeeping cannot dispatch work into a newly approved plan. Parser coverage verifies exact command handling and plan-review precedence; writer coverage verifies approved resets and draft preservation. The whole-plan review noted that extension lifecycle paths still lack direct hook-level tests. + +# Retired plan + +Condensed from plan "Apply role models on manual turns and reset stale auto state" (2 features, archived under /features/archive/2026-07-17-manual-role-models-and-runtime-reset/). + +Token usage: 2207627 in / 20868 out / 18306048 cache-read / 0 cache-write over 103 turns (per-step breakdown in the archived usage.md). 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/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/index.md b/memory/features/archive/2026-07-17-backlog-planning-and-feature-waves/index.md similarity index 100% rename from memory/features/index.md rename to memory/features/archive/2026-07-17-backlog-planning-and-feature-waves/index.md diff --git a/memory/plan.md b/memory/features/archive/2026-07-17-backlog-planning-and-feature-waves/plan.md similarity index 100% rename from memory/plan.md rename to memory/features/archive/2026-07-17-backlog-planning-and-feature-waves/plan.md 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/archive/2026-07-17-manual-role-models-and-runtime-reset/apply-role-models-manual-turns.md b/memory/features/archive/2026-07-17-manual-role-models-and-runtime-reset/apply-role-models-manual-turns.md new file mode 100644 index 0000000..dd91e87 --- /dev/null +++ b/memory/features/archive/2026-07-17-manual-role-models-and-runtime-reset/apply-role-models-manual-turns.md @@ -0,0 +1,45 @@ +--- +type: Feature +title: Apply configured models to manual turns +description: "Run manual iterator commands with their configured role model and restore the user's model afterward." +status: done +size: medium +depends_on: [reset-plan-runtime-state] +files: ["lib/pi-tools.mjs", "extensions/iterator.js", "test/pi-tools.test.mjs"] +memories: [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, decisions/settings-close-returns-to-work, setup/install-and-command-surface] +timestamp: "2026-07-17T17:49:36.706Z" +tags: [] +commits: + - sha: 4b007e50016e5de9014a296b26749fe9dc063f48 + kind: implement + date: 2026-07-17 + - sha: c7157f5b89a8b3464ecd4f849849bf7d794d8121 + kind: implement + date: 2026-07-17 +reviewed: 2026-07-17 +done: 2026-07-17 +--- + +# Implementation notes + +Add a pure exact command-to-role parser beside attribution utilities without changing `attributionFromInput()` results. Capture the parsed role for every input (clearing it for non-role inputs), apply it before manual agent starts when state is not auto, and restore the saved model at manual agent end before `kickAuto()`. Leave auto and feature-wave role ownership unchanged; plan-review must resolve to plan_reviewer, and the known thinking-level restoration limitation remains unchanged. + +# Snippets + +```js +export function attributionFromInput(text) {\n const m = String(text || '').trim().match(/^\\/(?:skill:)?([a-z-]+)/);\n // Preserve this return shape; roleFromInput is a separate parser.\n}\n\nexport function roleModelSpec(settings, role) {\n return { model, thinking };\n} +``` + +# Depends on + +* [Reset runtime state for approved plans](/features/reset-plan-runtime-state.md) + +# Blast radius + +Pi input, before-agent-start, and agent-end lifecycle hooks; configured-model usage rows and automatic/feature-wave model restoration. + +# Review + +## 2026-07-17 +* **Approved** _(agent review: openai-codex/gpt-5.6-sol)_ — Manual Iterator commands now select the exact configured role model before agent start, including planner turns without an active bundle, and restore the user's model before automatic dispatch resumes. +* **Needs changes** _(agent review: openai-codex/gpt-5.6-sol)_ — `extensions/iterator.js` returns from `before_agent_start` when `bundleExists(ctx.cwd)` is false before applying `pendingRole`, so manual `/iterator-plan` turns in a fresh or retired project never use `planner_model`. Apply the manual role (using gathered/default settings) before the bundle-only ambient-context early return, while preserving the auto/feature-wave guards and restoration order. diff --git a/memory/features/archive/2026-07-17-manual-role-models-and-runtime-reset/index.md b/memory/features/archive/2026-07-17-manual-role-models-and-runtime-reset/index.md new file mode 100644 index 0000000..6a55a44 --- /dev/null +++ b/memory/features/archive/2026-07-17-manual-role-models-and-runtime-reset/index.md @@ -0,0 +1,4 @@ +# Features + +* [Reset runtime state for approved plans](reset-plan-runtime-state.md) - ✅ done · medium · Start each approved plan in a manual, idle runtime state so stale auto mode cannot dispatch work. +* [Apply configured models to manual turns](apply-role-models-manual-turns.md) - ✅ done · medium · depends: reset-plan-runtime-state · Run manual iterator commands with their configured role model and restore the user's model afterward. diff --git a/memory/features/archive/2026-07-17-manual-role-models-and-runtime-reset/plan.md b/memory/features/archive/2026-07-17-manual-role-models-and-runtime-reset/plan.md new file mode 100644 index 0000000..5049480 --- /dev/null +++ b/memory/features/archive/2026-07-17-manual-role-models-and-runtime-reset/plan.md @@ -0,0 +1,42 @@ +--- +type: Plan +title: Apply role models on manual turns and reset stale auto state +description: Use configured role models for manual skill commands and prevent automatic runtime state from leaking into the next plan. +status: approved +branch: iterator/always-available-backlog +created: 2026-07-17 +timestamp: "2026-07-17T17:50:46.427Z" +plan_reviewed: 2026-07-17 +--- + +# Goal + +Fix manual iterator skill turns so they temporarily use their configured planner, implementer, tester, reviewer, or plan-reviewer model and usage accurately reflects that model. Prevent stale automatic runtime state from dispatching implementation after a new plan and feature set are approved, while preserving deliberate auto-mode behavior. + +# Architecture + +- Extend `architecture/package-and-skill-layout` with a pure command-to-role parser in `lib/pi-tools.mjs`; leave usage attribution's public shape unchanged and cover exact plan-review precedence. +- Extend the Pi extension lifecycle: capture a role at input, apply it before a manual agent turn, and restore the user's original model at the end of that manual turn. Existing auto and feature-wave paths retain ownership of their role application. +- Centralize new-plan runtime reset in `lib/write.mjs`'s approved-plan path, then reset terminal auto runs in the extension; state transitions remain deterministic writer operations under `architecture/workflow-state-ownership`. +- Synchronize changed root shared libraries into the shipped hub copy and cover model routing, plan-state reset, and explicit auto-mode behavior with the existing node:test suites. + +# Dependencies + +(none) + +# Key decisions + +# Features + +* [Reset runtime state for approved plans](/features/reset-plan-runtime-state.md) - Start each approved plan in a manual, idle runtime state so stale auto mode cannot dispatch work. +* [Apply configured models to manual turns](/features/apply-role-models-manual-turns.md) - Run manual iterator commands with their configured role model and restore the user's model afterward. + +# Plan review + +## 2026-07-17 _(agent review: openai-codex/gpt-5.6-sol)_ + +## Finding + +- **Architecture — promised lifecycle coverage is incomplete.** The plan says the existing `node:test` suites will cover model routing and explicit auto-mode behavior, but `test/pi-tools.test.mjs` only exercises the pure `roleFromInput()` parser and `test/write.test.mjs` only exercises approved/draft plan resets. No test drives the `extensions/iterator.js` hooks to prove manual `before_agent_start` model application, `agent_end` restoration before `kickAuto()`, auto/feature-wave exclusion, or the terminal auto-run reset to `mode: manual`. The implementation delivers those paths, but this architectural verification commitment remains unfulfilled. + +Goal coverage otherwise appears complete: approved plans deterministically reset stale state, drafts preserve it, completed auto runs return to manual mode, exact manual commands select the configured role (including `plan_reviewer` precedence), and the previous model is restored before automatic dispatch resumes. No unexplained scope drift, TODOs, pending features, or unaccepted features were found. diff --git a/memory/features/archive/2026-07-17-manual-role-models-and-runtime-reset/reset-plan-runtime-state.md b/memory/features/archive/2026-07-17-manual-role-models-and-runtime-reset/reset-plan-runtime-state.md new file mode 100644 index 0000000..3f687b0 --- /dev/null +++ b/memory/features/archive/2026-07-17-manual-role-models-and-runtime-reset/reset-plan-runtime-state.md @@ -0,0 +1,31 @@ +--- +type: Feature +title: Reset runtime state for approved plans +description: Start each approved plan in a manual, idle runtime state so stale auto mode cannot dispatch work. +status: done +size: medium +depends_on: [] +files: ["lib/write.mjs", "extensions/iterator.js", "test/write.test.mjs"] +memories: [architecture/package-and-skill-layout, architecture/workflow-state-ownership, 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, decisions/powerline-shows-sandbox-ui-port] +timestamp: "2026-07-17T17:43:46.967Z" +tags: [] +commits: + - sha: f4ecbdccab0d0109a8019cca9328f69a7066617e + kind: implement + date: 2026-07-17 +done: 2026-07-17 +--- + +# Implementation notes + +Reset mode, pause, phase, active feature, strikes, and escalation through `writeState()` immediately after an approved plan is written; do not alter draft-plan state. Include `state.md` in the writer result. Reset a completed auto run to manual/done while keeping escalation and circuit-breaker branches paused auto. After verification, repair the live stale state with the minimal deterministic `{ op:'state', set:{ mode:'manual' } }` write; do not create commits. + +# Snippets + +```js +if ((payload.status || 'approved') === 'approved') {\n writeState({ set: { mode: 'manual', paused: false, phase: 'idle',\n active_feature: null, strikes: {}, escalation: null } }, root);\n}\n\nif (action.done) {\n await writeState({ mode: 'manual', paused: false, phase: 'done', active_feature: null });\n} +``` + +# Blast radius + +Approved plan creation, completed automatic runs, and the dashboard's subsequent auto-dispatch decision. diff --git a/memory/features/archive/2026-07-17-manual-role-models-and-runtime-reset/usage.md b/memory/features/archive/2026-07-17-manual-role-models-and-runtime-reset/usage.md new file mode 100644 index 0000000..e72e5e9 --- /dev/null +++ b/memory/features/archive/2026-07-17-manual-role-models-and-runtime-reset/usage.md @@ -0,0 +1,51 @@ +--- +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\":170439,\"output\":1029,\"cacheRead\":1018880,\"cacheWrite\":0,\"turns\":5}},\"memory\":{\"openai-codex/gpt-5.6-terra\":{\"input\":260681,\"output\":3539,\"cacheRead\":1991168,\"cacheWrite\":0,\"turns\":9}},\"plan\":{\"openai-codex/gpt-5.6-terra\":{\"input\":69670,\"output\":4171,\"cacheRead\":3958272,\"cacheWrite\":0,\"turns\":14}},\"implement\":{\"openai-codex/gpt-5.6-terra\":{\"input\":1476076,\"output\":7475,\"cacheRead\":7436288,\"cacheWrite\":0,\"turns\":39},\"openai-codex/gpt-5.6-sol\":{\"input\":0,\"output\":0,\"cacheRead\":0,\"cacheWrite\":0,\"turns\":1}},\"review\":{\"openai-codex/gpt-5.6-terra\":{\"input\":39405,\"output\":729,\"cacheRead\":1547264,\"cacheWrite\":0,\"turns\":8},\"openai-codex/gpt-5.6-sol\":{\"input\":191356,\"output\":3925,\"cacheRead\":2354176,\"cacheWrite\":0,\"turns\":27}}},\"features\":{\"retire-plan\":{\"input\":170439,\"output\":1029,\"cacheRead\":1018880,\"cacheWrite\":0,\"turns\":5},\"reset-plan-runtime-state\":{\"input\":1413687,\"output\":4665,\"cacheRead\":8069120,\"cacheWrite\":0,\"turns\":31},\"apply-role-models-manual-turns\":{\"input\":281759,\"output\":6409,\"cacheRead\":2637824,\"cacheWrite\":0,\"turns\":38}}}" +timestamp: 2026-07-17T17:51:00.318Z +--- + +# Usage + +## hub + +| model | input | output | cache read | cache write | turns | +| --- | ---: | ---: | ---: | ---: | ---: | +| openai-codex/gpt-5.6-terra | 170439 | 1029 | 1018880 | 0 | 5 | + +## memory + +| model | input | output | cache read | cache write | turns | +| --- | ---: | ---: | ---: | ---: | ---: | +| openai-codex/gpt-5.6-terra | 260681 | 3539 | 1991168 | 0 | 9 | + +## plan + +| model | input | output | cache read | cache write | turns | +| --- | ---: | ---: | ---: | ---: | ---: | +| openai-codex/gpt-5.6-terra | 69670 | 4171 | 3958272 | 0 | 14 | + +## implement + +| model | input | output | cache read | cache write | turns | +| --- | ---: | ---: | ---: | ---: | ---: | +| openai-codex/gpt-5.6-terra | 1476076 | 7475 | 7436288 | 0 | 39 | +| openai-codex/gpt-5.6-sol | 0 | 0 | 0 | 0 | 1 | + +## review + +| model | input | output | cache read | cache write | turns | +| --- | ---: | ---: | ---: | ---: | ---: | +| openai-codex/gpt-5.6-terra | 39405 | 729 | 1547264 | 0 | 8 | +| openai-codex/gpt-5.6-sol | 191356 | 3925 | 2354176 | 0 | 27 | + +## Per feature + +| feature | input | output | cache read | cache write | turns | +| --- | ---: | ---: | ---: | ---: | ---: | +| retire-plan | 170439 | 1029 | 1018880 | 0 | 5 | +| reset-plan-runtime-state | 1413687 | 4665 | 8069120 | 0 | 31 | +| apply-role-models-manual-turns | 281759 | 6409 | 2637824 | 0 | 38 | + +Total: 2207627 in / 20868 out / 18306048 cache-read / 0 cache-write over 103 turns. diff --git a/memory/features/archive/2026-07-17-review-navigation-and-work-context/index.md b/memory/features/archive/2026-07-17-review-navigation-and-work-context/index.md new file mode 100644 index 0000000..6a179cb --- /dev/null +++ b/memory/features/archive/2026-07-17-review-navigation-and-work-context/index.md @@ -0,0 +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) - ✅ 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) - ✅ done · medium · Show complete feature titles in review and remove the unused Feedback panel. diff --git a/memory/features/archive/2026-07-17-review-navigation-and-work-context/plan.md b/memory/features/archive/2026-07-17-review-navigation-and-work-context/plan.md new file mode 100644 index 0000000..9f3e1b8 --- /dev/null +++ b/memory/features/archive/2026-07-17-review-navigation-and-work-context/plan.md @@ -0,0 +1,57 @@ +--- +type: Plan +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/always-available-backlog +created: 2026-07-17 +timestamp: "2026-07-17T16:30:16.999Z" +plan_reviewed: 2026-07-17 +--- + +# Goal + +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/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 + +(none) + +# Key decisions + +- 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 + +* [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/archive/2026-07-17-review-navigation-and-work-context/preserve-review-across-planning.md b/memory/features/archive/2026-07-17-review-navigation-and-work-context/preserve-review-across-planning.md new file mode 100644 index 0000000..5a805c6 --- /dev/null +++ b/memory/features/archive/2026-07-17-review-navigation-and-work-context/preserve-review-across-planning.md @@ -0,0 +1,41 @@ +--- +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: 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:23:18.304Z" +tags: [] +commits: + - sha: 30c6a6b70a54d59a72a79ba2f864a9b993006262 + kind: implement + date: 2026-07-17 + - sha: dd02899ab64b86edef1ab064bd6a3e156eb5a222 + kind: implement + date: 2026-07-17 +reviewed: 2026-07-17 +done: 2026-07-17 +--- + +# 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. + +# 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/archive/2026-07-17-review-navigation-and-work-context/show-active-work-in-work.md b/memory/features/archive/2026-07-17-review-navigation-and-work-context/show-active-work-in-work.md new file mode 100644 index 0000000..f42e61f --- /dev/null +++ b/memory/features/archive/2026-07-17-review-navigation-and-work-context/show-active-work-in-work.md @@ -0,0 +1,37 @@ +--- +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: 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:26:25.040Z" +tags: [] +commits: + - sha: e0b0453cbc024caa1cfdcbe079767214f4d8c6d8 + kind: implement + date: 2026-07-17 +done: 2026-07-17 +reviewed: 2026-07-17 +--- + +# 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. + +# 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/archive/2026-07-17-review-navigation-and-work-context/streamline-review-interface.md b/memory/features/archive/2026-07-17-review-navigation-and-work-context/streamline-review-interface.md new file mode 100644 index 0000000..8acd36e --- /dev/null +++ b/memory/features/archive/2026-07-17-review-navigation-and-work-context/streamline-review-interface.md @@ -0,0 +1,41 @@ +--- +type: Feature +title: Keep review controls fully readable +description: Show complete feature titles in review and remove the unused Feedback panel. +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:28.160Z" +tags: [] +commits: + - sha: e7d5824e2ac431640e6a1abc6169dfcc6121b43b + kind: implement + date: 2026-07-17 +done: 2026-07-17 +reviewed: 2026-07-17 +--- + +# 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. + +# 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 8f129b2..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 @@ -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). -* [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. * [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..8d8a56d 100644 --- a/memory/log.md +++ b/memory/log.md @@ -1,6 +1,54 @@ # iterator update log ## 2026-07-17 +* **Retirement**: Plan "Apply role models on manual turns and reset stale auto state" condensed into [Apply role models to manual turns and reset stale runtime state](/decisions/manual-role-models-and-runtime-reset.md). +* **Plan review**: Whole-plan review recorded (agent). +* **Review**: Reviewed [Apply configured models to manual turns](/features/apply-role-models-manual-turns.md); approved (agent). +* **Review**: Accepted [Apply configured models to manual turns](/features/apply-role-models-manual-turns.md) (committed as feature(apply-role-models-manual-turns)). +* **Implementation**: Committed feature(apply-role-models-manual-turns) on branch iterator/always-available-backlog; awaiting review. +* **Backlog**: edit after-consolidating-the-memory-files-there-is-still-a-stale-memory. +* **Backlog**: create make-sure-work-state-is-working. +* **Backlog**: create settign-and-function-to-memorize-commits-in-retire-plan. +* **Review**: Reviewed [Apply configured models to manual turns](/features/apply-role-models-manual-turns.md); changes (agent). +* **Implementation**: Committed feature(apply-role-models-manual-turns) on branch iterator/always-available-backlog; awaiting review. +* **Backlog**: create after-consolidating-the-memory-files-there-is-still-a-stale-memory. +* **Review**: Accepted [Reset runtime state for approved plans](/features/reset-plan-runtime-state.md) (committed as feature(reset-plan-runtime-state)). +* **Backlog**: create let-in-usage-add-a-price-for-the-models-ioptional. +* **Implementation**: Committed feature(reset-plan-runtime-state) on branch iterator/always-available-backlog; awaiting review. +* **Backlog**: create feature-plannning-memory-files. +* **Update**: Applied 2 feature adjustment(s). +* **Creation**: 2 feature(s) written. +* **Creation**: Plan "Apply role models on manual turns and reset stale auto state" approved on branch iterator/always-available-backlog. +* **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). +* **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)). +* **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). +* **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/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