Skip to content

docs(specs): audit all 24 specs against the code - #476

Open
nedtwigg wants to merge 5 commits into
mainfrom
spec-tighten
Open

docs(specs): audit all 24 specs against the code#476
nedtwigg wants to merge 5 commits into
mainfrom
spec-tighten

Conversation

@nedtwigg

Copy link
Copy Markdown
Member

One agent per spec, fanned out across all 24, verifying every above-the-fold
claim against the implementation. Goals were to shrink the specs, remove
redundant code comments, correct drift, and fix bugs that surfaced along the
way.

Specs net −218 lines (+3,259 / −3,477). That number understates the work:
most agents cut heavy duplication and spent it back on invariants that were
missing entirely. Biggest shrinks were layout.md (−90), mobile-terminal-ui.md
(−61) and terminal-state.md (−38); remote-security-model.md deliberately
grew, being the one place accuracy clearly outweighed brevity.

Recovered lost documentation

Commit afbf9b9b ("keep a minimized browser surface's DOM alive by parking it")
accidentally replaced three whole sections of tiling-engine.md — the wall
store, the LathHost adapter, and the 273-line animator — with the new Parked
leaves section. Five source files and layout.md still pointed at those section
names. Restored, rewritten against current code.

Corrections that would have misled someone acting on them

Spec Was Actually
remote-security-model.md, SECURITY.md Editing the ACL revokes at the client's next authorizeConnection The Host snapshots the store at startup — a restart is required. You'd have believed a device was revoked while it still had access.
vscode.md Deactivate teardown ordering Documented backwards, in the one place ordering bugs silently lose sessions
deploy.md APPLE_SIGNING_IDENTITY, APPLE_TEAM_ID are secrets Neither exists anywhere in the repo; APPLE_ID was mislabeled as one; a required makensis prerequisite was missing
4 specs Scrollback is persisted and replayed on restore Never persisted — normalizeSessionV3 actively scrubs the field
remote-api.md One WebSocket per session bound to a sessionId No sessionId exists; the Host multiplexes every session over one relay socket keyed by clientId
dor-browser.md The iframe proxy strips frame-blocking headers Drops the entire CSP — the injected shim is an inline script — so the upstream loses its own XSS policy inside the frame
glossary.md (I4) Registry: Orphaned is transient A minimized terminal sits Orphaned indefinitely, breaking the stated leak signal
dor-cli.md Never documented ELECTRON_RUN_AS_NODE=1, without which dor under VS Code launches Electron's GUI, ignores the script, and exits 0

