diff --git a/src/story-mode.ts b/src/story-mode.ts index 97374319..bef315b2 100644 --- a/src/story-mode.ts +++ b/src/story-mode.ts @@ -951,6 +951,8 @@ function reduceInput(name: string, input: unknown, cwd?: string): ReducedInput { input.headline = clip(segment, 200); const writes = heredocWrites(full, cwd); if (writes.length) input.writes = writes; + // A script or command that writes project files is an edit, whatever it was run as. + if (writes.length && (intent === 'run' || intent === 'look')) input.intent = 'edit'; // Files it prints, whatever else it does (`cat a.py && pytest`): the drawer offers them. A // relative name resolves against the line's cwd (printedFiles gives none after a `cd`). const reads = printedFiles(full).map((f) => @@ -1522,6 +1524,7 @@ function applyResult(ev: StoryEvent, r: Extract): v if (r.background) { // Started, not done: its task notification says how it ended. ev.running = true; + ev.background = true; ev.ok = undefined; } // Reading a background job's output file that reports tests is a look at test results. @@ -1533,7 +1536,9 @@ function applyResult(ev: StoryEvent, r: Extract): v ) ev.intent = 'test'; if (r.modified?.length) { - // Changed, but no diff to show: the file is listed, its lines unknown. + // Changed, but no diff to show: the file is listed, its lines unknown. A plain command that did + // it (a script, a formatter) edited files. + if (ev.intent === 'run' || ev.intent === 'look') ev.intent = 'edit'; const known = new Set((ev.edits ?? []).map((d) => d.file)); const more = r.modified .filter((f) => !known.has(f)) @@ -2271,6 +2276,9 @@ function decorate(draft: DraftPanel, events: StoryEvent[], turn: StoryTurn, inde const failedFirst = checks.slice(0, -1).some((e) => passed(e) === false); if (lastCheck?.running) story = isTest ? 'Claude starts the tests in the background.' : 'Claude starts the checks in the background.'; + // Finished later, in the background: not tests that ran during this turn. + else if (lastCheck?.background && ok !== undefined) + story = `The ${isTest ? 'tests' : 'checks'} run in the background and ${ok ? 'pass' : 'fail'}.`; else if (ok === undefined) story = isTest ? 'Claude runs the tests.' : 'Claude runs the checks.'; else if (!ok && tests && !tests.passed && !tests.failed && tests.errors) story = "The tests don't even load."; else if (!ok && tests && tests.passed && !tests.failed && !tests.errors) diff --git a/src/types/story.ts b/src/types/story.ts index 8f51866c..de980a61 100644 --- a/src/types/story.ts +++ b/src/types/story.ts @@ -128,6 +128,8 @@ export interface StoryEvent { description?: string; /** A command sent to the background ("Command running in background") that has not reported back. */ running?: boolean; + /** A command that ran in the background (its result came back later, as a report of its own). */ + background?: boolean; /** A save that ships the work: `gh pr create` (`pr`) or `git push` (`push`). */ ships?: 'pr' | 'push'; /** How many commits a Bash call makes (`git commit` segments). */ diff --git a/src/web/CLAUDE.md b/src/web/CLAUDE.md index 2313fbd3..6d5e00c1 100644 --- a/src/web/CLAUDE.md +++ b/src/web/CLAUDE.md @@ -54,7 +54,7 @@ Frontend JS modules have `@fileoverview` with `@dependency`/`@loadorder` tags. L **Settings surface** (`#appSettingsModal` + `#sessionOptionsModal` + `#createCaseModal`): one `set-*` language shared through a single `:is(...)` id scope in styles.css. App Settings' rail is a table of contents over ONE scrolling document (`switchSettingsTab` scrolls); Session Options and Add Case really switch (`switchOptionsTab` / `switchCaseModalTab`), and their larger per-modal size blocks are the design, not drift. ⚠️ **The load/save contract is `getElementById` by id**: renaming or dropping a control id silently stops it loading or saving. ⚠️ The Session Options "Session" entry still keys off `context` (label-only rename). ⚠️ Add Case keeps its legacy `.form-row` markup via an adapter; every `
` there needs `.set-adv-chev` plus both marker suppressions. ⚠️ Model cards and the effort segment are views over hidden `