Four sprints shipped before the installer resolved to the right directory

The installer was twenty lines. It resolved SCRIPT_DIR with a /../ suffix: the the article pipeline pattern, where the plist template sits one level above the installer. On the idea park-soak, installer and template are co-located. The suffix walked SCRIPT_DIR into the wrong directory. Four sprints had shipped before anyone verified that assumption.

It wasn’t the only thing that hadn’t transferred.

What the idea park is

The idea park is the idea-capture system for the editorial pipeline. Every backlog writer routes through it: Claude sessions, scheduled crons, sub-agents with Bash access. The design goal is uniform audit: every parked idea tagged with a caller, a timestamp, a source. The caller is set via BB_CALLER. Without it, the idea park defaults to claude_session and the provenance trail is wrong.

That convention lived in documentation before anything enforced it. It still isn’t enforced at runtime. Four sprints shipped while it remained aspirational.

Phase F was registered in docs/roadmap.md during the cleanup sprint. The idea park had been built from a plan at docs/superpowers/plans/2026-06-07-bbpark-implementation.md. The roadmap (the living spec) didn’t know the idea park existed until F1 through F4 had all shipped. Process miss, not code miss, but it belongs in the record.

The three failures

SCRIPT_DIR. the article pipeline was the pattern source. Its layout: the plist template lives in the parent directory of the installer. The installer computed SCRIPT_DIR with a /../ suffix that walks one level up from the script’s own location. Correct for the article pipeline.

The idea park-soak adopted the installer and adjusted the filenames. The /../ came along. The idea park-soak co-locates installer and template (the inbox-sync convention) so the path computation resolved to the directory above, not the one containing the template. Dropping the /../ fixed it. One character. Four sprints of incorrect resolution before anyone checked the layout assumption against the actual directory structure.

Path sanitisation. the idea park brainstorms write a provenance line into docs/roadmap.md. The function that builds it, _build_provenance, called os.getcwd() without sanitising the result. A separate function in the same codebase, _build_context_hint, already had _sanitise_cwd(), a helper that converts absolute paths to ~/-relative equivalents.

Two code paths, same data, one sanitised and one not. Four brainstorm entries (B-016 through B-019, written during smoke runs) landed with /Users/<username>/Development/BBBrain/... paths hardcoded in the roadmap file. Caught after the sprint-four merge. Backfilled manually. The fix: wire _sanitise_cwd() into _build_provenance, matching what _build_context_hint already did.

BB_CALLER convention drift. Agents that invoke the idea park via Bash are supposed to set BB_CALLER=subagent:<name> before the call. Without it, the idea park logs claude_session as the caller. The convention was in the documentation. Nothing scanned for it. An agent could declare the Bash tool, call the idea park, and the audit trail would misclassify the caller silently.

Sprint five added lint_agents.py. It scans ~/.claude/agents/*.md and flags any agent declaring the Bash tool without a BB_CALLER mention. This is documentation lint, not runtime enforcement: an agent can pass the check and still omit BB_CALLER at execution. It surfaces the gap; it doesn’t close it.

What review didn’t catch

The SCRIPT_DIR case is the most instructive. The installer was reviewed as code. The /../ suffix is semantically correct for the the article pipeline layout. A reviewer reading that line without knowing the idea park-soak’s directory structure would see nothing wrong. The code is correct in its original context. What review didn’t verify was whether the context had transferred.

That’s the class of bug that copying introduces. Directory layout assumptions aren’t visible in the code itself; they’re visible in the repository structure, one level above the diff.

The path sanitisation miss has different character. _sanitise_cwd() existed. A reviewer would need to know it had been applied to _build_context_hint, notice _build_provenance wasn’t calling it, and flag the omission across two functions that may not both be on the diff. In practice it surfaced by running a smoke test and inspecting the roadmap output.

BB_CALLER had no enforcement surface for review to check against. Nothing existed to violate. The fix was creating the enforcement, not sharpening the review.

What shipped

Five items in the cleanup sprint:

  • install_bbpark_soak_cron.sh: dropped /../, SCRIPT_DIR resolves to the co-located directory.
  • _build_provenance: wired _sanitise_cwd() in, matching _build_context_hint.
  • docs/roadmap.md B-016–B-019: backfilled the four leaked absolute paths.
  • lint_agents.py: scans ~/.claude/agents/*.md, warns on missing BB_CALLER documentation.
  • docs/roadmap.md Phase F: F1–F4 ship history plus F5 (agent lint) and F6 (soak cron retirement, pending) now registered.

Phase F registration should have happened during sprint one. The rest required the feature to be running before they surfaced.

The pattern

Three bugs, one shape: correct in the source context, wrong in the destination, invisible without deliberately checking the difference.

Copy-pasted installers carry directory assumptions that aren’t visible in the code. The review habit they need isn’t reading the logic (the logic is usually fine), it’s checking whether the path expectations transfer to the new layout. That means looking at the repository structure alongside the diff, not just at the diff.

Paired code paths that do similar work should use the same helper. If _build_context_hint sanitises and _build_provenance doesn’t, that’s a catch, but only if the reviewer knows to look for paired behaviour across both functions. The convention is established now. Future review has a precedent.

The lint tool handles the third class differently: it moves the check from “hope someone notices during review” to “CI warns on first read.” Runtime enforcement, if it comes, is a later sprint. Out of scope for now. Named as such so it doesn’t get forgotten.

All writing