Skip to content

T-TOFK-command-seam-streaming-doors

State: closed/done · Impact: medium · Complexity: medium

The command seam is capture-only, so every site that streams live output (stdio: "inherit") or manages a long-running process spawns on its own under an @sdlc:ignore-spawn exemption. Those sites can’t take a fake runner in tests, and they each hand-roll timeouts, cwd, env and failure handling. Giving the seam streaming and managed-process doors makes the exemption rare and meaningful again.

solutions/ontological/lib/util/command.ts re-exports @sksizer/command-seam’s capture doors (spawnRunner/runChecked and their async twins), and its header says streaming and long-lived sites “stay on their own spawns”. On main, 8 files carry 9 @sdlc:ignore-spawn markers. Paths are under solutions/ontological/ unless they start at the repo root: lib/services/dashboard/server.ts (two), lib/services/docs/site/build.ts, lib/services/docs/site/subsites.ts, lib/services/host/hosts/terminal.ts, lib/services/quality/ops/run.ts, scripts/release-lib.ts, scripts/supervise.ts, and the repo-root scripts/install-desktop-apps.ts. The unmerged stack adds more once it lands, among them workflow/ops/run.ts, session/ops/attach.ts and host/hosts/codex.ts; re-take the inventory when starting. solutions/ontological/scripts/check_command_seam.sh matches raw spawn names textually. It already ignores member calls (deps.spawnSync(...)), but it can’t tell a bare call of a locally named function (spawnSync('orca'), where spawnSync is an injected parameter) from a real child_process call. That bare-call false positive is what the Router hit (commit c9d173300).

@sksizer/command-seam exports a streaming door, whose run inherits stdio and resolves to { exitCode }, and a managed-process door, which returns a handle with pid, kill() and an exited promise. Each has the same injectable factory shape as spawnRunner, and async variants exist where callers need them. The streaming and long-running sites use these doors, and only genuinely unrepresentable sites keep @sdlc:ignore-spawn.

  1. Add the two doors, plus fakes for tests, to packages/ts/command-seam/. solutions/ontological depends on it via workspace:*, so this lands in the same branch with no publish step. Publishing a new version for the external consumers that pin ^0.1.0 is a separate follow-up.
  2. Re-export them from lib/util/command.ts and update its header comment.
  3. Move each exempted site onto the matching door, with a unit test per site using the fake.
  4. Make check_command_seam.sh import-aware: flag a bare spawnSync(/spawn(/ exec*(/fork( only in a file that pulls that name from child_process or node:child_process, whether by ES import or by a CommonJS require(...) destructure (solutions/ontological/scripts/build-npm-wrapper.ts uses the latter), and always flag Bun.spawn*. Missing the require form would be a false negative, which is worse than the false positive being fixed. A locally named function or parameter that happens to share the name then passes.
AreaNote
packages/ts/command-seam/the new doors and their fakes (workspace package)
solutions/ontological/lib/util/command.tsre-exports and header
solutions/ontological/lib/services/the exempted streaming and long-running sites
solutions/ontological/scripts/check_command_seam.sh’s bare-call false positive; release-lib.ts and supervise.ts sites
scripts/install-desktop-apps.tsrepo-level streaming site
  • AC-1: @sksizer/command-seam exports a streaming door and a managed-process door, each with a test fake, re-exported from lib/util/command.ts.
  • AC-2: grep -rln '@sdlc:ignore-spawn' over solutions/ontological/lib/, solutions/ontological/scripts/ (excluding check_command_seam.sh itself) and the repo-root scripts/ lists at most 3 files, each with a comment saying why the doors don’t fit.
  • AC-3: every migrated site has a unit test that injects the fake and runs no real process.
  • AC-4: check_command_seam.sh passes on a file that calls a parameter named spawnSync bare with no child_process import, and still fails on a file that imports spawnSync from node:child_process and calls it, and on one that destructures it from require('node:child_process').
  • Embedding PTYs or terminal emulation (hosts link out to the terminal).
  • Changing the capture doors’ existing API.
  • none
  • Raised while reviewing the Router MVP (sdlc/router commit c9d173300), where injected spawnSync/spawnAsync dependencies called bare had to be renamed to avoid the guard’s false positive.

← Back to Tasks