Public: anyone can read this; the crew can change it.
Every place the forge starts a process is listed in one test, each with the reason its arguments are never a person's text #10942
Check · Review · Land · Live done — Landed and live in production.
`test/spawn-sites.test.ts` now lists every file under `src/` that starts a process: 34 files, each with one line saying why its arguments are fixed or validated. The test scans `src/` for `Bun.spawn`, `Bun.spawnSync`, `Bun.$`, `spawn` or `$` imported from `"bun"`, and `child_process`. A process-starting file that isn't on the list fails the test, which names the file. So does a listed file that no…
shipped production is serving it deploy cca14ac
test/spawn-sites.test.ts now lists every file under src/ that starts a process: 34 files, each with one line saying why its arguments are fixed or validated. The test scans src/ for Bun.spawn, Bun.spawnSync, Bun.$, spawn or $ imported from "bun", and child_process. A process-starting file that isn't on the list fails the test, which names the file. So does a listed file that no longer starts a process. docs/ARCHITECTURE.md gets one line under Hygiene saying where the list lives. No behaviour changes.
What to look at:
- The reasons are the review. They describe what the arguments are; they don't say no person's text ever reaches them. Three places carry text someone wrote, and their entries say so:
- src/apps/process.ts and src/apps/tty.ts run a repository's forge.json command through /bin/sh -c when there is no cell. That command is written by someone with write access to the repository, not taken from a request.
- src/crew/claude.ts and src/crew/plan.ts pass an issue's text as a single prompt argument after its flag, never through a shell.
- Count: the issue estimated 37 files. The real count is 34: the rough grep also caught test files inside
src/(*.test.ts, which the scan skips), mentions in comments and regex$signs. The test checks itself that comments,typeof Bun.spawnand regex$don't count as sites. - What's left out: the check is per file, not per call, so a second spawn added to a listed file isn't flagged. A per-call list would break every time code shifted lines. I also didn't cover processes started indirectly, such as
new Worker.
The keep_plan tool wasn't available in this run, so the issue's plan wasn't updated.
To try it: bun test test/spawn-sites.test.ts passes (5 tests). Add Bun.spawn(["ls"]) to any file in src/ and it fails, naming that file. bun scripts/gate.ts and bun x tsc --noEmit both pass.
🤖 Generated with Claude Code
Approved. test/spawn-sites.test.ts lists 34 files with a reason each, fails by file name on an unlisted site, fails on a listed site that has vanished, and ARCHITECTURE.md says where the list lives. Self-tests cover each failure and the comment, typeof and regex-dollar false positives. It adds no dependency and no behaviour, and it reads src/ once, so it is fast. The README is untouched. I did not run the suite; the pull is green and I read the diff only.
Two things I would do next:
- The scan is line by line, so a multi-line
import {\n spawn,\n} from "bun"slips past it, as doesconst { spawn } = Bun. Match the import across lines, or scan whole text with the comment lines stripped. - The scan is per file, so a second spawn in a listed file is never flagged. Count the spawn calls per file and pin the count in the list, so a new call forces someone to reread the reason.
test/spawn-sites.test.tsnow lists every file undersrc/that starts a process: 34 files, each with one line saying why its arguments are fixed or validated. The test scanssrc/forBun.spawn,Bun.spawnSync,Bun.$,spawnor$imported from"bun", andchild_process. A process-starting file that isn't on the list fails the test, which names the file. So does a listed file that no longer starts a process.docs/ARCHITECTURE.mdgets one line under Hygiene saying where the list lives. No behaviour changes.What to look at:
-
src/apps/process.tsandsrc/apps/tty.tsrun a repository'sforge.jsoncommand through/bin/sh -cwhen there is no cell. That command is written by someone with write access to the repository, not taken from a request. -src/crew/claude.tsandsrc/crew/plan.tspass an issue's text as a single prompt argument after its flag, never through a shell.src/(*.test.ts, which the scan skips), mentions in comments and regex$signs. The test checks itself that comments,typeof Bun.spawnand regex$don't count as sites.new Worker.The
keep_plantool wasn't available in this run, so the issue's plan wasn't updated.To try it:
bun test test/spawn-sites.test.tspasses (5 tests). AddBun.spawn(["ls"])to any file insrc/and it fails, naming that file.bun scripts/gate.tsandbun x tsc --noEmitboth pass.🤖 Generated with Claude Code
Closes #10934