Also newly documented because they lived in no spec: MAX_TERMINAL_DIMENSION
(the clamp on peer-supplied terminal sizes), OSC_INCOMPLETE_LIMIT (the OSC
parser's only unbounded-growth guard), and the input-side report filters that two
other specs already pointed at.

Bugs fixed

  • KillConfirm.tsx — the confirm alphabet excluded x so the kill shortcut
    couldn't accept its own prompt, but k is equally a kill shortcut
    (handle-pane-shortcuts.ts:81) and was still in the set. Double-tapping k
    confirmed the kill ~1/25 of the time.
  • lib/.storybook/themes.ts — skipped flattenSelectionAlpha(), so stories
    under Selenized/Solarized rendered translucent header and AppBar fills the app
    never shows. No story pins those themes, so no snapshots change.

Housekeeping

Promoted dormouse.focus above the fold in vscode.md (it was under ## Future
while fully contributed and registered), repointed five stale "Host webview CSP"
references to that section's current name, and removed ~40 redundant or stale
code comments — including a false one claiming PasskeySetupFields is used in
two places when it has exactly one caller.

Left for a decision, not changed here

Both are behavior changes wanting a visual review or a product call, not
documentation fixes.

Verification

pnpm test green: all 4 lints (spec-lint 24 specs, xterm-lint,
loopback-lint, deploy-lint + selftest) and 2,555 tests across 9 packages.

🤖 Generated with Claude Code

https://claude.ai/code/session_01H9HtFUodtF1UsWX6UpRwnn

nedtwigg and others added 3 commits August 28, 2026 16:33
The alphabet already excluded `x` so that double-tapping the kill shortcut
could not accept its own confirmation prompt. But `k` is equally a kill
shortcut — `handle-pane-shortcuts.ts:81` fires on `e.key === 'k' || e.key
=== 'x'` — and it was still in the set, so double-tapping `k` confirmed the
kill roughly 1 time in 25.

Found while auditing docs/specs/shortcuts.md against the code.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H9HtFUodtF1UsWX6UpRwnn
`applyTheme()` runs `flattenSelectionAlpha()` so the list-selection colors,
which Dormouse uses as solid fills, are composited over the sidebar
background. Storybook built its maps with `completeThemeVars()` alone, so
stories under the four bundled themes that carry alpha there (Selenized,
Solarized) rendered translucent header and AppBar fills the app never shows.

No story pins those themes today, so no snapshots change.

Found while auditing docs/specs/theme.md against the code.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H9HtFUodtF1UsWX6UpRwnn
Fanned out one agent per spec to verify every above-the-fold claim against
the implementation, cut duplication, and correct drift. Specs net -218 lines
(+3259 / -3477); most agents cut restatement and spent it back on invariants
that were missing entirely.

Recovered lost documentation: commit afbf9b9 accidentally replaced three
whole sections of tiling-engine.md — the wall store, the LathHost adapter,
and the animator — with the new Parked leaves section. Five source files and
layout.md still referenced them by name. Restored against current code.

Corrections that would have misled someone acting on them:

- remote-security-model.md and SECURITY.md both said revoking a device by
  editing the ACL takes effect at its next authorizeConnection. The Host
  snapshots the store at startup, so a restart is required — you would have
  believed a device was revoked while it still had access.
- vscode.md documented the deactivate teardown ordering backwards, in the
  one place where ordering bugs silently lose sessions.
- deploy.md's secrets table named two secrets that exist nowhere in the repo
  (APPLE_SIGNING_IDENTITY, APPLE_TEAM_ID) and mislabeled APPLE_ID as one. It
  also omitted a makensis prerequisite the signing script hard-requires.
- Four specs claimed scrollback is persisted and replayed. It never is;
  normalizeSessionV3 actively scrubs the field.
- remote-api.md described the wrong protocol topology: a per-session
  sessionId that does not exist. The Host multiplexes every session over one
  relay socket keyed by the relay-assigned clientId.
- dor-browser.md said the iframe proxy strips frame-blocking headers. It
  drops the entire CSP, because the injected shim is an inline script — so
  the upstream loses its own XSS policy inside the frame.
- glossary.md I4 called Registry: Orphaned transient; a minimized terminal
  sits Orphaned indefinitely, which broke the stated leak signal.
- dor-cli.md never documented ELECTRON_RUN_AS_NODE=1, without which `dor`
  under VS Code launches Electron's GUI, ignores the script, and exits 0.

Also promoted dormouse.focus above the fold in vscode.md, repointed five
stale "Host webview CSP" references to the section's current name, and
removed ~40 redundant or stale code comments across the tree.

Two findings left for a decision rather than changed here: #474 (body
font-size falls back to 16px outside VS Code) and #475 (BrowserSidecarAdapter
persists sessions production drops).

pnpm test green: 4 lints + 2555 tests across 9 packages.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H9HtFUodtF1UsWX6UpRwnn
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 28, 2026

Copy link
Copy Markdown

Deploying mouseterm with  Cloudflare Pages  Cloudflare Pages

Latest commit: 2bde794
Status: ✅  Deploy successful!
Preview URL: https://c18aa9d9.mouseterm.pages.dev
Branch Preview URL: https://spec-tighten.mouseterm.pages.dev

View logs

@dormouse-bot dormouse-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I spot-checked the audit's load-bearing claims against the code rather than the prose, and they hold: the ACL snapshot (loadAcl: () => records handed to RemoteHost once in RemoteHostService.#startHost, so an edit really does need a restart), the deactivate() ordering, normalizeSessionV3's scrollback/resumeCommand scrub, clientId-keyed multiplexing with no sessionId anywhere in wire.ts, instrumentHtml dropping the whole in-document CSP, Orphaned outliving a minimized pane, the ELECTRON_RUN_AS_NODE=1 set inside both dor launchers, and the missing APPLE_SIGNING_IDENTITY / APPLE_TEAM_ID (APPLE_ID is a literal in sign-and-deploy.sh). Every backticked constant the specs newly cite resolves, and the numbers match (MAX_TERMINAL_DIMENSION 2000, OSC_INCOMPLETE_LIMIT 16 KiB, PANE_GUTTER_PX 7, LATH_MOTION_MS 440, PAIRING_PRESENCE_WINDOW_MS 30s, WS_CLOSE_HOST_REPLACED 4000, MAX_PARKED_SURFACES 8). Both behavior fixes check out: handleKillConfirm hijacks every key so k/x were the only self-accepting chars, and the four alpha-carrying themes (Selenized/Solarized × light/dark) are pinned by no story, so the Storybook flatten is snapshot-neutral. The poppedOut field removal is safe — nothing writes it into params; this.poppedOut = params.renderMode === 'ab-popout' is the only producer.

Four dangling section pointers, all the same class of staleness the PR set out to fix — renamed or deleted headings whose referrers weren't repointed. None are on a diff line, so no inline suggestions; happy to push a commit with these if you want it.

