From 185a9cebf81c600ffa565f1372db3497c4cd8081 Mon Sep 17 00:00:00 2001 From: Destin Date: Sun, 27 Sep 2026 20:42:26 -0700 Subject: [PATCH] chore(workspace): what the blank-chat session taught this workspace MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - verify.sh checks line budgets FIRST (~2 s) and prints overruns before the parallel checks start — the most repeated verify failure in wrap-ups.md (4th session; six overruns here), previously only reported at the end. Closes papercut (3) of the 2026-09-18 dev-workspace item. - glob-guard.test.mjs: non-live-app cases run against an empty fake /proc. `kill -9 4321 5678` failed on master the day pid 4321 was Destin's live app (the live-app guard blocking it correctly). Shown red on real /proc. - Roadmap: "Chat panel vanished" updated with youcoded#582 and the unknown trigger; phone empty-copy wipe (remote-access); split reply bubble (chat-data); Android swipe-away blank chat in the parity audit appendix. - wrap-ups.md entry. Co-Authored-By: Claude Opus 5.5 (1M context) --- .claude/hooks/glob-guard.test.mjs | 9 ++++++++- ROADMAP.md | 4 ++-- .../2026-09-10-android-parity-audit.md | 11 +++++++++++ docs/roadmap/chat-data.md | 5 +++++ docs/roadmap/dev-workspace.md | 11 +++++------ docs/roadmap/remote-access.md | 7 +++++++ docs/roadmap/user-interface.md | 8 ++++++-- docs/wrap-ups.md | 11 +++++++++++ scripts/verify.sh | 17 +++++++++++++++++ 9 files changed, 72 insertions(+), 11 deletions(-) diff --git a/.claude/hooks/glob-guard.test.mjs b/.claude/hooks/glob-guard.test.mjs index 3157c0ad7..94650e1da 100644 --- a/.claude/hooks/glob-guard.test.mjs +++ b/.claude/hooks/glob-guard.test.mjs @@ -26,10 +26,17 @@ writeFileSync(path.join(CWD, 'notes.md'), ''); process.on('exit', () => rmSync(CWD, { recursive: true, force: true })); /** @returns {{blocked: boolean, message: string}} */ +// WHY an empty fake /proc: the hook refuses to signal Destin's LIVE app, looked up +// by pid in /proc. Against the real one, a literal like `kill -9 4321 5678` failed +// the day pid 4321 was the live app's zygote (2026-09-27). The live-app cases below +// build their own fake /proc (runProc); every other case sees no processes at all. +const NO_PROC = mkdtempSync(path.join(tmpdir(), 'glob-guard-noproc-')); +process.on('exit', () => rmSync(NO_PROC, { recursive: true, force: true })); + function run(command, tool_name = 'Bash') { const r = spawnSync('python3', [HOOK], { input: JSON.stringify({ tool_name, tool_input: { command }, cwd: CWD }), - encoding: 'utf8', + encoding: 'utf8', env: { ...process.env, GLOB_GUARD_PROC: NO_PROC }, }); return { blocked: r.status === 2, message: (r.stderr || '').trim() }; } diff --git a/ROADMAP.md b/ROADMAP.md index cbfa93096..66e80e957 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -74,9 +74,9 @@ Target: `v1.3.1` | [dev-workspace](docs/roadmap/dev-workspace.md) — building the app, not the app | 101 | 32 | 4 | 9 | | [native-harness](docs/roadmap/native-harness.md) — the app's own agent doing work | 55 | 10 | 5 | 23 | | [user-interface](docs/roadmap/user-interface.md) — shared primitives, chrome, layout, copy | 39 | 16 | 2 | 6 | -| [remote-access](docs/roadmap/remote-access.md) — reaching the app from another device | 29 | 6 | 1 | 4 | +| [remote-access](docs/roadmap/remote-access.md) — reaching the app from another device | 30 | 7 | 1 | 4 | | [sync](docs/roadmap/sync.md) — moving your stuff between devices | 26 | 10 | 8 | 5 | -| [chat-data](docs/roadmap/chat-data.md) — everything kept about a chat | 22 | 7 | 2 | 2 | +| [chat-data](docs/roadmap/chat-data.md) — everything kept about a chat | 23 | 8 | 2 | 2 | | [other-features](docs/roadmap/other-features.md) — real features too small for their own area | 22 | 5 | 6 | 5 | | [marketplace](docs/roadmap/marketplace.md) — finding, installing and rating plugins and themes | 20 | 7 | 0 | 5 | | [files](docs/roadmap/files.md) — documents the user opens, edits or organises | 17 | 4 | 0 | 9 | diff --git a/docs/active/investigations/2026-09-10-android-parity-audit.md b/docs/active/investigations/2026-09-10-android-parity-audit.md index 02ca90759..a4d88ecf7 100644 --- a/docs/active/investigations/2026-09-10-android-parity-audit.md +++ b/docs/active/investigations/2026-09-10-android-parity-audit.md @@ -403,6 +403,17 @@ dev-workspace). ### Found after the consolidation +- **Swiping the app away and reopening it leaves every running conversation's chat blank** + (2026-09-27). Swiping from recents closes the screen but the background service keeps the + sessions running (`SessionService.onTaskRemoved` does nothing, on purpose). The reopened + screen asks each session for its first history page, and Android has no `transcript:page` + handler (the shim refuses it quietly) while its transcript watcher only pushes new lines — so + the chat shows "Start a conversation" while the terminal and Claude still have everything, + until close + resume. Read from the code during the desktop blank-chat fix (youcoded#582), + not reproduced on a handset. The desktop cure (read a rebuilt screen's first page to the end) + needs the Kotlin tail reader the paging item already asks for. + `android` `needs-verify` `checked 2026-09-27` + - **Dogfood APKs count as a separate person in our own numbers** (2026-09-13). Android's device id is scoped to the app-signing key, and `android-test-build.yml` signs with a throwaway debug key — so a dogfood build installed beside the real app on one phone reports a DIFFERENT device diff --git a/docs/roadmap/chat-data.md b/docs/roadmap/chat-data.md index 02a559af6..5d37d80e6 100644 --- a/docs/roadmap/chat-data.md +++ b/docs/roadmap/chat-data.md @@ -3,6 +3,11 @@ Filing test: everything kept about a chat — transcript, title, tags, notes, se resume state. Not here: the model is running right now (native-harness); the files a chat produced and the panel that shows them (files). +- [ ] A streamed reply was split mid-sentence across two chat bubbles ("…for your review. Now" | + "the invites.") in one practice-app run while the computer was busy; seen once in ~30 + loaded runs (2026-09-27), not yet seen in the real app + `chat` `desktop` `needs-verify` `checked 2026-09-27` `needs-repro` + - [ ] The Projects page's conversation list shows only Claude Code conversations and carries no tags, note or last-used model, so its cards look sparser than the same conversations in the Resume browser, and assistant conversations in that folder never appear there (seen while diff --git a/docs/roadmap/dev-workspace.md b/docs/roadmap/dev-workspace.md index 4271aba9a..2b8b9b9d4 100644 --- a/docs/roadmap/dev-workspace.md +++ b/docs/roadmap/dev-workspace.md @@ -60,16 +60,15 @@ seen-on is always n/a here. bigger budget `n/a` `confirmed` `checked 2026-09-16` -- [ ] Three small tooling papercuts from the 2026-09-18 premium-motion session, each costing a - whole cycle: (1) `run-dev.sh --stop` lists the dev window's own `claude` children with no +- [ ] Two small tooling papercuts from the 2026-09-18 premium-motion session, each costing a + whole cycle (a third, line budgets only reported at the end of verify, was fixed + 2026-09-27: verify checks them first): (1) `run-dev.sh --stop` lists the dev window's own `claude` children with no sign they are its children — have it say so from the process's ancestry, instead of a doc asking sessions to remember; (2) a fresh session worktree has no `scratch/perf-lab/assets`, so `fixture.mjs --ensure-assets` re-downloads ~490 MB and a slow link times the whole run out after the 3-minute build and the quiet-machine wait — copy or hardlink from the shared - checkout's `scratch/perf-lab/assets` when it is there; (3) `verify.sh` found six separate - line-budget overruns of one to thirteen lines, each only at the END of a full run — run - `tests/line-budgets.test.ts` first, it takes under a second - `n/a` `confirmed` `checked 2026-09-18` + checkout's `scratch/perf-lab/assets` when it is there + `n/a` `confirmed` `checked 2026-09-27` - [ ] The perf lab's new "blank on arrival" count has never been shown to FAIL. It was added 2026-09-18 (`scenario-workload.mjs` → `switchBlank`) because the painted clock counts entry diff --git a/docs/roadmap/remote-access.md b/docs/roadmap/remote-access.md index 68158ad86..6d2b23b80 100644 --- a/docs/roadmap/remote-access.md +++ b/docs/roadmap/remote-access.md @@ -1,6 +1,13 @@ # remote-access — reaching the app from another device Filing test: reaching the app from another device — the protocol, the browser client. +- [ ] A phone connected to the computer can lose the messages it is showing: when the computer + sends its copy of the conversations and its copy of one is empty (it had not loaded that + conversation yet, or its own chat went blank), that empty copy replaces the phone's. Read + from the code during the 2026-09-27 blank-chat fix; left out when Destin scoped that work + to desktop + `desktop` `needs-verify` `checked 2026-09-27` + - [ ] With a phone connected, the computer sends it the terminal output and chat events of every open session, not only the one the phone is showing — more sessions running means a busier computer and phone. Found by the 2026-09-23 performance review; not built because remote diff --git a/docs/roadmap/user-interface.md b/docs/roadmap/user-interface.md index bd339654d..038e27e9b 100644 --- a/docs/roadmap/user-interface.md +++ b/docs/roadmap/user-interface.md @@ -114,8 +114,12 @@ layout, copy. Not here: one screen only — that screen's area, with the surface `all` `confirmed` `checked 2026-09-11` `v1.3.1` → docs/active/investigations/2026-09-10-error-inventory/README.md - [ ] Chat panel vanished from a live session (beta.16, 2026-08-27) — no messages, and new - sessions showed no "Start a conversation" text; Destin said ignore for now - `chat` `desktop` `needs-verify` `checked 2026-08-27` `needs-repro` → docs/archive/investigations/2026-08-27-terminal-black-glyphs-mipmap-driver.md + sessions showed no "Start a conversation" text; Destin said ignore for now. 2026-09-27: a + friend on Windows and Destin on Linux saw messages disappear MID-conversation behind + "Start a conversation" while Claude and the terminal still had them. Four causes fixed + (youcoded#582); what rebuilds the chat mid-conversation in the built app is still unknown — + next step is a tripwire that logs what emptied an open chat, or the friend's desktop.log + `chat` `desktop` `needs-verify` `checked 2026-09-27` `needs-repro` → docs/archive/investigations/2026-08-27-terminal-black-glyphs-mipmap-driver.md - [ ] Switching sessions in terminal view redraws the letters of EVERY open terminal each time (the black-glyph safety net in `TerminalView.tsx`). Measured 2026-09-10 with the new perf-lab diff --git a/docs/wrap-ups.md b/docs/wrap-ups.md index e1b72500c..6c178910b 100644 --- a/docs/wrap-ups.md +++ b/docs/wrap-ups.md @@ -782,3 +782,14 @@ recurred — the repetition is the data. - Line budgets raised on App.tsx, SettingsPanel, SyncPanel, StatusBar, SessionStrip. RECURRENCE of the budget friction already in `dev-workspace.md` → no new entry - Destin: "the simplest possible version … fewest error cases … least thinking from the model", and journeys run automatically because "a check nobody runs goes stale" (his call when asked) → applied: spec decisions + the verify.sh comment - deleted/merged: run-review.sh, cdp-ports.sh, probe-ports.sh, montage.sh, make-gallery.py and their 3 tests + CI step deleted; 82 click plans archived (site-gallery kept); 16 per-shot port/offset mechanisms replaced by one engine; ui-probe and the two drag probes deliberately kept (reasons in the spec) + +## 2026-09-27 — chat goes blank over a live conversation; native "unreadable" read as "empty" (youcoded#582, session/chat-vanish) +- Line budgets tripped six times (App.tsx, types, ipc-handlers, native-session-host, remote-server, chat-reducer), each only at the END of verify, plus two line-budgets.json merge conflicts. RECURRENCE (4th session) of the 2026-09-18 papercut → applied: `scripts/verify.sh` runs the budget test first (~2 s) and prints the overruns before the parallel checks start; shown red with a one-line probe; roadmap papercut (3) closed in `dev-workspace.md` [~3 extra verify runs] +- Static reading could not find what empties a live chat in the built app; the renderer has no way to write to desktop.log, so the trigger stays unknown → roadmap: the existing `user-interface.md` "Chat panel vanished" item updated with the four fixed causes and the tripwire as next step +- Two independent reviews of my own work each found a real gap (duplicate tool card from an overlapping page; resume's sync header read made the new retry inert) → applied in #582; no new rule: the global rules already ask for unintended-consequence review, and it worked +- A pre-existing journey flake (resume-conversation, ~1 in 6 under load, on master too): step 2's "Resume" matched a chatsearch card's own Resume button when the menu opened slowly → applied: the journey waits for `chat/sessions` (8/8 under 32-core load) +- I read a STALE verify log (verify keeps logs only for failed runs) and briefly believed a green --full run had failed → dropped: verify prints its own log path on failure; my mistake, caught by re-running the suite +- The glob-guard hook blocked a cp-backup/restore toggle for a red-check; a reversible sed toggle worked → no change: the hook did its job +- Destin: "ignore android" (scope), "investigate more thoroughly and fix… check our own work" → no new rule (global.md covers both); Android and phone findings filed (android audit appendix, `remote-access.md`), a split reply bubble filed in `chat-data.md` +- The workspace hook suite failed 1/430 at close-out, on master too: `glob-guard.test.mjs` asserted `kill -9 4321 5678` is allowed, and pid 4321 was Destin's live app that day, so the live-app guard (correctly) blocked it → applied: every non-live-app case runs against an empty fake /proc (`GLOB_GUARD_PROC`); shown red against the real /proc +- deleted/merged: App.tsx's first-page closure moved to `state/first-page-loader.ts` (App.tsx −27 lines, budget lowered to match) diff --git a/scripts/verify.sh b/scripts/verify.sh index df5de8b34..778b9068a 100755 --- a/scripts/verify.sh +++ b/scripts/verify.sh @@ -267,6 +267,22 @@ fi # caught before release. Checkouts that predate tsgo fall back to tsc. TSC=tsc [[ -x "$DESKTOP/node_modules/.bin/tsgo" ]] && TSC=tsgo + +# Line budgets FIRST, synchronously (~2 s), printed before the parallel checks start. +# WHY: an over-budget file was the most repeated verify failure in docs/wrap-ups.md +# (four sessions; six overruns in one, 2026-09-27), and it only surfaced when every +# check had finished, minutes later. Reported now, it is fixed while the rest run. +# Still counted as a failure at the end; the related-tests run repeats it harmlessly. +BUDGETS_FAILED=0 +if [[ -f "$DESKTOP/tests/line-budgets.test.ts" ]]; then + if ! ( cd "$DESKTOP" && npx vitest run tests/line-budgets.test.ts ) >"$LOGDIR/budgets.log" 2>&1; then + BUDGETS_FAILED=1 + echo "FAIL line budgets (checked first — the other checks still run)" + grep -E 'lines, budget' "$LOGDIR/budgets.log" | grep -vE '^\s*[-+]' | sed 's/^ */ /' | sort -u + echo "" + fi +fi + start types "types ($TSC --noEmit)" npx "$TSC" --noEmit -p tsconfig.json # The test tree is its own TS project (different module resolution, allowJs for # the .mjs orchestrator). Separate check so a failure names which tree broke. @@ -326,6 +342,7 @@ fi FAILED=0 FAILED_KEYS=() +if [[ $BUDGETS_FAILED -eq 1 ]]; then FAILED=1; FAILED_KEYS+=("budgets"); fi for key in types testtypes tests knip lint design invariants screens journeys; do [[ -n "${PID[$key]:-}" ]] || continue wait "${PID[$key]}"; rc=$?