Fix dossier-runtime `site` subcommand dispatch (isSub omits 'site')

task-runtime-site-dispatch-bug

task confidence verified status done 2026-06-17 owner platform-engineer
source log-auditor — surfaced while recording DEC-0039 (/client-new); the command file already documents the `site` caveat. FDE confirmed the bug in both src and built dist. CLOSED 2026-06-17 (FDE) — fixed at the ROOT: extracted SUBCOMMANDS into packages/runtime/src/subcommands.ts so the `Sub` type AND `isSub` derive from one array (the guard can never omit a name again); rebuilt dist; the built bin now routes `site` to cmdSite. okf/runtime suites green (64 incl. 4 new).

Fix dossier-runtime site subcommand dispatch

A documented subcommand is dead. In packages/runtime/src/cli.ts, isSub() (the command-name type guard, ~lines 352–354) returns true only for provision | list | run | deprovision — it omits 'site', even though the Sub type (line 77), the switch (lines ~96–107), and the HELP text (line 37) all include site. Net effect: dossier-runtime site ... is rejected at line 86–89 with unknown command "site" (exit 2) before it can reach cmdSite. The same omission is present in the built packages/runtime/dist/cli.js (the minified isSub, function E, lists only the four names).

The fix

One line — add s === 'site' to isSub:

function isSub(s: string | undefined): s is Sub {
  return s === 'provision' || s === 'list' || s === 'run' || s === 'site' || s === 'deprovision';
}

…then rebuild so dist/cli.js carries it. This is the runtime/platform layer owner's fix, not in /client-new's path: /client-new — a thin operator slash command over the control-plane CLI drives only provision/run, and the command file already notes the site caveat.

Severity

Low-but-real (p2). The feature is reachable another way today: dossier-runtime run --site builds the tenant site as part of the loop (cmdRun, the --site branch) and works correctly — so this is a dead standalone entry point, not a missing capability. Worth fixing because a documented, type-declared, HELP-advertised subcommand silently failing is a correctness/trust gap, and the guard should never have been able to drift from the Sub type it claims to narrow.

Provenance

Surfaced by the log-auditor while recording /client-new — a thin operator slash command over the control-plane CLI; the FDE confirmed the bug in both source and built dist. confidence: inferred (agent-surfaced). Board globbed before filing — no open task covered the site dispatch.

Closed 2026-06-17 — fixed at the root, not patched

Instead of the literal one-line s === 'site' (which could silently drift from the Sub union again — the very failure this records), the subcommand list was extracted into packages/runtime/src/subcommands.ts as a single SUBCOMMANDS array; both the Sub type and the isSub guard now derive from it. Adding a subcommand there forces the main() switch to handle it (TS exhaustiveness) and isSub accepts it automatically — the guard cannot omit a declared subcommand. cli.ts imports the shared guard (its local copy deleted). A 4-case regression test (test/subcommands.test.ts) pins the contract. Verified end-to-end through the built dist/cli.js: dossier-runtime site … now reaches cmdSite ("no provisioned tenant"), not the dispatcher's "unknown command". Typecheck clean; full runtime suite 64/64 green. Closed by the Principal Forward Deployed Engineer.