fix(cli-registry): address PR B2 review — fix two test guards, drop unused catalogue

Two required fixes from Ark0N's review of #458:

1. test/frontend-cli-no-id-branching.test.ts's ALLOWED_BRANCHES keyed on
   <file>::<line>::<expression>. A single inserted line anywhere above an
   entry shifted every subsequent line number, so all 21 entries went stale
   simultaneously and the same 21 branches were reported as "new" — on a
   file six other open PRs also touch. Dropped the line number from the key
   (<file>::<expression>, matching the backend guard's own design), which
   collapses 21 line-keyed entries to 11 or-collapse where the same
   expression recurs at multiple call sites in the same file.

2. test/run-mode-ui.test.ts's terminal-ownership guard scanned method
   bodies via `^ {2}async (run[A-Za-z]*)\(\) \{$`, which matched the 8
   one-line run<Mode>() wrappers PR B2 introduced but not _runCliMode(mode),
   where the real logic (and the actual risk the guard exists to catch) now
   lives. Fixed the regex to `^ {2}async (_?run[A-Za-z]*)\(\w*\) \{$` and
   added _runCliMode to the sanity list. Same-class fix in
   test/opencode-resize.test.ts, which had the identical blind spot via
   runOpenCode.toString().

Both reproduced live before fixing (inserted the same comment line; added
this.terminal.clear() to _runCliMode) to confirm the bug, then confirmed
the fix catches it and the suite stays green otherwise.

Also resolves Open Question 2 by dropping window.__codemanCliCatalog
entirely: nothing consumed it, and a registry DECLARED_FOR_LATER field
costs nothing until read while an unconsumed script tag on every page
render is a different trade. Reverts Phase 1 cleanly — server.ts's
injection, shortBadge back in types.ts's DECLARED_FOR_LATER list and the
pinned guard test, and the three associated render-index-html.test.ts /
server-index-title.test.ts assertions.

Full gate: 405 files / 7717 tests / 0 failures (net unchanged), typecheck/
lint/format clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GuHtuPiHXdykq9T6rKQJ9n
This commit is contained in:
Devvyn
2026-09-20 02:04:37 +08:00
co-authored by Claude Sonnet 5
parent cd64b0a3f7
commit 2df9355367
9 changed files with 91 additions and 140 deletions
+4 -7
View File
@@ -629,15 +629,12 @@ export interface CliOverlays {
/**
* ⚠️ DECLARED-FOR-LATER: fields no code reads yet.
*
* `accent`, `overlays.credStore`, `capabilities.echo`, `capabilities.wheelForward`,
* `shortBadge`, `accent`, `overlays.credStore`, `capabilities.echo`, `capabilities.wheelForward`,
* `capabilities.keyboardAccessory` and `capabilities.maxFrameBytes` all describe FRONTEND
* behaviour, and most of the frontend is deliberately untouched by the change that introduced
* this registry — `app.js`, `terminal-ui.js`, `styles.css` and friends keep their own
* behaviour, and the frontend is deliberately untouched by the change that introduced this
* registry — `app.js`, `terminal-ui.js`, `styles.css` and friends keep their own
* hand-authored per-CLI rules, and moving them is its own piece of work with its own way of
* being verified (a mobile/browser suite the CI gate cannot see). `shortBadge` graduated out of
* this list (PR B2): it is read server-side into `window.__codemanCliCatalog`
* (`src/web/server.ts`), the general run-menu catalogue injected for the frontend to consume —
* see `docs/cli-registry.md`.
* being verified (a mobile/browser suite the CI gate cannot see).
*
* They are declared now because each entry should describe its CLI completely, and because
* transcribing them while the hand-written source is still on screen is when the values are
+11 -12
View File
@@ -20,15 +20,14 @@
* near-identical bodies guaranteed to drift, exactly what the CLI registry's
* own no-id-branching rule exists to prevent server-side.
*
* Deliberately a LOCAL table rather than reading `window.__codemanCliCatalog`
* (server.ts, PR B2): that catalogue carries only menu-facing metadata
* (id/label/shortBadge/order/kind), and several unit tests exercise these
* run*() methods inside a bare `vm.createContext()` sandbox with no `window`
* global at all (see test/run-mode-ui.test.ts) — referencing `window` there
* unguarded would throw, not degrade. `buildConfig` returns the CLI's
* top-level legacy config field for a LOCAL launch, or `null` for a CLI that
* sends none (pi: no bypass flag exists, so there is nothing to send — see
* runPi's own history below for why that must stay true).
* Deliberately a LOCAL table rather than a server-injected catalogue: several
* unit tests exercise these run*() methods inside a bare `vm.createContext()`
* sandbox with no `window` global at all (see test/run-mode-ui.test.ts) —
* referencing `window` there unguarded would throw, not degrade. `buildConfig`
* returns the CLI's top-level legacy config field for a LOCAL launch, or
* `null` for a CLI that sends none (pi: no bypass flag exists, so there is
* nothing to send — see runPi's own history below for why that must stay
* true).
*/
const RUN_MODE_LAUNCH = {
opencode: {
@@ -112,9 +111,9 @@ const RUN_MODE_LAUNCH = {
/**
* External (non-Claude, non-Shell) CLI run modes — the keys of RUN_MODE_LAUNCH
* above, kept as its own Set so `_isAltCliMode()` doesn't recompute an array
* every call. Single source for what used to be two hand-copied 8-way
* `session.mode === '<id>' || ...` chains inside one function
* above, kept as its own Set (`EXTERNAL_CLI_MODES.has(mode)`) rather than an
* array recomputed per call. Single source for what used to be two hand-copied
* 8-way `session.mode === '<id>' || ...` chains inside one function
* (`openSessionOptions`), guaranteed to drift from each other the moment a
* ninth CLI landed in one and not the other.
*/
-18
View File
@@ -1674,24 +1674,6 @@ export class WebServer extends EventEmitter {
'</head>',
`<script>window.__codemanCustomModelClis=${customModelClisJson};</script>\n</head>`
);
// The general-purpose run-menu catalogue (PR B2, docs/cli-registry.md): every
// ENABLED CliEntry's menu-facing fields, unfiltered by capability — unlike
// __codemanCustomModelClis above, which is narrowed to one picker's needs.
// Field list mirrors what scripts/generate-cli-catalog.mts exports to
// config/clis.stock.json (id/label/shortBadge/order/kind); launch/env/
// capabilities/overlays are spawn-time concerns the server alone interprets
// and must never leak here, same rule as that generated artifact.
const cliCatalog = enabledClis().map((entry) => ({
id: entry.id,
label: entry.label,
shortBadge: entry.shortBadge,
order: entry.order,
kind: entry.kind,
}));
// `label`/`shortBadge` are user-clis.json-settable strings, so this needs the
// same </script>-breakout guard as __codemanCustomModelClis above.
const cliCatalogJson = escapeScriptJson(JSON.stringify(cliCatalog));
html = html.replace('</head>', `<script>window.__codemanCliCatalog=${cliCatalogJson};</script>\n</head>`);
}
if (!soloSessionId && process.env.CODEMAN_GESTURE === '1') {
html = html.replace('</head>', `<script>window.__codemanGestureAvailable=true;</script>\n</head>`);