1. SECURITY.md:11 and docs/specs/server.md:1042 point at a heading this PR deleted. The diff removes ### Server deployment modes from remote-api.md, folding it into one sentence under ## Transport. Both referrers survive:

  • SECURITY.md:11Two deployment modes are defined (`docs/specs/remote-api.md` → "Server deployment modes")
  • docs/specs/server.md:1042model are unchanged from selfhost ([remote-api.md](./remote-api.md), Server deployment modes)

Both files are edited by this PR, so the rename sweep reached them but not these lines. remote-api.md → "Transport" is the surviving home.

2. docs/specs/server.md:1044 cites content this diff deleted. The saas-multitenant scope says the SaaS account model is staged in remote-api.md:

Complementary front-door work is staged elsewhere and this scope does not restate it: the SaaS account model (email + passkey self-serve signup) in remote-api.md

The deleted Server deployment modes bullet was the only place remote-api.md described email + passkey self-serve signup; what's left there is only how accounts come to exist differs, which doesn't say how. Per AGENTS.md's "a scope is defined in exactly one spec; other specs link to it by name and never restate its contents", the scope now links to nothing — either restore the account-model sentence in remote-api.md or move it into this scope.

3. lib/src/index.css still names the pre-restore heading. The .lath-host block comment reads The HTML adapter for Lath (docs/specs/tiling-engine.md → "Adapters"). tiling-engine.md's restored heading is ## The HTML adapter (LathHost) — the same repoint LathHost.tsx got in this diff.

4. lib/src/components/MobileTerminalUi.tsx points at a heading this diff removed. In the blur-retry callback: repeat across rAF and a few staggered ticks. See mobile-terminal-ui.md (Touch interactions). The diff drops ## Touch interactions; that exact rule now lives under ## Keyboard focus invariant ("Since Wall may defer xterm focus to requestAnimationFrame, the wrapper repeats that blur across a rAF and staggered timers"). The file is in the diff, so this one was in reach.

Notes
  • Checked mechanically: extracted every spec.md → "Section", spec.md (Section), and spec.md §Section reference from .ts/.tsx/.css/.mjs/.sh/.rs and from the specs themselves, and resolved each against the actual heading set. Findings 1, 3, 4 came out of that; finding 2 was a follow-on from reading the deleted hunk. All other references resolve, including the five repointed "Host webview CSP" sites and the "Animation contract""Animation" rename.
  • pnpm lint:specs now fails on a pre-install checkout: docs/specs/webgl-text.md:14 backticks canopy/node_modules, which spec-lint.mjs requires on disk. canopy is a workspace package so pnpm install creates it and CI is unaffected — flagging only because lint:specs is documented as a standalone command.
  • docs/specs/server.md also overlaps with #472, which edits it; no conflicting content that I could see, but they'll want ordering.

@dormouse-bot

Copy link
Copy Markdown
Collaborator

Heads-up on a conflict, not a review comment: #478 edits the same docs/specs/theme.md paragraph this PR rewrites, and the two versions differ in what they say rather than only in wording.

#478 fixes a real bug — --mt-font-size was declared at document level while the --vscode-font-size it reads is written to body.style, so body { font-size: var(--mt-font-size) } was invalid-at-computed-value-time and body inherited the UA 16px. Alongside the one-line CSS fix it widens lib/src/lib/themes/consumed-keys.test.ts into a guard that fails when a theme.css token whose value contains any var() chain isn't declared with the same value at both document and body level. The paragraph in #478 states that rule; this PR's condensed version ("the parallel @theme declarations exist so Tailwind can generate utility classes, but the body-level ones are the runtime source of truth") plus the checklist bullet state the intent but not the enforced condition, and don't mention the typography tokens.

So if the conflict gets resolved toward this PR's wording, the spec loses the sentence describing a test that will fail people. Taking #478's paragraph for that one block and this PR's edits everywhere else keeps both. Nothing else collides — this PR's lib/src/theme.css hunks are comment rewrites inside @theme, above the body block #478 touches.

@nedtwigg

Copy link
Copy Markdown
Member Author

@dormouse-bot please merge origin/main and resolve any conflicts appropriately

Also resolve the problems you identified above please.

Four spec conflicts, resolved toward whichever side carries the newer fact:

- theme.md — took main's paragraph from #478. It states the enforced rule
  (a token whose value contains a var() chain must be declared with the same
  value at both document and body level) that consumed-keys.test.ts now
  fails on; this branch's condensed version stated the intent but not the
  condition. This branch's checklist bullets are kept.
- transport.md — took main's paragraph from e7694d0. It corrects what the
  gate costs on reload: Sessions survive but the layout does not, because
  reconnect.ts reads getState() for the resume plan. The PERSIST_SESSION
  flag name and the plumbing note live in standalone.md, so dropping them
  here is the say-it-once rule, not a loss.
