From 3854fc7cc6ffd5de8f64868c3dc56c2747a1c78c Mon Sep 17 00:00:00 2001 From: Christoph Maier Date: Mon, 20 Jul 2026 18:17:05 +0200 Subject: [PATCH 01/20] feature(agent-neutral-shell-copy): Use Agent in shared dashboard messages Feature: agent-neutral-shell-copy --- extensions/iterator.js | 2 +- lib/server.mjs | 2 +- lib/session-server.mjs | 2 +- lib/ui.mjs | 8 ++-- memory/backlog/index.md | 8 ++-- memory/features/agent-neutral-shell-copy.md | 30 ++++++++++++++ .../features/agent-neutral-workflow-copy.md | 35 ++++++++++++++++ memory/features/index.md | 5 +++ .../features/review-exact-red-test-source.md | 32 +++++++++++++++ memory/index.md | 1 + memory/log.md | 4 ++ memory/plan.md | 40 +++++++++++++++++++ memory/state.md | 12 +++--- memory/usage.md | 8 ++-- skills/iterator/lib/server.mjs | 2 +- skills/iterator/lib/ui.mjs | 8 ++-- test/extension-model-lifecycle.test.mjs | 17 +++++++- test/server.test.mjs | 9 ++++- test/session-server.test.mjs | 1 + 19 files changed, 196 insertions(+), 30 deletions(-) create mode 100644 memory/features/agent-neutral-shell-copy.md create mode 100644 memory/features/agent-neutral-workflow-copy.md create mode 100644 memory/features/index.md create mode 100644 memory/features/review-exact-red-test-source.md create mode 100644 memory/plan.md diff --git a/extensions/iterator.js b/extensions/iterator.js index 7577a49..a91e70c 100644 --- a/extensions/iterator.js +++ b/extensions/iterator.js @@ -1161,7 +1161,7 @@ export default function iteratorExtension(pi) { } const cmd = actionToCommand(result); if (!cmd) return; - session.showWorking(`Dispatched ${cmd} — Claude is working…`); + session.showWorking(`Dispatched ${cmd} — Agent is working…`); dispatch(cmd); }, onControl, diff --git a/lib/server.mjs b/lib/server.mjs index 9f233ad..f104482 100644 --- a/lib/server.mjs +++ b/lib/server.mjs @@ -290,7 +290,7 @@ export async function serve({ step = 'iterator', html, onSubmit, reports = {} }) } /** The little "you can close this tab" page shown after a submit. */ -export function doneHtml(msg = 'Sent to Claude') { +export function doneHtml(msg = 'Sent to Agent') { return ` From bbb2be96942cd1a87c440fb58f7d0a6fec5c5975 Mon Sep 17 00:00:00 2001 From: Christoph Maier Date: Mon, 20 Jul 2026 18:25:46 +0200 Subject: [PATCH 04/20] feature(review-exact-red-test-source): Review exact red test source before writing Feature: review-exact-red-test-source --- lib/views/test.mjs | 93 ++++++++++++++++--- memory/features/agent-neutral-shell-copy.md | 8 +- .../features/review-exact-red-test-source.md | 4 +- memory/log.md | 2 + memory/state.md | 6 +- memory/usage.md | 14 ++- skills/iterator-test/SKILL.md | 58 ++++++++---- skills/iterator/lib/views/test.mjs | 93 ++++++++++++++++--- test/client-js-parse.test.mjs | 23 ++++- test/server.test.mjs | 19 +++- test/ui.test.mjs | 37 ++++++++ 11 files changed, 300 insertions(+), 57 deletions(-) diff --git a/lib/views/test.mjs b/lib/views/test.mjs index d6d0280..7577f94 100644 --- a/lib/views/test.mjs +++ b/lib/views/test.mjs @@ -5,10 +5,11 @@ * * input: { step:"test", branch, feature:{name,description}, runner, * mode, // "red" (feature pending) | "green" (feature done) - * cases:[ {title,kind,rationale} ] } // kind: happy | edge | integration + * cases:[ {id,title,kind,rationale,path,code} ], + * draftFiles:[ {path,content} ] } // exact red-mode files; additive for green mode * output: one JSON line to stdout — - * { type:"test-approved", branch, feature, cases:[ {title,kind,rationale,include} ] } - * { type:"test-feedback", branch, feature, cases:[ {title,kind,rationale,include,comment} ], comment } + * { type:"test-approved", branch, feature, cases:[...], draftFiles:[...] } + * { type:"test-feedback", branch, feature, cases:[...], draftFiles:[...], comment } * plus the shared { type:"cancel" } / { type:"timeout" }. */ import { renderPage } from '../ui.mjs'; @@ -35,6 +36,15 @@ h1{font-family:var(--font-display);font-size:var(--fs-xl);font-weight:600;margin .k-edge{background:var(--bg-yellow);color:var(--dot-yellow)} .k-integration{background:var(--hunk-bg);color:var(--hunk-fg)} .crat{font-size:var(--fs-sm);color:var(--text-muted);margin:8px 0 0;line-height:1.5} +.source-meta,.file-meta{display:flex;align-items:center;justify-content:space-between;gap:var(--sp-2);margin-top:var(--sp-3);font:var(--fs-xs)/1.4 var(--font-mono);color:var(--text-muted)} +.source-meta span,.file-meta span{text-transform:uppercase;letter-spacing:.08em;font-weight:600} +.source-meta code,.file-meta code{color:var(--text);overflow-wrap:anywhere;text-align:right} +.source-code,.file-code{margin:var(--sp-2) 0 0;background:var(--code-bg);border:1px solid var(--border);border-radius:var(--radius-sm);padding:var(--sp-3);overflow:auto;max-height:360px;font:var(--fs-xs)/1.55 var(--font-mono);color:var(--text)} +.source-code code,.file-code code{white-space:pre} +.files{margin-top:var(--sp-5)} +.file{margin-bottom:var(--sp-3)} +.source-warning{display:none;background:var(--bg-yellow);color:var(--dot-yellow);border-radius:var(--radius-sm);padding:var(--sp-2) var(--sp-3);margin-bottom:var(--sp-4);font-size:var(--fs-sm)} +.source-warning.visible{display:block} .sdot{display:inline-block;width:8px;height:8px;border-radius:50%;background:currentColor;margin-right:4px;vertical-align:1px} textarea.ccmt{width:100%;margin-top:8px;background:var(--bg);border:1px solid var(--border);border-radius:6px; color:var(--text);font-size:12px;font-family:inherit;resize:vertical;min-height:44px;outline:none;padding:8px 10px;line-height:1.5} @@ -52,11 +62,16 @@ const BODY = `
-

These are the test cases Claude proposes for this feature. Untick any you don't want, edit a title, or leave a comment. Click Accept to have Claude write and run them; add a comment and it becomes Send review to revise the plan first.

+

+
Exact red-test source is incomplete. Every included case must map to code inside a proposed file before this plan can be accepted.
+
Overall comment (optional) - +
`; @@ -64,21 +79,30 @@ const BODY = ` const JS = ` const feature = D.feature || {}; const ORIG = JSON.parse(JSON.stringify(D.cases || [])); -const state = (D.cases || []).map(c => ({ title:c.title||'', kind:c.kind||'happy', rationale:c.rationale||'', include:true, comment:'' })); +const state = (D.cases || []).map((c,i) => ({ + id:String(c.id||('case-'+(i+1))), title:c.title||'', kind:c.kind||'happy', rationale:c.rationale||'', + path:c.path||'', code:c.code||'', include:c.include!==false, comment:c.comment||'' +})); +const draftFiles = (D.draftFiles || []).map(f => ({ path:String(f.path||''), content:String(f.content||'') })); document.getElementById('title').textContent = 'Test plan — ' + (feature.name || 'feature'); document.getElementById('desc').textContent = feature.description || ''; if(D.runner) document.getElementById('runner').innerHTML = 'Runner: '+esc(D.runner)+''; const modeEl = document.getElementById('mode'); +const hintEl = document.getElementById('hint'); if(D.mode==='red'){ modeEl.className='mode red'; - modeEl.innerHTML='Red mode — this feature is not implemented yet. Accepted tests are written against the feature\\'s contract and are expected to fail until /iterator-implement turns them green.'; + modeEl.innerHTML='Red mode — this feature is not implemented yet. The exact reviewed files are expected to fail until /iterator-implement turns them green.'; + hintEl.innerHTML='Verify the real source for every test and the complete proposed files below. Untick a case or leave feedback to have the Agent revise the source. Accept writes this exact reviewed content without regenerating it.'; }else if(D.mode==='green'){ modeEl.className='mode green'; modeEl.innerHTML='Green mode — this feature is implemented; accepted tests must pass against the current code.'; + hintEl.innerHTML='These are the test cases the Agent proposes for this feature. Untick any you do not want, edit a title, or leave a comment. Accept writes and runs them; feedback asks the Agent to revise the plan first.'; } renderCases(); +renderDraftFiles(); refresh(); +refreshSourceGate(); function renderCases(){ const wrap = document.getElementById('cases'); @@ -88,6 +112,9 @@ function renderCases(){ const div = document.createElement('div'); div.className = 'case' + (c.include?'':' excluded'); const kind = ['happy','edge','integration'].includes(c.kind)?c.kind:'happy'; + const source = D.mode==='red' + ? '
Test source'+esc(c.path)+'
'+esc(c.code)+'
' + : ''; div.innerHTML = '
'+ ''+ @@ -95,13 +122,41 @@ function renderCases(){ ''+kind+''+ '
'+ (c.rationale?'
'+esc(c.rationale)+'
':'')+ + source+ ''; wrap.appendChild(div); }); } -function toggleCase(i,val){ state[i].include=val; renderCases(); refresh(); } -function editTitle(i,val){ state[i].title=val.trim(); refresh(); } -function editComment(i,val){ state[i].comment=val; refresh(); } +function renderDraftFiles(){ + const section = document.getElementById('draft-files'); + if(D.mode!=='red'){ section.hidden=true; return; } + section.hidden=false; + const wrap = document.getElementById('file-list'); + wrap.innerHTML = draftFiles.length ? '' : '
No exact files proposed.
'; + draftFiles.forEach(f => { + const div = document.createElement('div'); + div.className='file'; + div.innerHTML='
Proposed file'+esc(f.path)+'
'+esc(f.content)+'
'; + wrap.appendChild(div); + }); +} +function sourceReady(){ + if(D.mode!=='red') return true; + const included = state.filter(c=>c.include); + if(!included.length) return false; + return draftFiles.length>0 && included.every(c=> + c.path && c.code && draftFiles.some(f=>f.path===c.path && f.content.includes(c.code)) + ); +} +function refreshSourceGate(){ + const ready=sourceReady(); + document.getElementById('source-warning').className='source-warning'+(ready?'':' visible'); + const btn=document.getElementById('primary'); + if(btn) btn.disabled=!(ready||hasChanges()); +} +function toggleCase(i,val){ state[i].include=val; renderCases(); refresh(); refreshSourceGate(); } +function editTitle(i,val){ state[i].title=val.trim(); refresh(); refreshSourceGate(); } +function editComment(i,val){ state[i].comment=val; refresh(); refreshSourceGate(); } function hasChanges(){ if(document.getElementById('global-comment').value.trim()) return true; @@ -109,17 +164,27 @@ function hasChanges(){ if(state.some((c,i)=>!c.include || c.title!==(ORIG[i]&&ORIG[i].title))) return true; return false; } +function casePayload(c, withComment){ + const out={id:c.id,title:c.title,kind:c.kind,rationale:c.rationale,path:c.path,code:c.code,include:c.include}; + if(withComment) out.comment=c.comment.trim(); + return out; +} function onPrimary(){ const changed = hasChanges(); + if(!sourceReady() && !changed){ + alert('Exact red-test source is incomplete. Ask the Agent to revise the proposal.'); + return; + } const comment = document.getElementById('global-comment').value.trim(); + const reviewedFiles=draftFiles.map(f=>({path:f.path,content:f.content})); if(changed){ post({ type:'test-feedback', branch:D.branch||'HEAD', feature:feature.name, - cases: state.map(c=>({title:c.title,kind:c.kind,rationale:c.rationale,include:c.include,comment:c.comment.trim()})), - comment }, 'Sent — Claude is revising the test plan'); + cases: state.map(c=>casePayload(c,true)), draftFiles:reviewedFiles, + comment }, 'Sent — Agent is revising the test plan'); } else { post({ type:'test-approved', branch:D.branch||'HEAD', feature:feature.name, - cases: state.filter(c=>c.include).map(c=>({title:c.title,kind:c.kind,rationale:c.rationale,include:true})) }, - 'Accepted — Claude is writing tests'); + cases: state.filter(c=>c.include).map(c=>casePayload(c,false)), draftFiles:reviewedFiles }, + 'Accepted — Agent is writing reviewed tests'); } } `; diff --git a/memory/features/agent-neutral-shell-copy.md b/memory/features/agent-neutral-shell-copy.md index 1538f32..5c8dea4 100644 --- a/memory/features/agent-neutral-shell-copy.md +++ b/memory/features/agent-neutral-shell-copy.md @@ -7,13 +7,14 @@ size: small depends_on: [] files: ["extensions/iterator.js", "lib/server.mjs", "lib/ui.mjs", "lib/session-server.mjs", "test/server.test.mjs", "test/session-server.test.mjs", "test/extension-model-lifecycle.test.mjs"] memories: [pitfalls/cancel-now-after-grace-timer, pitfalls/client-js-template-literal-escaping, architecture/browser-server-contract, architecture/package-and-skill-layout, patterns/one-json-line-server-results, patterns/safe-browser-rendering, decisions/backlog-planning-and-feature-waves, decisions/focus-feature-execution-and-dashboard-ownership] -timestamp: "2026-07-20T16:17:51.866Z" +timestamp: "2026-07-20T16:17:55.849Z" tags: [] commits: - sha: 3854fc7cc6ffd5de8f64868c3dc56c2747a1c78c kind: implement date: 2026-07-20 done: 2026-07-20 +reviewed: 2026-07-20 --- # Implementation notes @@ -33,3 +34,8 @@ session.showWorking(`Dispatched ${cmd} — Claude is working…`); # Blast radius Shared completion and working-state copy appears across every browser workflow and Pi dashboard dispatch. + +# Review + +## 2026-07-20 +* **Approved** _(agent review: openai-codex/gpt-5.6-sol)_ — Approved: shared shell, completion, pending-round, and dispatch messages consistently use Agent, with synced copies and focused regression coverage. diff --git a/memory/features/review-exact-red-test-source.md b/memory/features/review-exact-red-test-source.md index 401ad56..5e977a1 100644 --- a/memory/features/review-exact-red-test-source.md +++ b/memory/features/review-exact-red-test-source.md @@ -2,12 +2,12 @@ type: Feature title: Review exact red test source description: Users can inspect and approve the complete executable source for every proposed red-mode test before files are written. -status: pending +status: implemented size: medium depends_on: [] files: ["skills/iterator-test/SKILL.md", "lib/views/test.mjs", "test/ui.test.mjs", "test/client-js-parse.test.mjs"] memories: [pitfalls/client-js-template-literal-escaping, decisions/iterator-dashboard-feature-workflow, decisions/parallel-feature-waves-and-consolidated-review, decisions/review-navigation-and-work-context, decisions/settings-close-returns-to-work, decisions/streamline-backlog-planning-and-knowledge-actions] -timestamp: "2026-07-20T16:13:44.335Z" +timestamp: "2026-07-20T16:25:46.746Z" tags: [] --- diff --git a/memory/log.md b/memory/log.md index 610f49e..762a943 100644 --- a/memory/log.md +++ b/memory/log.md @@ -1,6 +1,8 @@ # iterator update log ## 2026-07-20 +* **Implementation**: Committed feature(review-exact-red-test-source) on branch iterator/safe-role-model-handoff; awaiting review. +* **Review**: Reviewed [Use Agent wording in the dashboard shell](/features/agent-neutral-shell-copy.md); approved (agent). * **Review**: Accepted [Use Agent wording in the dashboard shell](/features/agent-neutral-shell-copy.md) (committed as feature(agent-neutral-shell-copy)). * **Implementation**: Committed feature(agent-neutral-shell-copy) on branch iterator/safe-role-model-handoff; awaiting review. * **Update**: Applied 3 feature adjustment(s). diff --git a/memory/state.md b/memory/state.md index 415428e..4d491c9 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: agent-neutral-shell-copy +phase: implementing +active_feature: review-exact-red-test-source strikes: "{}" escalation: null -timestamp: 2026-07-20T16:17:18.833Z +timestamp: 2026-07-20T16:18:09.398Z --- Runtime flow state; read via gather, written only by the state op. diff --git a/memory/usage.md b/memory/usage.md index 2347a76..a67e272 100644 --- a/memory/usage.md +++ b/memory/usage.md @@ -2,9 +2,9 @@ type: Usage title: Token usage description: Per-step model/token ledger and optional project-owned pricing for the active plan — written only by the usage op. -totals: "{\"steps\":{\"hub\":{\"openai-codex/gpt-5.6-terra\":{\"input\":38403,\"output\":1157,\"cacheRead\":217088,\"cacheWrite\":0,\"turns\":8}},\"plan\":{\"openai-codex/gpt-5.6-sol\":{\"input\":92807,\"output\":7147,\"cacheRead\":464384,\"cacheWrite\":0,\"turns\":20}},\"implement\":{\"openai-codex/gpt-5.6-terra\":{\"input\":244183,\"output\":4327,\"cacheRead\":2065408,\"cacheWrite\":0,\"turns\":31}}},\"features\":{\"retire-plan\":{\"input\":38403,\"output\":1157,\"cacheRead\":217088,\"cacheWrite\":0,\"turns\":8},\"agent-neutral-shell-copy\":{\"input\":244183,\"output\":4327,\"cacheRead\":2065408,\"cacheWrite\":0,\"turns\":31}},\"featureModels\":{\"retire-plan\":{\"openai-codex/gpt-5.6-terra\":{\"input\":38403,\"output\":1157,\"cacheRead\":217088,\"cacheWrite\":0,\"turns\":8}},\"agent-neutral-shell-copy\":{\"openai-codex/gpt-5.6-terra\":{\"input\":244183,\"output\":4327,\"cacheRead\":2065408,\"cacheWrite\":0,\"turns\":31}}}}" +totals: "{\"steps\":{\"hub\":{\"openai-codex/gpt-5.6-terra\":{\"input\":38403,\"output\":1157,\"cacheRead\":217088,\"cacheWrite\":0,\"turns\":8}},\"plan\":{\"openai-codex/gpt-5.6-sol\":{\"input\":92807,\"output\":7147,\"cacheRead\":464384,\"cacheWrite\":0,\"turns\":20}},\"implement\":{\"openai-codex/gpt-5.6-terra\":{\"input\":244183,\"output\":4327,\"cacheRead\":2065408,\"cacheWrite\":0,\"turns\":31}},\"review\":{\"openai-codex/gpt-5.6-sol\":{\"input\":277751,\"output\":853,\"cacheRead\":717824,\"cacheWrite\":0,\"turns\":9}}},\"features\":{\"retire-plan\":{\"input\":38403,\"output\":1157,\"cacheRead\":217088,\"cacheWrite\":0,\"turns\":8},\"agent-neutral-shell-copy\":{\"input\":521934,\"output\":5180,\"cacheRead\":2783232,\"cacheWrite\":0,\"turns\":40}},\"featureModels\":{\"retire-plan\":{\"openai-codex/gpt-5.6-terra\":{\"input\":38403,\"output\":1157,\"cacheRead\":217088,\"cacheWrite\":0,\"turns\":8}},\"agent-neutral-shell-copy\":{\"openai-codex/gpt-5.6-terra\":{\"input\":244183,\"output\":4327,\"cacheRead\":2065408,\"cacheWrite\":0,\"turns\":31},\"openai-codex/gpt-5.6-sol\":{\"input\":277751,\"output\":853,\"cacheRead\":717824,\"cacheWrite\":0,\"turns\":9}}}}" prices: "{}" -timestamp: 2026-07-20T16:17:18.580Z +timestamp: 2026-07-20T16:18:09.146Z --- # Usage @@ -27,11 +27,17 @@ timestamp: 2026-07-20T16:17:18.580Z | --- | ---: | ---: | ---: | ---: | ---: | ---: | | openai-codex/gpt-5.6-terra | 244183 | 4327 | 2065408 | 0 | 31 | — | +## review + +| model | input | output | cache read | cache write | turns | cost | +| --- | ---: | ---: | ---: | ---: | ---: | ---: | +| openai-codex/gpt-5.6-sol | 277751 | 853 | 717824 | 0 | 9 | — | + ## Per feature | feature | input | output | cache read | cache write | turns | cost | | --- | ---: | ---: | ---: | ---: | ---: | ---: | | retire-plan | 38403 | 1157 | 217088 | 0 | 8 | — | -| agent-neutral-shell-copy | 244183 | 4327 | 2065408 | 0 | 31 | — | +| agent-neutral-shell-copy | 521934 | 5180 | 2783232 | 0 | 40 | — | -Total: 375393 in / 12631 out / 2746880 cache-read / 0 cache-write over 59 turns. Cost unavailable: add every used model rate. +Total: 653144 in / 13484 out / 3464704 cache-read / 0 cache-write over 68 turns. Cost unavailable: add every used model rate. diff --git a/skills/iterator-test/SKILL.md b/skills/iterator-test/SKILL.md index a1454b6..e647bc6 100644 --- a/skills/iterator-test/SKILL.md +++ b/skills/iterator-test/SKILL.md @@ -9,10 +9,12 @@ Generates tests at the **feature** level: pick a feature, review a proposed test plan in the browser, then write focused tests for exactly the behavior that feature covers. The mode is decided by the feature's `status`: -- **Red mode** (`pending`) — no implementation exists yet. Tests are written - from the feature's *contract* (description, implementation notes, snippets, - the module paths in `files`) and are **expected to fail**. Red tests become - the goal `/iterator-implement` drives to green. +- **Red mode** (`pending`) — no implementation exists yet. Draft the exact + test files from the feature's *contract* (description, implementation notes, + snippets, and module paths in `files`) before opening review. The user sees + and approves that complete executable source; the accepted files are then + written unchanged and are **expected to fail**. Red tests become the goal + `/iterator-implement` drives to green. - **Green mode** (`done`) — tests are written against the real code and must pass. @@ -64,40 +66,64 @@ a couple of the `existingTests` files to match the assertion/mocking style exactly. If `runner` is null there is no test setup — recommend one and confirm before adding a dev dependency or config. -Derive the cases: +Derive the cases and, in red mode, their exact source artifacts: - **Red mode:** use the contract — import from the paths the feature *will* - own; assert the behavior the notes promise. + own; assert the behavior the notes promise. Before review, draft the complete + content of every proposed test file, including its target path, imports, + setup, helpers, and assertions. Each case carries a stable `id`, `path`, and + the exact executable `code` block for that test; each block must occur + verbatim in the matching `draftFiles[{path,content}]` artifact. Do not write + these files yet. - **Green mode:** for each file in the contract's `files` (expand globs against `git ls-files`), read the current implementation and its public surface. - In both modes, consider the failure modes implied by the feature's `dependsOn`. -Then open the test-plan UI — the server gathers the payload itself; your -drafted `cases` are the only thing you pass (each -`{ "title": ..., "kind": "happy|edge|integration", "rationale": ... }`): +Then open the test-plan UI — the server gathers the payload itself. Green mode +passes the existing metadata-only `cases`. Red mode also passes each case's +`id`, `path`, and `code`, plus `draftFiles` containing the complete exact files +the user is approving: ```sh node /../iterator/server.mjs << 'TEST_DATA' { "gather": true, "step": "test", "feature": "", - "extra": { "cases": [ { "title": "...", "kind": "happy", "rationale": "..." } ] } } + "extra": { + "cases": [ { "id": "reject-empty", "title": "Reject empty input", + "kind": "edge", "rationale": "Contract requirement", + "path": "test/feature.test.mjs", "code": "test('rejects empty input', () => { ... });" } ], + "draftFiles": [ { "path": "test/feature.test.mjs", + "content": "import test from 'node:test';\n..." } ] + } } TEST_DATA ``` +The red-mode gate refuses approval unless every included case maps to source in +its matching complete file. Unticking a case or commenting sends feedback so +you can regenerate a coherent exact draft before the next approval round. + ### 3. Process the test-plan output (one JSON line) -- `{ "type": "test-approved", "cases": [...] }` → write tests for exactly the - included cases (step 4). -- `{ "type": "test-feedback", "cases": [...], "comment": "..." }` → revise - the plan per the comments and re-run step 2's serve. +- `{ "type": "test-approved", "cases": [...], "draftFiles": [...] }` → in + red mode write the returned `draftFiles` byte-for-byte (step 4); they are the + reviewed source of truth. In green mode, write tests for exactly the included + metadata-only cases as before. +- `{ "type": "test-feedback", "cases": [...], "draftFiles": [...], + "comment": "..." }` → revise both the cases and exact files per the comments, + then re-run step 2's serve. Never carry excluded test code into the next + draft. - `cancel` / `timeout` → relay the result's `report` and stop; no files written. ### 4. Write the tests, run them, and verify the expected color -- Place test files at `suggestedTestPath` (or the detected convention). Reuse - existing helpers/fixtures rather than inventing new ones. +- In **red mode**, write each approved `draftFiles[].content` unchanged to its + approved path. Do not regenerate, reinterpret, or materially alter source + after acceptance; if an approved artifact is inconsistent, return to review. +- In **green mode**, place generated test files at `suggestedTestPath` (or the + detected convention). In both modes, reuse existing helpers/fixtures rather + than inventing new ones. - Keep each feature's tests in their own file(s) so coverage maps back to the feature. - Do **not** weaken assertions to make tests pass — if the implementation diff --git a/skills/iterator/lib/views/test.mjs b/skills/iterator/lib/views/test.mjs index d6d0280..7577f94 100644 --- a/skills/iterator/lib/views/test.mjs +++ b/skills/iterator/lib/views/test.mjs @@ -5,10 +5,11 @@ * * input: { step:"test", branch, feature:{name,description}, runner, * mode, // "red" (feature pending) | "green" (feature done) - * cases:[ {title,kind,rationale} ] } // kind: happy | edge | integration + * cases:[ {id,title,kind,rationale,path,code} ], + * draftFiles:[ {path,content} ] } // exact red-mode files; additive for green mode * output: one JSON line to stdout — - * { type:"test-approved", branch, feature, cases:[ {title,kind,rationale,include} ] } - * { type:"test-feedback", branch, feature, cases:[ {title,kind,rationale,include,comment} ], comment } + * { type:"test-approved", branch, feature, cases:[...], draftFiles:[...] } + * { type:"test-feedback", branch, feature, cases:[...], draftFiles:[...], comment } * plus the shared { type:"cancel" } / { type:"timeout" }. */ import { renderPage } from '../ui.mjs'; @@ -35,6 +36,15 @@ h1{font-family:var(--font-display);font-size:var(--fs-xl);font-weight:600;margin .k-edge{background:var(--bg-yellow);color:var(--dot-yellow)} .k-integration{background:var(--hunk-bg);color:var(--hunk-fg)} .crat{font-size:var(--fs-sm);color:var(--text-muted);margin:8px 0 0;line-height:1.5} +.source-meta,.file-meta{display:flex;align-items:center;justify-content:space-between;gap:var(--sp-2);margin-top:var(--sp-3);font:var(--fs-xs)/1.4 var(--font-mono);color:var(--text-muted)} +.source-meta span,.file-meta span{text-transform:uppercase;letter-spacing:.08em;font-weight:600} +.source-meta code,.file-meta code{color:var(--text);overflow-wrap:anywhere;text-align:right} +.source-code,.file-code{margin:var(--sp-2) 0 0;background:var(--code-bg);border:1px solid var(--border);border-radius:var(--radius-sm);padding:var(--sp-3);overflow:auto;max-height:360px;font:var(--fs-xs)/1.55 var(--font-mono);color:var(--text)} +.source-code code,.file-code code{white-space:pre} +.files{margin-top:var(--sp-5)} +.file{margin-bottom:var(--sp-3)} +.source-warning{display:none;background:var(--bg-yellow);color:var(--dot-yellow);border-radius:var(--radius-sm);padding:var(--sp-2) var(--sp-3);margin-bottom:var(--sp-4);font-size:var(--fs-sm)} +.source-warning.visible{display:block} .sdot{display:inline-block;width:8px;height:8px;border-radius:50%;background:currentColor;margin-right:4px;vertical-align:1px} textarea.ccmt{width:100%;margin-top:8px;background:var(--bg);border:1px solid var(--border);border-radius:6px; color:var(--text);font-size:12px;font-family:inherit;resize:vertical;min-height:44px;outline:none;padding:8px 10px;line-height:1.5} @@ -52,11 +62,16 @@ const BODY = `
-

These are the test cases Claude proposes for this feature. Untick any you don't want, edit a title, or leave a comment. Click Accept to have Claude write and run them; add a comment and it becomes Send review to revise the plan first.

+

+
Exact red-test source is incomplete. Every included case must map to code inside a proposed file before this plan can be accepted.
+
Overall comment (optional) - +
`; @@ -64,21 +79,30 @@ const BODY = ` const JS = ` const feature = D.feature || {}; const ORIG = JSON.parse(JSON.stringify(D.cases || [])); -const state = (D.cases || []).map(c => ({ title:c.title||'', kind:c.kind||'happy', rationale:c.rationale||'', include:true, comment:'' })); +const state = (D.cases || []).map((c,i) => ({ + id:String(c.id||('case-'+(i+1))), title:c.title||'', kind:c.kind||'happy', rationale:c.rationale||'', + path:c.path||'', code:c.code||'', include:c.include!==false, comment:c.comment||'' +})); +const draftFiles = (D.draftFiles || []).map(f => ({ path:String(f.path||''), content:String(f.content||'') })); document.getElementById('title').textContent = 'Test plan — ' + (feature.name || 'feature'); document.getElementById('desc').textContent = feature.description || ''; if(D.runner) document.getElementById('runner').innerHTML = 'Runner: '+esc(D.runner)+''; const modeEl = document.getElementById('mode'); +const hintEl = document.getElementById('hint'); if(D.mode==='red'){ modeEl.className='mode red'; - modeEl.innerHTML='Red mode — this feature is not implemented yet. Accepted tests are written against the feature\\'s contract and are expected to fail until /iterator-implement turns them green.'; + modeEl.innerHTML='Red mode — this feature is not implemented yet. The exact reviewed files are expected to fail until /iterator-implement turns them green.'; + hintEl.innerHTML='Verify the real source for every test and the complete proposed files below. Untick a case or leave feedback to have the Agent revise the source. Accept writes this exact reviewed content without regenerating it.'; }else if(D.mode==='green'){ modeEl.className='mode green'; modeEl.innerHTML='Green mode — this feature is implemented; accepted tests must pass against the current code.'; + hintEl.innerHTML='These are the test cases the Agent proposes for this feature. Untick any you do not want, edit a title, or leave a comment. Accept writes and runs them; feedback asks the Agent to revise the plan first.'; } renderCases(); +renderDraftFiles(); refresh(); +refreshSourceGate(); function renderCases(){ const wrap = document.getElementById('cases'); @@ -88,6 +112,9 @@ function renderCases(){ const div = document.createElement('div'); div.className = 'case' + (c.include?'':' excluded'); const kind = ['happy','edge','integration'].includes(c.kind)?c.kind:'happy'; + const source = D.mode==='red' + ? '
Test source'+esc(c.path)+'
'+esc(c.code)+'
' + : ''; div.innerHTML = '
'+ ''+ @@ -95,13 +122,41 @@ function renderCases(){ ''+kind+''+ '
'+ (c.rationale?'
'+esc(c.rationale)+'
':'')+ + source+ ''; wrap.appendChild(div); }); } -function toggleCase(i,val){ state[i].include=val; renderCases(); refresh(); } -function editTitle(i,val){ state[i].title=val.trim(); refresh(); } -function editComment(i,val){ state[i].comment=val; refresh(); } +function renderDraftFiles(){ + const section = document.getElementById('draft-files'); + if(D.mode!=='red'){ section.hidden=true; return; } + section.hidden=false; + const wrap = document.getElementById('file-list'); + wrap.innerHTML = draftFiles.length ? '' : '
No exact files proposed.
'; + draftFiles.forEach(f => { + const div = document.createElement('div'); + div.className='file'; + div.innerHTML='
Proposed file'+esc(f.path)+'
'+esc(f.content)+'
'; + wrap.appendChild(div); + }); +} +function sourceReady(){ + if(D.mode!=='red') return true; + const included = state.filter(c=>c.include); + if(!included.length) return false; + return draftFiles.length>0 && included.every(c=> + c.path && c.code && draftFiles.some(f=>f.path===c.path && f.content.includes(c.code)) + ); +} +function refreshSourceGate(){ + const ready=sourceReady(); + document.getElementById('source-warning').className='source-warning'+(ready?'':' visible'); + const btn=document.getElementById('primary'); + if(btn) btn.disabled=!(ready||hasChanges()); +} +function toggleCase(i,val){ state[i].include=val; renderCases(); refresh(); refreshSourceGate(); } +function editTitle(i,val){ state[i].title=val.trim(); refresh(); refreshSourceGate(); } +function editComment(i,val){ state[i].comment=val; refresh(); refreshSourceGate(); } function hasChanges(){ if(document.getElementById('global-comment').value.trim()) return true; @@ -109,17 +164,27 @@ function hasChanges(){ if(state.some((c,i)=>!c.include || c.title!==(ORIG[i]&&ORIG[i].title))) return true; return false; } +function casePayload(c, withComment){ + const out={id:c.id,title:c.title,kind:c.kind,rationale:c.rationale,path:c.path,code:c.code,include:c.include}; + if(withComment) out.comment=c.comment.trim(); + return out; +} function onPrimary(){ const changed = hasChanges(); + if(!sourceReady() && !changed){ + alert('Exact red-test source is incomplete. Ask the Agent to revise the proposal.'); + return; + } const comment = document.getElementById('global-comment').value.trim(); + const reviewedFiles=draftFiles.map(f=>({path:f.path,content:f.content})); if(changed){ post({ type:'test-feedback', branch:D.branch||'HEAD', feature:feature.name, - cases: state.map(c=>({title:c.title,kind:c.kind,rationale:c.rationale,include:c.include,comment:c.comment.trim()})), - comment }, 'Sent — Claude is revising the test plan'); + cases: state.map(c=>casePayload(c,true)), draftFiles:reviewedFiles, + comment }, 'Sent — Agent is revising the test plan'); } else { post({ type:'test-approved', branch:D.branch||'HEAD', feature:feature.name, - cases: state.filter(c=>c.include).map(c=>({title:c.title,kind:c.kind,rationale:c.rationale,include:true})) }, - 'Accepted — Claude is writing tests'); + cases: state.filter(c=>c.include).map(c=>casePayload(c,false)), draftFiles:reviewedFiles }, + 'Accepted — Agent is writing reviewed tests'); } } `; diff --git a/test/client-js-parse.test.mjs b/test/client-js-parse.test.mjs index 8f45600..5678582 100644 --- a/test/client-js-parse.test.mjs +++ b/test/client-js-parse.test.mjs @@ -78,7 +78,28 @@ const MIN_DATA = { ], }, settings: { branch: "main", plan: "P", settings: {}, schema: [] }, - test: { branch: "main", feature: { name: "f" }, tests: [] }, + test: { + branch: "main", + mode: "red", + feature: { name: "f" }, + cases: [ + { + id: "works", + title: "Works", + kind: "happy", + rationale: "Contract", + path: "test/f.test.mjs", + code: "test('works', () => assert.equal(run(), ''));", + }, + ], + draftFiles: [ + { + path: "test/f.test.mjs", + content: + "import test from 'node:test';\ntest('works', () => assert.equal(run(), ''));\n", + }, + ], + }, usage: { branch: "main", plan: "P", usage: { steps: [] } }, }; diff --git a/test/server.test.mjs b/test/server.test.mjs index df64f98..cc70d84 100644 --- a/test/server.test.mjs +++ b/test/server.test.mjs @@ -662,14 +662,29 @@ const SMOKE = [ ], [ "test", - "test cases Claude proposes", + "Verify the real source for every test", { step: "test", branch: "test", + mode: "red", feature: { name: "auth-middleware", description: "JWT middleware" }, runner: "vitest", cases: [ - { title: "passes a valid token", kind: "happy", rationale: "core" }, + { + id: "valid-token", + title: "passes a valid token", + kind: "happy", + rationale: "core", + path: "test/auth.test.mjs", + code: "test('valid token', () => expect(authenticate()).toBe(true));", + }, + ], + draftFiles: [ + { + path: "test/auth.test.mjs", + content: + "test('valid token', () => expect(authenticate()).toBe(true));\n", + }, ], }, ], diff --git a/test/ui.test.mjs b/test/ui.test.mjs index abcd8ec..1c8cb34 100644 --- a/test/ui.test.mjs +++ b/test/ui.test.mjs @@ -128,6 +128,43 @@ test("shared client JS wires read-only mode while the agent works", () => { ); }); +test("red test review exposes and preserves exact source artifacts", async () => { + const { render } = await import("../lib/views/test.mjs"); + const caseCode = "test('rejects ', () => assert.throws(() => parse('')));"; + const html = render({ + branch: "main", + mode: "red", + feature: { name: "parser", description: "Parse input" }, + runner: "npm test", + cases: [ + { + id: "reject-empty", + title: "Reject empty input", + kind: "edge", + rationale: "Required by the contract.", + path: "test/parser.test.mjs", + code: caseCode, + }, + ], + draftFiles: [ + { + path: "test/parser.test.mjs", + content: `import test from 'node:test';\n${caseCode}\n`, + }, + ], + }); + assert.match(html, /Verify the real source for every test/); + assert.match(html, /Complete proposed test files/); + assert.match(html, /f\.content\.includes\(c\.code\)/); + assert.match(html, /btn\.disabled=!\(ready\|\|hasChanges\(\)\)/); + assert.match(html, /if\(!sourceReady\(\) && !changed\)/); + assert.match(html, /draftFiles:reviewedFiles/); + assert.match(html, /code:c\.code/); + assert.match(html, /Accepted — Agent is writing reviewed tests/); + assert.match(html, /\\u003cempty>/, "embedded source remains inert"); + assert.doesNotMatch(html, /Claude/); +}); + test("mdToHtml preserves query strings while escaping href quotes", () => { const html = renderPage({ step: "t", From a0081a06ac557b746bdcc42144b8e87295cfbeba Mon Sep 17 00:00:00 2001 From: Christoph Maier Date: Mon, 20 Jul 2026 18:25:46 +0200 Subject: [PATCH 05/20] chore(iterator): record feature commit --- memory/features/review-exact-red-test-source.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/memory/features/review-exact-red-test-source.md b/memory/features/review-exact-red-test-source.md index 5e977a1..a5d44a8 100644 --- a/memory/features/review-exact-red-test-source.md +++ b/memory/features/review-exact-red-test-source.md @@ -7,8 +7,12 @@ size: medium depends_on: [] files: ["skills/iterator-test/SKILL.md", "lib/views/test.mjs", "test/ui.test.mjs", "test/client-js-parse.test.mjs"] memories: [pitfalls/client-js-template-literal-escaping, decisions/iterator-dashboard-feature-workflow, decisions/parallel-feature-waves-and-consolidated-review, decisions/review-navigation-and-work-context, decisions/settings-close-returns-to-work, decisions/streamline-backlog-planning-and-knowledge-actions] -timestamp: "2026-07-20T16:25:46.746Z" +timestamp: "2026-07-20T16:25:46.819Z" tags: [] +commits: + - sha: bbb2be96942cd1a87c440fb58f7d0a6fec5c5975 + kind: implement + date: 2026-07-20 --- # Implementation notes From d539f33d5980be6b054b797e47c6779a5f99face Mon Sep 17 00:00:00 2001 From: Christoph Maier Date: Mon, 20 Jul 2026 18:46:30 +0200 Subject: [PATCH 06/20] chore(iterator): record feature commits and memory updates --- memory/features/index.md | 2 +- .../features/review-exact-red-test-source.md | 5 +++-- memory/log.md | 1 + memory/state.md | 4 ++-- memory/usage.md | 8 +++++--- skills/iterator/lib/views/test.mjs | 19 +++++++++++++------ 6 files changed, 25 insertions(+), 14 deletions(-) diff --git a/memory/features/index.md b/memory/features/index.md index 98bfa75..1912ee3 100644 --- a/memory/features/index.md +++ b/memory/features/index.md @@ -1,5 +1,5 @@ # Features * [Use Agent wording in the dashboard shell](agent-neutral-shell-copy.md) - ✅ done · small · Shared dashboard status, submission, cancellation, and working messages refer to the active coding Agent rather than Claude. -* [Review exact red test source](review-exact-red-test-source.md) - ⬜ pending · medium · Users can inspect and approve the complete executable source for every proposed red-mode test before files are written. +* [Review exact red test source](review-exact-red-test-source.md) - ✅ done · medium · Users can inspect and approve the complete executable source for every proposed red-mode test before files are written. * [Use Agent wording in workflow views](agent-neutral-workflow-copy.md) - ⬜ pending · small · depends: review-exact-red-test-source, agent-neutral-shell-copy · Interactive plan, feature, review, memory, and question views consistently address the provider-neutral Agent. diff --git a/memory/features/review-exact-red-test-source.md b/memory/features/review-exact-red-test-source.md index a5d44a8..94d7be2 100644 --- a/memory/features/review-exact-red-test-source.md +++ b/memory/features/review-exact-red-test-source.md @@ -2,17 +2,18 @@ type: Feature title: Review exact red test source description: Users can inspect and approve the complete executable source for every proposed red-mode test before files are written. -status: implemented +status: done size: medium depends_on: [] files: ["skills/iterator-test/SKILL.md", "lib/views/test.mjs", "test/ui.test.mjs", "test/client-js-parse.test.mjs"] memories: [pitfalls/client-js-template-literal-escaping, decisions/iterator-dashboard-feature-workflow, decisions/parallel-feature-waves-and-consolidated-review, decisions/review-navigation-and-work-context, decisions/settings-close-returns-to-work, decisions/streamline-backlog-planning-and-knowledge-actions] -timestamp: "2026-07-20T16:25:46.819Z" +timestamp: "2026-07-20T16:46:30.851Z" tags: [] commits: - sha: bbb2be96942cd1a87c440fb58f7d0a6fec5c5975 kind: implement date: 2026-07-20 +done: 2026-07-20 --- # Implementation notes diff --git a/memory/log.md b/memory/log.md index 762a943..8ab5086 100644 --- a/memory/log.md +++ b/memory/log.md @@ -1,6 +1,7 @@ # iterator update log ## 2026-07-20 +* **Review**: Accepted [Review exact red test source](/features/review-exact-red-test-source.md) (committed as feature(review-exact-red-test-source)). * **Implementation**: Committed feature(review-exact-red-test-source) on branch iterator/safe-role-model-handoff; awaiting review. * **Review**: Reviewed [Use Agent wording in the dashboard shell](/features/agent-neutral-shell-copy.md); approved (agent). * **Review**: Accepted [Use Agent wording in the dashboard shell](/features/agent-neutral-shell-copy.md) (committed as feature(agent-neutral-shell-copy)). diff --git a/memory/state.md b/memory/state.md index 4d491c9..ad958d1 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: review-exact-red-test-source strikes: "{}" escalation: null -timestamp: 2026-07-20T16:18:09.398Z +timestamp: 2026-07-20T16:45:53.753Z --- Runtime flow state; read via gather, written only by the state op. diff --git a/memory/usage.md b/memory/usage.md index a67e272..f23557a 100644 --- a/memory/usage.md +++ b/memory/usage.md @@ -2,9 +2,9 @@ type: Usage title: Token usage description: Per-step model/token ledger and optional project-owned pricing for the active plan — written only by the usage op. -totals: "{\"steps\":{\"hub\":{\"openai-codex/gpt-5.6-terra\":{\"input\":38403,\"output\":1157,\"cacheRead\":217088,\"cacheWrite\":0,\"turns\":8}},\"plan\":{\"openai-codex/gpt-5.6-sol\":{\"input\":92807,\"output\":7147,\"cacheRead\":464384,\"cacheWrite\":0,\"turns\":20}},\"implement\":{\"openai-codex/gpt-5.6-terra\":{\"input\":244183,\"output\":4327,\"cacheRead\":2065408,\"cacheWrite\":0,\"turns\":31}},\"review\":{\"openai-codex/gpt-5.6-sol\":{\"input\":277751,\"output\":853,\"cacheRead\":717824,\"cacheWrite\":0,\"turns\":9}}},\"features\":{\"retire-plan\":{\"input\":38403,\"output\":1157,\"cacheRead\":217088,\"cacheWrite\":0,\"turns\":8},\"agent-neutral-shell-copy\":{\"input\":521934,\"output\":5180,\"cacheRead\":2783232,\"cacheWrite\":0,\"turns\":40}},\"featureModels\":{\"retire-plan\":{\"openai-codex/gpt-5.6-terra\":{\"input\":38403,\"output\":1157,\"cacheRead\":217088,\"cacheWrite\":0,\"turns\":8}},\"agent-neutral-shell-copy\":{\"openai-codex/gpt-5.6-terra\":{\"input\":244183,\"output\":4327,\"cacheRead\":2065408,\"cacheWrite\":0,\"turns\":31},\"openai-codex/gpt-5.6-sol\":{\"input\":277751,\"output\":853,\"cacheRead\":717824,\"cacheWrite\":0,\"turns\":9}}}}" +totals: "{\"steps\":{\"hub\":{\"openai-codex/gpt-5.6-terra\":{\"input\":38403,\"output\":1157,\"cacheRead\":217088,\"cacheWrite\":0,\"turns\":8}},\"plan\":{\"openai-codex/gpt-5.6-sol\":{\"input\":92807,\"output\":7147,\"cacheRead\":464384,\"cacheWrite\":0,\"turns\":20}},\"implement\":{\"openai-codex/gpt-5.6-terra\":{\"input\":244183,\"output\":4327,\"cacheRead\":2065408,\"cacheWrite\":0,\"turns\":31},\"openai-codex/gpt-5.6-sol\":{\"input\":385552,\"output\":15997,\"cacheRead\":4586496,\"cacheWrite\":0,\"turns\":33}},\"review\":{\"openai-codex/gpt-5.6-sol\":{\"input\":277751,\"output\":853,\"cacheRead\":717824,\"cacheWrite\":0,\"turns\":9}}},\"features\":{\"retire-plan\":{\"input\":38403,\"output\":1157,\"cacheRead\":217088,\"cacheWrite\":0,\"turns\":8},\"agent-neutral-shell-copy\":{\"input\":521934,\"output\":5180,\"cacheRead\":2783232,\"cacheWrite\":0,\"turns\":40},\"review-exact-red-test-source\":{\"input\":385552,\"output\":15997,\"cacheRead\":4586496,\"cacheWrite\":0,\"turns\":33}},\"featureModels\":{\"retire-plan\":{\"openai-codex/gpt-5.6-terra\":{\"input\":38403,\"output\":1157,\"cacheRead\":217088,\"cacheWrite\":0,\"turns\":8}},\"agent-neutral-shell-copy\":{\"openai-codex/gpt-5.6-terra\":{\"input\":244183,\"output\":4327,\"cacheRead\":2065408,\"cacheWrite\":0,\"turns\":31},\"openai-codex/gpt-5.6-sol\":{\"input\":277751,\"output\":853,\"cacheRead\":717824,\"cacheWrite\":0,\"turns\":9}},\"review-exact-red-test-source\":{\"openai-codex/gpt-5.6-sol\":{\"input\":385552,\"output\":15997,\"cacheRead\":4586496,\"cacheWrite\":0,\"turns\":33}}}}" prices: "{}" -timestamp: 2026-07-20T16:18:09.146Z +timestamp: 2026-07-20T16:45:53.504Z --- # Usage @@ -26,6 +26,7 @@ timestamp: 2026-07-20T16:18:09.146Z | model | input | output | cache read | cache write | turns | cost | | --- | ---: | ---: | ---: | ---: | ---: | ---: | | openai-codex/gpt-5.6-terra | 244183 | 4327 | 2065408 | 0 | 31 | — | +| openai-codex/gpt-5.6-sol | 385552 | 15997 | 4586496 | 0 | 33 | — | ## review @@ -39,5 +40,6 @@ timestamp: 2026-07-20T16:18:09.146Z | --- | ---: | ---: | ---: | ---: | ---: | ---: | | retire-plan | 38403 | 1157 | 217088 | 0 | 8 | — | | agent-neutral-shell-copy | 521934 | 5180 | 2783232 | 0 | 40 | — | +| review-exact-red-test-source | 385552 | 15997 | 4586496 | 0 | 33 | — | -Total: 653144 in / 13484 out / 3464704 cache-read / 0 cache-write over 68 turns. Cost unavailable: add every used model rate. +Total: 1038696 in / 29481 out / 8051200 cache-read / 0 cache-write over 101 turns. Cost unavailable: add every used model rate. diff --git a/skills/iterator/lib/views/test.mjs b/skills/iterator/lib/views/test.mjs index 7577f94..35ef809 100644 --- a/skills/iterator/lib/views/test.mjs +++ b/skills/iterator/lib/views/test.mjs @@ -12,7 +12,7 @@ * { type:"test-feedback", branch, feature, cases:[...], draftFiles:[...], comment } * plus the shared { type:"cancel" } / { type:"timeout" }. */ -import { renderPage } from '../ui.mjs'; +import { renderPage } from "../ui.mjs"; const CSS = ` .main{max-width:820px;margin:0 auto;padding:24px var(--sp-5)} @@ -190,9 +190,16 @@ function onPrimary(){ `; export function render(data) { - return renderPage({ - step: 'test', subtitle: '/ test', branch: data.branch, title: (data.feature && data.feature.name), - data, css: CSS, body: BODY, clientJs: JS, - primaryIdle: 'Accept', primaryChanged: 'Send review', - }); + return renderPage({ + step: "test", + subtitle: "/ test", + branch: data.branch, + title: data.feature && data.feature.name, + data, + css: CSS, + body: BODY, + clientJs: JS, + primaryIdle: "Accept", + primaryChanged: "Send review", + }); } From 9544c699b49cb5926e8749b10607b05838e0adc0 Mon Sep 17 00:00:00 2001 From: Christoph Maier Date: Mon, 20 Jul 2026 18:49:54 +0200 Subject: [PATCH 07/20] feature(agent-neutral-workflow-copy): Use Agent wording across workflow views Feature: agent-neutral-workflow-copy --- lib/views/feature.mjs | 12 ++++---- lib/views/memory-review.mjs | 4 +-- lib/views/plan.mjs | 8 ++--- lib/views/question.mjs | 4 +-- lib/views/review.mjs | 4 +-- lib/views/widgets.mjs | 2 +- .../features/agent-neutral-workflow-copy.md | 4 +-- .../features/review-exact-red-test-source.md | 8 ++++- memory/log.md | 2 ++ memory/state.md | 6 ++-- memory/usage.md | 10 +++---- skills/iterator/lib/views/feature.mjs | 12 ++++---- skills/iterator/lib/views/memory-review.mjs | 4 +-- skills/iterator/lib/views/plan.mjs | 8 ++--- skills/iterator/lib/views/question.mjs | 4 +-- skills/iterator/lib/views/review.mjs | 4 +-- skills/iterator/lib/views/widgets.mjs | 2 +- test/agent-copy.test.mjs | 29 +++++++++++++++++++ test/ui.test.mjs | 2 +- 19 files changed, 83 insertions(+), 46 deletions(-) create mode 100644 test/agent-copy.test.mjs diff --git a/lib/views/feature.mjs b/lib/views/feature.mjs index 20151af..68680ef 100644 --- a/lib/views/feature.mjs +++ b/lib/views/feature.mjs @@ -142,7 +142,7 @@ function makeCard(c){ (snippets?'
Relevant snippets
'+snippets:'')+ ((c.memories&&c.memories.length)?'
Relevant memories
'+c.memories.map(m=>''+esc(m)+'').join('')+'
':'')+ (files?'
Files
'+files+'
':'')+ - (done?'':'
Comment
')+ + (done?'':'
Comment
')+ ''; // Handlers are wired with closures (never inline attribute strings), so // feature names containing quotes/backslashes can't break or inject markup. @@ -184,20 +184,20 @@ function updateDesc(name,desc){ function setComment(name,val){ val=val.trim(); if(val) S.comments[name]=val; else delete S.comments[name]; refresh(); } function splitFeature(name){ const c=S.features.find(c=>c.name===name); if(!c) return; - if(!confirm('Split "'+name+'"? Claude will split it into single-feature features and reopen this view.')) return; - post({type:'split-request', branch:D.branch||'HEAD', feature:name, content:JSON.stringify(c)}, 'Splitting — Claude is working…'); + if(!confirm('Split "'+name+'"? The Agent will split it into single-feature features and reopen this view.')) return; + post({type:'split-request', branch:D.branch||'HEAD', feature:name, content:JSON.stringify(c)}, 'Splitting — Agent is working…'); } function toggleMerge(name){ S.mergeSel = S.mergeSel===name?null:name; renderCards(); } function completeMerge(target){ const a=S.mergeSel; if(!a||a===target){ S.mergeSel=null; renderCards(); return; } - if(!confirm('Merge "'+a+'" and "'+target+'"? Claude will combine them and reopen this view.')) return; - post({type:'merge-request', branch:D.branch||'HEAD', features:[a,target]}, 'Merging — Claude is working…'); + if(!confirm('Merge "'+a+'" and "'+target+'"? The Agent will combine them and reopen this view.')) return; + post({type:'merge-request', branch:D.branch||'HEAD', features:[a,target]}, 'Merging — Agent is working…'); } function collectComments(){ return Object.entries(S.comments).map(([feature,comment])=>({feature,comment})); } function hasChanges(){ return !!(S.moves.length||S.renames.length||S.descUpdates.length||collectComments().length); } function onPrimary(){ if(hasChanges()){ - post({type:'plan-adjustments', branch:D.branch||'HEAD', moves:S.moves, renames:S.renames, descUpdates:S.descUpdates, comments:collectComments()}, 'Sent — Claude is updating the features'); + post({type:'plan-adjustments', branch:D.branch||'HEAD', moves:S.moves, renames:S.renames, descUpdates:S.descUpdates, comments:collectComments()}, 'Sent — Agent is updating the features'); } else { post({type:'plan-approved', branch:D.branch||'HEAD'}, 'Features accepted — run /iterator-implement to build them'); } diff --git a/lib/views/memory-review.mjs b/lib/views/memory-review.mjs index 8a1642f..cd6dd9d 100644 --- a/lib/views/memory-review.mjs +++ b/lib/views/memory-review.mjs @@ -117,7 +117,7 @@ function cardHtml(m) {
${verdictButtons(m)}
+ placeholder="Comment — sends this memory back to the Agent for revision">
`; } @@ -313,7 +313,7 @@ ${areaSections({ ...data, mode }, memories)} -

Any comment sends the review back to Claude for another round. +

Any comment sends the review back to the Agent for another round. With no comments, the header button accepts the verdicts as chosen above.

`; diff --git a/lib/views/plan.mjs b/lib/views/plan.mjs index 470bb12..ce37037 100644 --- a/lib/views/plan.mjs +++ b/lib/views/plan.mjs @@ -85,7 +85,7 @@ const PLAN_BODY = `

-

Each section is rendered markdown — click any section to edit (save with blur or ⌘/Ctrl+Enter). Use the 💬 icon to leave a comment on a section, and confirm the external dependencies below — new packages, libraries, or services this plan needs, never tasks or todos. When everything looks right click Accept; if you edit anything or add a comment the button becomes Send review so Claude can revise first.

+

Each section is rendered markdown — click any section to edit (save with blur or ⌘/Ctrl+Enter). Use the 💬 icon to leave a comment on a section, and confirm the external dependencies below — new packages, libraries, or services this plan needs, never tasks or todos. When everything looks right click Accept; if you edit anything or add a comment the button becomes Send review so the Agent can revise first.

External dependencies
@@ -94,7 +94,7 @@ const PLAN_BODY = `
Global comment (optional)
@@ -106,7 +106,7 @@ const PLAN_BODY = `
-
@@ -236,7 +236,7 @@ function onPrimary(){ dependencies: deps, comments: collectComments(), comment: document.getElementById('global-comment').value.trim(), - }, changed ? 'Review sent to Claude' : 'Plan approved'); + }, changed ? 'Review sent to Agent' : 'Plan approved'); } `; diff --git a/lib/views/question.mjs b/lib/views/question.mjs index aa727f9..f876c36 100644 --- a/lib/views/question.mjs +++ b/lib/views/question.mjs @@ -48,7 +48,7 @@ const BODY = `
Other — type your own answer
-

Answering here returns control to Claude — same as answering in the terminal.

+

Answering here returns control to the Agent — same as answering in the terminal.

`; @@ -88,7 +88,7 @@ function hasChanges(){ return choice != null || Boolean(ft && ft.value.trim()); function onPrimary(){ const text = ft && ft.value.trim() ? ft.value.trim() : null; if(!choice && !text) return; - post({ type:'answer', choice, text }, 'Answer sent to Claude'); + post({ type:'answer', choice, text }, 'Answer sent to Agent'); } `; diff --git a/lib/views/review.mjs b/lib/views/review.mjs index 690b59a..cbeca1b 100644 --- a/lib/views/review.mjs +++ b/lib/views/review.mjs @@ -517,10 +517,10 @@ function onPrimary(){ const unc = Object.entries(S.unc).map(([path, feature]) => ({ path, feature })); if(unc.length) out.uncategorized = unc; if(MEM.length) out.memory = memDecisions(); - post(out, D.source==='commits' ? 'Accepted — Claude is finalizing' : 'Accepted — Claude is committing'); + post(out, D.source==='commits' ? 'Accepted — Agent is finalizing' : 'Accepted — Agent is committing'); return; } - post(buildFeedbackObj(), 'Review sent to Claude'); + post(buildFeedbackObj(), 'Review sent to Agent'); } // Bootstrap LAST: selectFeature -> renderHunks reads consts declared through diff --git a/lib/views/widgets.mjs b/lib/views/widgets.mjs index 18a6280..e9e0c66 100644 --- a/lib/views/widgets.mjs +++ b/lib/views/widgets.mjs @@ -59,7 +59,7 @@ button.act.danger-armed{background:var(--bg-red);border-color:var(--del-fg);colo export const WIDGETS_JS = ` // Shared dashboard helpers (lib/views/widgets.mjs). function action(act, feature, msg, prompt){ - return post({ type:'action', action: act, feature: feature || null, prompt: prompt || null }, msg || 'Sent to Claude'); + return post({ type:'action', action: act, feature: feature || null, prompt: prompt || null }, msg || 'Sent to Agent'); } function testBadge(c){ if(!c.testsStatus || c.testsStatus==='none') return ''; diff --git a/memory/features/agent-neutral-workflow-copy.md b/memory/features/agent-neutral-workflow-copy.md index c20993e..9d558e6 100644 --- a/memory/features/agent-neutral-workflow-copy.md +++ b/memory/features/agent-neutral-workflow-copy.md @@ -2,12 +2,12 @@ type: Feature title: Use Agent wording in workflow views description: Interactive plan, feature, review, memory, and question views consistently address the provider-neutral Agent. -status: pending +status: implemented size: small depends_on: [review-exact-red-test-source, agent-neutral-shell-copy] files: ["lib/views/plan.mjs", "lib/views/feature.mjs", "lib/views/review.mjs", "lib/views/memory-review.mjs", "lib/views/question.mjs", "lib/views/widgets.mjs", "test/agent-copy.test.mjs"] memories: [patterns/agent-reviewed-memory-writes, patterns/safe-browser-rendering, decisions/backlog-planning-and-feature-waves, decisions/iterator-dashboard-feature-workflow, decisions/parallel-feature-waves-and-consolidated-review, decisions/review-navigation-and-work-context, decisions/streamline-backlog-planning-and-knowledge-actions] -timestamp: "2026-07-20T16:13:44.335Z" +timestamp: "2026-07-20T16:49:54.271Z" tags: [] --- diff --git a/memory/features/review-exact-red-test-source.md b/memory/features/review-exact-red-test-source.md index 94d7be2..7e4bc11 100644 --- a/memory/features/review-exact-red-test-source.md +++ b/memory/features/review-exact-red-test-source.md @@ -7,13 +7,14 @@ size: medium depends_on: [] files: ["skills/iterator-test/SKILL.md", "lib/views/test.mjs", "test/ui.test.mjs", "test/client-js-parse.test.mjs"] memories: [pitfalls/client-js-template-literal-escaping, decisions/iterator-dashboard-feature-workflow, decisions/parallel-feature-waves-and-consolidated-review, decisions/review-navigation-and-work-context, decisions/settings-close-returns-to-work, decisions/streamline-backlog-planning-and-knowledge-actions] -timestamp: "2026-07-20T16:46:30.851Z" +timestamp: "2026-07-20T16:46:37.169Z" tags: [] commits: - sha: bbb2be96942cd1a87c440fb58f7d0a6fec5c5975 kind: implement date: 2026-07-20 done: 2026-07-20 +reviewed: 2026-07-20 --- # Implementation notes @@ -35,3 +36,8 @@ post({ type:'test-approved', branch:D.branch||'HEAD', feature:feature.name, # Blast radius Changes the manual red-test approval payload consumed by the test skill; malformed round-tripping could authorize different tests or break test-plan rendering. + +# Review + +## 2026-07-20 +* **Approved** _(agent review: openai-codex/gpt-5.6-sol)_ — Approved: red-mode review now exposes per-case executable source and complete files, validates mappings, preserves exact approved artifacts, safely supports feedback, and retains green-mode compatibility. diff --git a/memory/log.md b/memory/log.md index 8ab5086..bb12bc0 100644 --- a/memory/log.md +++ b/memory/log.md @@ -1,6 +1,8 @@ # iterator update log ## 2026-07-20 +* **Implementation**: Committed feature(agent-neutral-workflow-copy) on branch iterator/safe-role-model-handoff; awaiting review. +* **Review**: Reviewed [Review exact red test source](/features/review-exact-red-test-source.md); approved (agent). * **Review**: Accepted [Review exact red test source](/features/review-exact-red-test-source.md) (committed as feature(review-exact-red-test-source)). * **Implementation**: Committed feature(review-exact-red-test-source) on branch iterator/safe-role-model-handoff; awaiting review. * **Review**: Reviewed [Use Agent wording in the dashboard shell](/features/agent-neutral-shell-copy.md); approved (agent). diff --git a/memory/state.md b/memory/state.md index ad958d1..71cc2fc 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: review-exact-red-test-source +phase: implementing +active_feature: agent-neutral-workflow-copy strikes: "{}" escalation: null -timestamp: 2026-07-20T16:45:53.753Z +timestamp: 2026-07-20T16:46:50.842Z --- Runtime flow state; read via gather, written only by the state op. diff --git a/memory/usage.md b/memory/usage.md index f23557a..d09e14e 100644 --- a/memory/usage.md +++ b/memory/usage.md @@ -2,9 +2,9 @@ type: Usage title: Token usage description: Per-step model/token ledger and optional project-owned pricing for the active plan — written only by the usage op. -totals: "{\"steps\":{\"hub\":{\"openai-codex/gpt-5.6-terra\":{\"input\":38403,\"output\":1157,\"cacheRead\":217088,\"cacheWrite\":0,\"turns\":8}},\"plan\":{\"openai-codex/gpt-5.6-sol\":{\"input\":92807,\"output\":7147,\"cacheRead\":464384,\"cacheWrite\":0,\"turns\":20}},\"implement\":{\"openai-codex/gpt-5.6-terra\":{\"input\":244183,\"output\":4327,\"cacheRead\":2065408,\"cacheWrite\":0,\"turns\":31},\"openai-codex/gpt-5.6-sol\":{\"input\":385552,\"output\":15997,\"cacheRead\":4586496,\"cacheWrite\":0,\"turns\":33}},\"review\":{\"openai-codex/gpt-5.6-sol\":{\"input\":277751,\"output\":853,\"cacheRead\":717824,\"cacheWrite\":0,\"turns\":9}}},\"features\":{\"retire-plan\":{\"input\":38403,\"output\":1157,\"cacheRead\":217088,\"cacheWrite\":0,\"turns\":8},\"agent-neutral-shell-copy\":{\"input\":521934,\"output\":5180,\"cacheRead\":2783232,\"cacheWrite\":0,\"turns\":40},\"review-exact-red-test-source\":{\"input\":385552,\"output\":15997,\"cacheRead\":4586496,\"cacheWrite\":0,\"turns\":33}},\"featureModels\":{\"retire-plan\":{\"openai-codex/gpt-5.6-terra\":{\"input\":38403,\"output\":1157,\"cacheRead\":217088,\"cacheWrite\":0,\"turns\":8}},\"agent-neutral-shell-copy\":{\"openai-codex/gpt-5.6-terra\":{\"input\":244183,\"output\":4327,\"cacheRead\":2065408,\"cacheWrite\":0,\"turns\":31},\"openai-codex/gpt-5.6-sol\":{\"input\":277751,\"output\":853,\"cacheRead\":717824,\"cacheWrite\":0,\"turns\":9}},\"review-exact-red-test-source\":{\"openai-codex/gpt-5.6-sol\":{\"input\":385552,\"output\":15997,\"cacheRead\":4586496,\"cacheWrite\":0,\"turns\":33}}}}" +totals: "{\"steps\":{\"hub\":{\"openai-codex/gpt-5.6-terra\":{\"input\":38403,\"output\":1157,\"cacheRead\":217088,\"cacheWrite\":0,\"turns\":8}},\"plan\":{\"openai-codex/gpt-5.6-sol\":{\"input\":92807,\"output\":7147,\"cacheRead\":464384,\"cacheWrite\":0,\"turns\":20}},\"implement\":{\"openai-codex/gpt-5.6-terra\":{\"input\":244183,\"output\":4327,\"cacheRead\":2065408,\"cacheWrite\":0,\"turns\":31},\"openai-codex/gpt-5.6-sol\":{\"input\":385552,\"output\":15997,\"cacheRead\":4586496,\"cacheWrite\":0,\"turns\":33}},\"review\":{\"openai-codex/gpt-5.6-sol\":{\"input\":668749,\"output\":2049,\"cacheRead\":2107392,\"cacheWrite\":0,\"turns\":18}}},\"features\":{\"retire-plan\":{\"input\":38403,\"output\":1157,\"cacheRead\":217088,\"cacheWrite\":0,\"turns\":8},\"agent-neutral-shell-copy\":{\"input\":521934,\"output\":5180,\"cacheRead\":2783232,\"cacheWrite\":0,\"turns\":40},\"review-exact-red-test-source\":{\"input\":776550,\"output\":17193,\"cacheRead\":5976064,\"cacheWrite\":0,\"turns\":42}},\"featureModels\":{\"retire-plan\":{\"openai-codex/gpt-5.6-terra\":{\"input\":38403,\"output\":1157,\"cacheRead\":217088,\"cacheWrite\":0,\"turns\":8}},\"agent-neutral-shell-copy\":{\"openai-codex/gpt-5.6-terra\":{\"input\":244183,\"output\":4327,\"cacheRead\":2065408,\"cacheWrite\":0,\"turns\":31},\"openai-codex/gpt-5.6-sol\":{\"input\":277751,\"output\":853,\"cacheRead\":717824,\"cacheWrite\":0,\"turns\":9}},\"review-exact-red-test-source\":{\"openai-codex/gpt-5.6-sol\":{\"input\":776550,\"output\":17193,\"cacheRead\":5976064,\"cacheWrite\":0,\"turns\":42}}}}" prices: "{}" -timestamp: 2026-07-20T16:45:53.504Z +timestamp: 2026-07-20T16:46:50.579Z --- # Usage @@ -32,7 +32,7 @@ timestamp: 2026-07-20T16:45:53.504Z | model | input | output | cache read | cache write | turns | cost | | --- | ---: | ---: | ---: | ---: | ---: | ---: | -| openai-codex/gpt-5.6-sol | 277751 | 853 | 717824 | 0 | 9 | — | +| openai-codex/gpt-5.6-sol | 668749 | 2049 | 2107392 | 0 | 18 | — | ## Per feature @@ -40,6 +40,6 @@ timestamp: 2026-07-20T16:45:53.504Z | --- | ---: | ---: | ---: | ---: | ---: | ---: | | retire-plan | 38403 | 1157 | 217088 | 0 | 8 | — | | agent-neutral-shell-copy | 521934 | 5180 | 2783232 | 0 | 40 | — | -| review-exact-red-test-source | 385552 | 15997 | 4586496 | 0 | 33 | — | +| review-exact-red-test-source | 776550 | 17193 | 5976064 | 0 | 42 | — | -Total: 1038696 in / 29481 out / 8051200 cache-read / 0 cache-write over 101 turns. Cost unavailable: add every used model rate. +Total: 1429694 in / 30677 out / 9440768 cache-read / 0 cache-write over 110 turns. Cost unavailable: add every used model rate. diff --git a/skills/iterator/lib/views/feature.mjs b/skills/iterator/lib/views/feature.mjs index 20151af..68680ef 100644 --- a/skills/iterator/lib/views/feature.mjs +++ b/skills/iterator/lib/views/feature.mjs @@ -142,7 +142,7 @@ function makeCard(c){ (snippets?'
Relevant snippets
'+snippets:'')+ ((c.memories&&c.memories.length)?'
Relevant memories
'+c.memories.map(m=>''+esc(m)+'').join('')+'
':'')+ (files?'
Files
'+files+'
':'')+ - (done?'':'
Comment
')+ + (done?'':'
Comment
')+ ''; // Handlers are wired with closures (never inline attribute strings), so // feature names containing quotes/backslashes can't break or inject markup. @@ -184,20 +184,20 @@ function updateDesc(name,desc){ function setComment(name,val){ val=val.trim(); if(val) S.comments[name]=val; else delete S.comments[name]; refresh(); } function splitFeature(name){ const c=S.features.find(c=>c.name===name); if(!c) return; - if(!confirm('Split "'+name+'"? Claude will split it into single-feature features and reopen this view.')) return; - post({type:'split-request', branch:D.branch||'HEAD', feature:name, content:JSON.stringify(c)}, 'Splitting — Claude is working…'); + if(!confirm('Split "'+name+'"? The Agent will split it into single-feature features and reopen this view.')) return; + post({type:'split-request', branch:D.branch||'HEAD', feature:name, content:JSON.stringify(c)}, 'Splitting — Agent is working…'); } function toggleMerge(name){ S.mergeSel = S.mergeSel===name?null:name; renderCards(); } function completeMerge(target){ const a=S.mergeSel; if(!a||a===target){ S.mergeSel=null; renderCards(); return; } - if(!confirm('Merge "'+a+'" and "'+target+'"? Claude will combine them and reopen this view.')) return; - post({type:'merge-request', branch:D.branch||'HEAD', features:[a,target]}, 'Merging — Claude is working…'); + if(!confirm('Merge "'+a+'" and "'+target+'"? The Agent will combine them and reopen this view.')) return; + post({type:'merge-request', branch:D.branch||'HEAD', features:[a,target]}, 'Merging — Agent is working…'); } function collectComments(){ return Object.entries(S.comments).map(([feature,comment])=>({feature,comment})); } function hasChanges(){ return !!(S.moves.length||S.renames.length||S.descUpdates.length||collectComments().length); } function onPrimary(){ if(hasChanges()){ - post({type:'plan-adjustments', branch:D.branch||'HEAD', moves:S.moves, renames:S.renames, descUpdates:S.descUpdates, comments:collectComments()}, 'Sent — Claude is updating the features'); + post({type:'plan-adjustments', branch:D.branch||'HEAD', moves:S.moves, renames:S.renames, descUpdates:S.descUpdates, comments:collectComments()}, 'Sent — Agent is updating the features'); } else { post({type:'plan-approved', branch:D.branch||'HEAD'}, 'Features accepted — run /iterator-implement to build them'); } diff --git a/skills/iterator/lib/views/memory-review.mjs b/skills/iterator/lib/views/memory-review.mjs index 8a1642f..cd6dd9d 100644 --- a/skills/iterator/lib/views/memory-review.mjs +++ b/skills/iterator/lib/views/memory-review.mjs @@ -117,7 +117,7 @@ function cardHtml(m) {
${verdictButtons(m)}
+ placeholder="Comment — sends this memory back to the Agent for revision">
`; } @@ -313,7 +313,7 @@ ${areaSections({ ...data, mode }, memories)} -

Any comment sends the review back to Claude for another round. +

Any comment sends the review back to the Agent for another round. With no comments, the header button accepts the verdicts as chosen above.

`; diff --git a/skills/iterator/lib/views/plan.mjs b/skills/iterator/lib/views/plan.mjs index 470bb12..ce37037 100644 --- a/skills/iterator/lib/views/plan.mjs +++ b/skills/iterator/lib/views/plan.mjs @@ -85,7 +85,7 @@ const PLAN_BODY = `

-

Each section is rendered markdown — click any section to edit (save with blur or ⌘/Ctrl+Enter). Use the 💬 icon to leave a comment on a section, and confirm the external dependencies below — new packages, libraries, or services this plan needs, never tasks or todos. When everything looks right click Accept; if you edit anything or add a comment the button becomes Send review so Claude can revise first.

+

Each section is rendered markdown — click any section to edit (save with blur or ⌘/Ctrl+Enter). Use the 💬 icon to leave a comment on a section, and confirm the external dependencies below — new packages, libraries, or services this plan needs, never tasks or todos. When everything looks right click Accept; if you edit anything or add a comment the button becomes Send review so the Agent can revise first.

External dependencies
@@ -94,7 +94,7 @@ const PLAN_BODY = `
Global comment (optional)
@@ -106,7 +106,7 @@ const PLAN_BODY = `
-
@@ -236,7 +236,7 @@ function onPrimary(){ dependencies: deps, comments: collectComments(), comment: document.getElementById('global-comment').value.trim(), - }, changed ? 'Review sent to Claude' : 'Plan approved'); + }, changed ? 'Review sent to Agent' : 'Plan approved'); } `; diff --git a/skills/iterator/lib/views/question.mjs b/skills/iterator/lib/views/question.mjs index aa727f9..f876c36 100644 --- a/skills/iterator/lib/views/question.mjs +++ b/skills/iterator/lib/views/question.mjs @@ -48,7 +48,7 @@ const BODY = `
Other — type your own answer
-

Answering here returns control to Claude — same as answering in the terminal.

+

Answering here returns control to the Agent — same as answering in the terminal.

`; @@ -88,7 +88,7 @@ function hasChanges(){ return choice != null || Boolean(ft && ft.value.trim()); function onPrimary(){ const text = ft && ft.value.trim() ? ft.value.trim() : null; if(!choice && !text) return; - post({ type:'answer', choice, text }, 'Answer sent to Claude'); + post({ type:'answer', choice, text }, 'Answer sent to Agent'); } `; diff --git a/skills/iterator/lib/views/review.mjs b/skills/iterator/lib/views/review.mjs index 690b59a..cbeca1b 100644 --- a/skills/iterator/lib/views/review.mjs +++ b/skills/iterator/lib/views/review.mjs @@ -517,10 +517,10 @@ function onPrimary(){ const unc = Object.entries(S.unc).map(([path, feature]) => ({ path, feature })); if(unc.length) out.uncategorized = unc; if(MEM.length) out.memory = memDecisions(); - post(out, D.source==='commits' ? 'Accepted — Claude is finalizing' : 'Accepted — Claude is committing'); + post(out, D.source==='commits' ? 'Accepted — Agent is finalizing' : 'Accepted — Agent is committing'); return; } - post(buildFeedbackObj(), 'Review sent to Claude'); + post(buildFeedbackObj(), 'Review sent to Agent'); } // Bootstrap LAST: selectFeature -> renderHunks reads consts declared through diff --git a/skills/iterator/lib/views/widgets.mjs b/skills/iterator/lib/views/widgets.mjs index 18a6280..e9e0c66 100644 --- a/skills/iterator/lib/views/widgets.mjs +++ b/skills/iterator/lib/views/widgets.mjs @@ -59,7 +59,7 @@ button.act.danger-armed{background:var(--bg-red);border-color:var(--del-fg);colo export const WIDGETS_JS = ` // Shared dashboard helpers (lib/views/widgets.mjs). function action(act, feature, msg, prompt){ - return post({ type:'action', action: act, feature: feature || null, prompt: prompt || null }, msg || 'Sent to Claude'); + return post({ type:'action', action: act, feature: feature || null, prompt: prompt || null }, msg || 'Sent to Agent'); } function testBadge(c){ if(!c.testsStatus || c.testsStatus==='none') return ''; diff --git a/test/agent-copy.test.mjs b/test/agent-copy.test.mjs new file mode 100644 index 0000000..c4798c1 --- /dev/null +++ b/test/agent-copy.test.mjs @@ -0,0 +1,29 @@ +import { test } from "node:test"; +import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; + +const ACTOR_SURFACES = [ + "extensions/iterator.js", + "lib/server.mjs", + "lib/ui.mjs", + "lib/session-server.mjs", + "lib/views/plan.mjs", + "lib/views/feature.mjs", + "lib/views/review.mjs", + "lib/views/memory-review.mjs", + "lib/views/question.mjs", + "lib/views/test.mjs", + "lib/views/widgets.mjs", +]; + +test("interactive surfaces use provider-neutral Agent wording", () => { + for (const path of ACTOR_SURFACES) { + const source = readFileSync(new URL(`../${path}`, import.meta.url), "utf8"); + assert.doesNotMatch( + source, + /\bClaude\b(?! Code)/, + `${path} must reserve Claude for the explicit Claude Code product name`, + ); + assert.match(source, /\bAgent\b/, `${path} identifies the active Agent`); + } +}); diff --git a/test/ui.test.mjs b/test/ui.test.mjs index 1c8cb34..582a653 100644 --- a/test/ui.test.mjs +++ b/test/ui.test.mjs @@ -717,7 +717,7 @@ test("review view groups files by Declared/Tests/Incidental with pre-seeded disp // 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'\)/); + assert.match(html, /post\(buildFeedbackObj\(\), 'Review sent to Agent'\)/); }); test("planning hero goal box persists an unsent draft and clears it on plan start", async () => { From 900c99324950f98f869457e0a7dc57aacdca9bf9 Mon Sep 17 00:00:00 2001 From: Christoph Maier Date: Mon, 20 Jul 2026 18:49:54 +0200 Subject: [PATCH 08/20] chore(iterator): record feature commit --- memory/features/agent-neutral-workflow-copy.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/memory/features/agent-neutral-workflow-copy.md b/memory/features/agent-neutral-workflow-copy.md index 9d558e6..a29feb6 100644 --- a/memory/features/agent-neutral-workflow-copy.md +++ b/memory/features/agent-neutral-workflow-copy.md @@ -7,8 +7,12 @@ size: small depends_on: [review-exact-red-test-source, agent-neutral-shell-copy] files: ["lib/views/plan.mjs", "lib/views/feature.mjs", "lib/views/review.mjs", "lib/views/memory-review.mjs", "lib/views/question.mjs", "lib/views/widgets.mjs", "test/agent-copy.test.mjs"] memories: [patterns/agent-reviewed-memory-writes, patterns/safe-browser-rendering, decisions/backlog-planning-and-feature-waves, decisions/iterator-dashboard-feature-workflow, decisions/parallel-feature-waves-and-consolidated-review, decisions/review-navigation-and-work-context, decisions/streamline-backlog-planning-and-knowledge-actions] -timestamp: "2026-07-20T16:49:54.271Z" +timestamp: "2026-07-20T16:49:54.348Z" tags: [] +commits: + - sha: 9544c699b49cb5926e8749b10607b05838e0adc0 + kind: implement + date: 2026-07-20 --- # Implementation notes From 54f09fba46c022a63a56fb6fd8a7e0cf3b0a4cc5 Mon Sep 17 00:00:00 2001 From: Christoph Maier Date: Mon, 20 Jul 2026 18:50:56 +0200 Subject: [PATCH 09/20] chore(iterator): record feature commits and memory updates --- .../features/agent-neutral-workflow-copy.md | 5 +++-- memory/features/index.md | 2 +- memory/log.md | 1 + memory/state.md | 4 ++-- memory/usage.md | 9 ++++---- skills/iterator/lib/views/feature.mjs | 21 ++++++++++++------- skills/iterator/lib/views/question.mjs | 19 +++++++++++------ 7 files changed, 39 insertions(+), 22 deletions(-) diff --git a/memory/features/agent-neutral-workflow-copy.md b/memory/features/agent-neutral-workflow-copy.md index a29feb6..0981626 100644 --- a/memory/features/agent-neutral-workflow-copy.md +++ b/memory/features/agent-neutral-workflow-copy.md @@ -2,17 +2,18 @@ type: Feature title: Use Agent wording in workflow views description: Interactive plan, feature, review, memory, and question views consistently address the provider-neutral Agent. -status: implemented +status: done size: small depends_on: [review-exact-red-test-source, agent-neutral-shell-copy] files: ["lib/views/plan.mjs", "lib/views/feature.mjs", "lib/views/review.mjs", "lib/views/memory-review.mjs", "lib/views/question.mjs", "lib/views/widgets.mjs", "test/agent-copy.test.mjs"] memories: [patterns/agent-reviewed-memory-writes, patterns/safe-browser-rendering, decisions/backlog-planning-and-feature-waves, decisions/iterator-dashboard-feature-workflow, decisions/parallel-feature-waves-and-consolidated-review, decisions/review-navigation-and-work-context, decisions/streamline-backlog-planning-and-knowledge-actions] -timestamp: "2026-07-20T16:49:54.348Z" +timestamp: "2026-07-20T16:50:56.611Z" tags: [] commits: - sha: 9544c699b49cb5926e8749b10607b05838e0adc0 kind: implement date: 2026-07-20 +done: 2026-07-20 --- # Implementation notes diff --git a/memory/features/index.md b/memory/features/index.md index 1912ee3..72cca29 100644 --- a/memory/features/index.md +++ b/memory/features/index.md @@ -2,4 +2,4 @@ * [Use Agent wording in the dashboard shell](agent-neutral-shell-copy.md) - ✅ done · small · Shared dashboard status, submission, cancellation, and working messages refer to the active coding Agent rather than Claude. * [Review exact red test source](review-exact-red-test-source.md) - ✅ done · medium · Users can inspect and approve the complete executable source for every proposed red-mode test before files are written. -* [Use Agent wording in workflow views](agent-neutral-workflow-copy.md) - ⬜ pending · small · depends: review-exact-red-test-source, agent-neutral-shell-copy · Interactive plan, feature, review, memory, and question views consistently address the provider-neutral Agent. +* [Use Agent wording in workflow views](agent-neutral-workflow-copy.md) - ✅ done · small · depends: review-exact-red-test-source, agent-neutral-shell-copy · Interactive plan, feature, review, memory, and question views consistently address the provider-neutral Agent. diff --git a/memory/log.md b/memory/log.md index bb12bc0..4bf40fd 100644 --- a/memory/log.md +++ b/memory/log.md @@ -1,6 +1,7 @@ # iterator update log ## 2026-07-20 +* **Review**: Accepted [Use Agent wording in workflow views](/features/agent-neutral-workflow-copy.md) (committed as feature(agent-neutral-workflow-copy)). * **Implementation**: Committed feature(agent-neutral-workflow-copy) on branch iterator/safe-role-model-handoff; awaiting review. * **Review**: Reviewed [Review exact red test source](/features/review-exact-red-test-source.md); approved (agent). * **Review**: Accepted [Review exact red test source](/features/review-exact-red-test-source.md) (committed as feature(review-exact-red-test-source)). diff --git a/memory/state.md b/memory/state.md index 71cc2fc..df8dcc4 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: agent-neutral-workflow-copy strikes: "{}" escalation: null -timestamp: 2026-07-20T16:46:50.842Z +timestamp: 2026-07-20T16:50:25.826Z --- Runtime flow state; read via gather, written only by the state op. diff --git a/memory/usage.md b/memory/usage.md index d09e14e..137a7a2 100644 --- a/memory/usage.md +++ b/memory/usage.md @@ -2,9 +2,9 @@ type: Usage title: Token usage description: Per-step model/token ledger and optional project-owned pricing for the active plan — written only by the usage op. -totals: "{\"steps\":{\"hub\":{\"openai-codex/gpt-5.6-terra\":{\"input\":38403,\"output\":1157,\"cacheRead\":217088,\"cacheWrite\":0,\"turns\":8}},\"plan\":{\"openai-codex/gpt-5.6-sol\":{\"input\":92807,\"output\":7147,\"cacheRead\":464384,\"cacheWrite\":0,\"turns\":20}},\"implement\":{\"openai-codex/gpt-5.6-terra\":{\"input\":244183,\"output\":4327,\"cacheRead\":2065408,\"cacheWrite\":0,\"turns\":31},\"openai-codex/gpt-5.6-sol\":{\"input\":385552,\"output\":15997,\"cacheRead\":4586496,\"cacheWrite\":0,\"turns\":33}},\"review\":{\"openai-codex/gpt-5.6-sol\":{\"input\":668749,\"output\":2049,\"cacheRead\":2107392,\"cacheWrite\":0,\"turns\":18}}},\"features\":{\"retire-plan\":{\"input\":38403,\"output\":1157,\"cacheRead\":217088,\"cacheWrite\":0,\"turns\":8},\"agent-neutral-shell-copy\":{\"input\":521934,\"output\":5180,\"cacheRead\":2783232,\"cacheWrite\":0,\"turns\":40},\"review-exact-red-test-source\":{\"input\":776550,\"output\":17193,\"cacheRead\":5976064,\"cacheWrite\":0,\"turns\":42}},\"featureModels\":{\"retire-plan\":{\"openai-codex/gpt-5.6-terra\":{\"input\":38403,\"output\":1157,\"cacheRead\":217088,\"cacheWrite\":0,\"turns\":8}},\"agent-neutral-shell-copy\":{\"openai-codex/gpt-5.6-terra\":{\"input\":244183,\"output\":4327,\"cacheRead\":2065408,\"cacheWrite\":0,\"turns\":31},\"openai-codex/gpt-5.6-sol\":{\"input\":277751,\"output\":853,\"cacheRead\":717824,\"cacheWrite\":0,\"turns\":9}},\"review-exact-red-test-source\":{\"openai-codex/gpt-5.6-sol\":{\"input\":776550,\"output\":17193,\"cacheRead\":5976064,\"cacheWrite\":0,\"turns\":42}}}}" +totals: "{\"steps\":{\"hub\":{\"openai-codex/gpt-5.6-terra\":{\"input\":38403,\"output\":1157,\"cacheRead\":217088,\"cacheWrite\":0,\"turns\":8}},\"plan\":{\"openai-codex/gpt-5.6-sol\":{\"input\":92807,\"output\":7147,\"cacheRead\":464384,\"cacheWrite\":0,\"turns\":20}},\"implement\":{\"openai-codex/gpt-5.6-terra\":{\"input\":244183,\"output\":4327,\"cacheRead\":2065408,\"cacheWrite\":0,\"turns\":31},\"openai-codex/gpt-5.6-sol\":{\"input\":676460,\"output\":21230,\"cacheRead\":9467904,\"cacheWrite\":0,\"turns\":55}},\"review\":{\"openai-codex/gpt-5.6-sol\":{\"input\":668749,\"output\":2049,\"cacheRead\":2107392,\"cacheWrite\":0,\"turns\":18}}},\"features\":{\"retire-plan\":{\"input\":38403,\"output\":1157,\"cacheRead\":217088,\"cacheWrite\":0,\"turns\":8},\"agent-neutral-shell-copy\":{\"input\":521934,\"output\":5180,\"cacheRead\":2783232,\"cacheWrite\":0,\"turns\":40},\"review-exact-red-test-source\":{\"input\":776550,\"output\":17193,\"cacheRead\":5976064,\"cacheWrite\":0,\"turns\":42},\"agent-neutral-workflow-copy\":{\"input\":290908,\"output\":5233,\"cacheRead\":4881408,\"cacheWrite\":0,\"turns\":22}},\"featureModels\":{\"retire-plan\":{\"openai-codex/gpt-5.6-terra\":{\"input\":38403,\"output\":1157,\"cacheRead\":217088,\"cacheWrite\":0,\"turns\":8}},\"agent-neutral-shell-copy\":{\"openai-codex/gpt-5.6-terra\":{\"input\":244183,\"output\":4327,\"cacheRead\":2065408,\"cacheWrite\":0,\"turns\":31},\"openai-codex/gpt-5.6-sol\":{\"input\":277751,\"output\":853,\"cacheRead\":717824,\"cacheWrite\":0,\"turns\":9}},\"review-exact-red-test-source\":{\"openai-codex/gpt-5.6-sol\":{\"input\":776550,\"output\":17193,\"cacheRead\":5976064,\"cacheWrite\":0,\"turns\":42}},\"agent-neutral-workflow-copy\":{\"openai-codex/gpt-5.6-sol\":{\"input\":290908,\"output\":5233,\"cacheRead\":4881408,\"cacheWrite\":0,\"turns\":22}}}}" prices: "{}" -timestamp: 2026-07-20T16:46:50.579Z +timestamp: 2026-07-20T16:50:25.562Z --- # Usage @@ -26,7 +26,7 @@ timestamp: 2026-07-20T16:46:50.579Z | model | input | output | cache read | cache write | turns | cost | | --- | ---: | ---: | ---: | ---: | ---: | ---: | | openai-codex/gpt-5.6-terra | 244183 | 4327 | 2065408 | 0 | 31 | — | -| openai-codex/gpt-5.6-sol | 385552 | 15997 | 4586496 | 0 | 33 | — | +| openai-codex/gpt-5.6-sol | 676460 | 21230 | 9467904 | 0 | 55 | — | ## review @@ -41,5 +41,6 @@ timestamp: 2026-07-20T16:46:50.579Z | retire-plan | 38403 | 1157 | 217088 | 0 | 8 | — | | agent-neutral-shell-copy | 521934 | 5180 | 2783232 | 0 | 40 | — | | review-exact-red-test-source | 776550 | 17193 | 5976064 | 0 | 42 | — | +| agent-neutral-workflow-copy | 290908 | 5233 | 4881408 | 0 | 22 | — | -Total: 1429694 in / 30677 out / 9440768 cache-read / 0 cache-write over 110 turns. Cost unavailable: add every used model rate. +Total: 1720602 in / 35910 out / 14322176 cache-read / 0 cache-write over 132 turns. Cost unavailable: add every used model rate. diff --git a/skills/iterator/lib/views/feature.mjs b/skills/iterator/lib/views/feature.mjs index 68680ef..11c19b8 100644 --- a/skills/iterator/lib/views/feature.mjs +++ b/skills/iterator/lib/views/feature.mjs @@ -18,8 +18,8 @@ * { type:"merge-request", features:[a,b] } * plus the shared { type:"cancel" } / { type:"timeout" }. */ -import { renderPage } from '../ui.mjs'; -import { GRAPH_CSS, GRAPH_JS } from './graph.mjs'; +import { renderPage } from "../ui.mjs"; +import { GRAPH_CSS, GRAPH_JS } from "./graph.mjs"; const FEATURE_CSS = ` .sumbar{padding:14px 20px;display:flex;align-items:center;gap:24px;border-bottom:1px solid var(--border); @@ -205,9 +205,16 @@ function onPrimary(){ `; export function render(data) { - return renderPage({ - step: 'feature', subtitle: '/ features', branch: data.branch, title: data.plan, - data, css: FEATURE_CSS + GRAPH_CSS, body: FEATURE_BODY, clientJs: GRAPH_JS + FEATURE_JS, - primaryIdle: 'Accept', primaryChanged: 'Send review', - }); + return renderPage({ + step: "feature", + subtitle: "/ features", + branch: data.branch, + title: data.plan, + data, + css: FEATURE_CSS + GRAPH_CSS, + body: FEATURE_BODY, + clientJs: GRAPH_JS + FEATURE_JS, + primaryIdle: "Accept", + primaryChanged: "Send review", + }); } diff --git a/skills/iterator/lib/views/question.mjs b/skills/iterator/lib/views/question.mjs index f876c36..8e00e01 100644 --- a/skills/iterator/lib/views/question.mjs +++ b/skills/iterator/lib/views/question.mjs @@ -14,7 +14,7 @@ * { type:"answer", choice:"