- vscode.md — took main's "All under vscode-ext/test/". This branch's "Six
  suites" became wrong when main added webview-boot.smoketest.ts.
- server.md — kept this branch's tightened orphan-reap bullet and its
  unbackticked "Source of truth:", took main's two tailscaled bullets and
  the teardown paragraph, both of which are new material.
Same class of staleness the branch set out to fix — headings this branch
renamed or deleted, whose referrers were missed because none sat on a diff
line.

- SECURITY.md and the saas-multitenant scope in server.md pointed at
  remote-api.md's "Server deployment modes", which this branch folded into
  one sentence under "Transport".
- That scope also said remote-api.md holds the SaaS account model this scope
  "does not restate" — the deleted section was the only place remote-api.md
  described email + passkey self-serve signup, and the scope's own
  **Accounts** bullet already owns it. Point there instead of at a spec that
  no longer says it.
- lib/src/index.css named tiling-engine.md's pre-restore "Adapters"; the
  restored heading is "The HTML adapter (LathHost)", the same repoint
  LathHost.tsx got here.
- MobileTerminalUi.tsx's blur-retry comment named mobile-terminal-ui.md's
  "Touch interactions", removed here; the rule it cites now lives under
  "Keyboard focus invariant".
@dormouse-bot

Copy link
Copy Markdown
Collaborator

Merged origin/main and resolved all four conflicts, then fixed the four dangling pointers from my earlier review. Pushed as 2bde794d; pnpm test is green locally (4 lints + 2,554 tests across 9 packages).

Conflicts

Resolved toward whichever side carries the newer fact, not toward this branch by default.

Spec Resolution
theme.md Took main's paragraph from #478 — exactly the swap I flagged. It states the rule consumed-keys.test.ts now enforces (a token whose value contains a var() chain must be declared with the same value at both document and body level); this branch's condensed version stated the intent but not the condition. This branch's two checklist-bullet enrichments survived un-conflicted and are kept.
transport.md Took main's paragraph from e7694d0a ("say what the gate costs on reload"). It corrects a claim this branch got wrong: Sessions survive a reload but the layout does not, because reconnect.ts reads getState() for the resume plan. The PERSIST_SESSION flag name and the plumbing note that this branch added here already live in standalone.md → "Standalone persists no Session state", so dropping them is say-it-once, not a loss.
vscode.md Took main's "All under vscode-ext/test/". This branch's "Six suites" became wrong the moment main added webview-boot.smoketest.ts.
server.md Split: kept this branch's tightened orphan-reap bullet and its unbackticked Source of truth:, took main's two tailscaled bullets and the whole teardown paragraph — both are material this branch never saw.

The four dangling pointers

All four were the staleness class this branch set out to fix — renamed or deleted headings whose referrers weren't on a diff line.

  1. SECURITY.md and the saas-multitenant scope in server.md → repointed remote-api.md → "Server deployment modes" to "Transport".
  2. That scope also claimed remote-api.md holds the SaaS account model it "does not restate". The deleted section was the only place remote-api.md described email + passkey self-serve signup, and the scope's own Accounts bullet already owns it — so the sentence now points there rather than at a spec that no longer says it. Nothing restored to remote-api.md.
  3. lib/src/index.csstiling-engine.md "Adapters" to "The HTML adapter (LathHost)".
  4. lib/src/components/MobileTerminalUi.tsxmobile-terminal-ui.md "Touch interactions" to "Keyboard focus invariant".
Verification, and one note I left alone

Re-ran the mechanical sweep from the original review against the merged tree — every spec.md → "Section", spec.md (Section) reference in .ts/.tsx/.css/.mjs/.sh/.rs/.ps1 and in the specs themselves, resolved against the actual heading set. Zero unresolved after these four. Two references resolve to bold-lead paragraphs rather than headings (server.md "Pairing approval modal", standalone.md "Standalone persists no Session state") — both name real, findable content, so I left them.

Lints, all green on the merged tree: spec-lint (24 specs, 25 files), xterm-lint (3 workspaces, 4 lockfile entries), loopback-lint (3 listeners, 1 allowlisted), deploy-lint (3 installers, 10 rules, 28 checks) + selftest (32/32 load-bearing).

Left alone: the pnpm lint:specs-on-a-pre-install-checkout note from my review. docs/specs/webgl-text.md backticks canopy/node_modules, which spec-lint.mjs requires on disk, so the lint fails before pnpm install. It's cosmetic — canopy is a workspace package, so any real checkout creates it and CI is unaffected. Say the word if you want the path unbackticked.

注册 for free to join this conversation on GitHub. Already have an account? 登录 to comment

标签

None yet

项目

None yet

Development

Successfully merging this pull request may close these issues.

2 participants