Setup — P0 Once Per PROJ, Fully Automatic
Owns the P0 phase of the agent workflow (CONCEPT.md §4). This skill replaces the FIRST-ACTION preflight block that used to live inside Skill 5: setup happens ONCE per PROJ, before any execution session, so implementer lanes start with a clean branch, verified tools, and a machine-readable state file.
P0 is script-driven — run the scripts, record the results — with exactly ONE bounded judgment step: the ground file (step 6b, context-curator judgment). There are no user questions in P0.
Input
specs/PROJ-<X>-<theme>/state.jsonatCP1:approved(sealed by checkpoint at Checkpoint 1)specs/PROJ-<X>-<theme>/3-4_plan/— wave plans +wave-gate-config.json
Workflow
0. Gate on approval
CP1:approved→ proceed.- state.json missing or any other phase/status → STOP. Route to checkpoint (4a) — P0 never runs on an unapproved plan.
Before changing state or creating a worktree, run the plan consistency gate:
PLAN_DIR=specs/PROJ-<X>-<theme>/3-4_plan
[ -d "$PLAN_DIR" ] || PLAN_DIR=specs/PROJ-<X>-<theme>/6_plan
node ~/.claude/skills/4b_setup/scripts/validate-wave-plan.mjs "$PLAN_DIR"
Any non-zero exit is a hard stop: route back to writing-plans. For a
legacy PROJ, select the existing 6_plan/ directory as above; never create a
parallel current-layout directory. worktree.sh prepare repeats this exact
validation before it creates anything.
1. Persistent PROJ worktree + BASE_SHA
From the control checkout root, run:
WORKTREE=$(~/.claude/skills/4b_setup/scripts/worktree.sh prepare <X> <theme>)
cd "$WORKTREE"
P0_STATE=$(bash ~/.claude/skills/4b_setup/scripts/state.sh get <X> <theme> '.phase + ":" + .status')
case "$P0_STATE" in
CP1:approved|P0:blocked) bash ~/.claude/skills/4b_setup/scripts/state.sh transition <X> <theme> P0 running ;;
P0:running) : ;; # interrupted P0 resumes without a duplicate transition
P0:done) P0_ALREADY_DONE=1 ;; # refresh-only; never transition or reseal
*) echo "unexpected P0 resume state: $P0_STATE" >&2; exit 1 ;;
esac
Use the installed absolute state.sh path until the synchronization below has installed the helper set; scripts/state.sh is not assumed to exist at CP1. The synchronized, committed repo helpers are then canonical for the run.
When P0_ALREADY_DONE=1, synchronize and validate the helpers, then commit any refresh separately; skip all phase transitions and the P0 seal commit.
prepare requires a clean control checkout at its committed CP1 HEAD,
creates proj/PROJ-<X> at that exact commit, and tags
proj-PROJ-<X>-base. The default persistent path is the sibling
<parent>/<repo>-proj<X>; SKILLCHAIN_WORKTREE_ROOT overrides the parent.
An existing correctly registered branch/path is resumed idempotently. A dirty
control checkout, mismatched existing branch, conflicting registration, or
occupied target path is a hard stop.
.env.local is linked from the control checkout only when the source exists
and Git ignores it. If only .env.local.example exists, stop and require the
local secret file. Secret values are never printed or stored. Dependencies are
installed reproducibly from the one supported lockfile inside the worktree;
node_modules is never linked. The helper records .worktree metadata,
.base_sha, and .branch through state.sh, including shared database mode,
dev port (SKILLCHAIN_DEV_PORT or frontend.dev_url), and cleanup status.
Database migrations and auth-consuming commands must run through the shared resource lock:
scripts/worktree.sh with-shared-lock -- <command...>
The default lock lives in the Git common directory and therefore serializes
parallel worktrees of this repository. Set
SKILLCHAIN_SHARED_RESOURCE_LOCK to an explicit shared path to override it.
The helper defaults to exclusive for compatibility. Optional
with-shared-lock --shared --timeout <seconds> -- <command> allows independent
test lifecycles to overlap on that same file while still excluding migrations.
Use it only after proving fixture/actor isolation; it is not an automatic
read-only SQL classifier. Migrations, resets and global fixture changes retain
the default exclusive mode across every worktree. Do not split migration and
test locks: that would allow schema changes during tests. The lock does not
establish schema-version compatibility or FIFO migration order.
Record the lock's actual path, acquisition layer, wait budget and DB/browser
owner in the runtime constraints. Preserve exit 73 through project wrappers;
an outer command timeout must not obscure the lock wait. Browser probes close
their named session on every exit, inside the owned resource window; their
daemon can retain fd 8 after the opening command returns. Keep DB-backed tests
and browser runs serialized when they share fixtures. Execution's
references/worker-lifecycle.md describes diagnosis and handover.
Before running any repo preflight, synchronize from the installed skill tree:
node ~/.claude/skills/4b_setup/scripts/sync-framework.mjs
A non-zero exit blocks setup until the reported helper differences are reconciled. The command plans the whole inventory before copying anything; unrecognized or modified project copies are never overwritten. Compare each conflict with its installed source, merge the needed upstream changes while preserving project adaptations, test the result, then rerun with --adopt scripts/<reviewed-file> (repeat the option for multiple files). Do not adopt stale code to bypass an update. No separate user confirmation is required for an already-authorized helper refresh.
2. Permission preflight (Claude host)
Verify Claude Code was started with --dangerously-skip-permissions, or
run bash scripts/merge-project-settings.sh once per project (copy from
~/.claude/skills/5_executing/scripts/merge-project-settings.sh if
missing) so the allowlist + defaultMode: bypassPermissions are in
place. Without this, overnight runs die on the first permission prompt.
3. CodeRabbit config preflight
If .coderabbit.yaml/.coderabbit.yml is missing at repo root, copy
~/.claude/skills/5_executing/references/coderabbit-template.yaml to
.coderabbit.yaml and include it in the setup commit.
4. Tool + auth preflight
Run bash scripts/preflight.sh <X> <theme> (if scripts/ lacks it, copy
the WHOLE 4b_setup helper set first — preflight.sh, ponytail-check.sh,
compile-context-bundles.mjs, context-injector.mjs, state.sh,
worktree.sh, validate-wave-plan.mjs, migration-drift-check.sh from
~/.claude/skills/4b_setup/scripts/ — preflight calls its siblings; a
lone copy also works, it falls back to the installed skill tree). It checks
the CONCEPT.md §7 CLI list including auth states and a bounded live
probe per provider (claude hard, codex degradable) and writes the
preflight block into state.json:
- Exit 0 → continue. If it reports DEGRADED (codex missing or
unauthenticated), the run continues single-provider —
degradedis now set in state.json and every review falls back to MODEL-opposite. Never work around this flag and never unset it by hand. - Exit 1 → hard tool missing = stop condition (§8): transition to
blocked (
bash scripts/state.sh transition <X> <theme> P0 blocked), write the stop report, do not continue.
For a repo with supabase/migrations/, preflight also runs
migration-drift-check.sh: every git worktree of this repo shares ONE local
Supabase Postgres instance, so another worktree may have applied migrations
absent from this worktree's own supabase/migrations/ folder — code and
grants here would then silently run against a schema this branch never
declared. That is a stop condition too (fix: supabase db reset from this
worktree); a worktree merely having pending local migrations is normal and
not flagged. wave-gate.sh re-runs the same check at the start of every
wave, since a wave can run long after P0 in a repo another worktree has
since advanced.
Preflight also reports the repo's structure state — a missing
docs/components.md, a still hand-written one, a missing or oversized
docs/DESIGN-SYSTEM.md. This is a backstop: the decision belongs to
1b_visual-companion, which records it under ## Design System State in
layout-decision.md. If that record exists, honour it and do not re-ask.
None of these is a stop condition — repos predating the design system, and
backend-only repos, run unchanged. Only for a repo that never went through UI
discovery, put it to the user as a decision instead of acting on it:
"This chain expects
docs/DESIGN-SYSTEM.md(design rules, ≤80 lines) — this repo has none. Create it now via1c_frontend-design, or skip and proceed without design rules?"
Skipping is a valid answer; record it and continue. Only the hand-written
registry needs a real migration (move the purpose texts into doc blocks above
the exports, then --force), and even that is optional — the generator
refuses to overwrite the file, and the wave gate reports the pending
migration without blocking.
5. Copy framework scripts + templates into the repo
The synchronizer in step 1 installs this inventory and records source/project SHA-256 hashes in .skillchain-helpers.json. Verify it before sealing P0:
node ~/.claude/skills/4b_setup/scripts/sync-framework.mjs --check
Commit the manifest and changed helpers/templates with setup. Do not manually overwrite an older helper. Unmodified managed copies update automatically; reviewed adaptations survive until their installed source or local bytes change, at which point reconciliation is required. Run only at setup/resume boundaries, never during a gate or while workers are editing these files.
| From (installed skill) | To |
|---|---|
4b_setup/scripts/state.sh, preflight.sh, env-local.sh, ponytail-check.sh, compile-context-bundles.mjs, context-injector.mjs, worktree.sh, validate-wave-plan.mjs, migration-drift-check.sh, sync-framework.mjs |
scripts/ |
4b_setup/manifests/roles/*.md |
templates/roles/ |
4a_checkpoint/templates/decisions.md.tmpl |
templates/ |
cross-review/scripts/cross-review.sh, review-with-claude.sh, review-with-codex.sh |
scripts/ |
cross-review/templates/cross-review-prompt.md.tmpl |
templates/ |
6_qa/scripts/ledger.mjs, harvest-debt.sh |
scripts/ |
7_documentation/scripts/curation-caps.sh |
scripts/ |
0b_intake/scripts/intake-seal-check.sh |
scripts/ |
5_executing/templates/agent-md-entry.md.tmpl |
templates/ |
8_delivery/scripts/conflict-probe.sh, render-pr-body.mjs, ci-poll.sh |
scripts/ |
8_delivery/templates/pr-body.md.tmpl |
templates/ |
5_executing/scripts/wave-gate.sh, quality-gate-proof.sh, quality-evidence.mjs, gen-component-registry.mjs |
scripts/ (as today) |
chmod +x the shell scripts.
After copying, preflight merges the framework-owned files into Biome's
files.ignore (or creates biome.json) and adds .state.lock to
.gitignore. This preserves target linting for target code while keeping the
versioned framework helpers available for the run.
Before committing the copied files, run the target's existing lint command
when it has one (npm run lint --if-present, or its configured equivalent)
and node scripts/gen-component-registry.mjs --check when component folders
exist. Exit 3 means a hand-written or safety-refused registry: report it and
do not overwrite it; any other non-zero registry result must be fixed before
P0 is sealed.
5a. Verify the verifier
Before trusting an unattended run, prove one applicable gate can reject a
controlled bad change. When auth_budget is configured, its project hooks must
honor SKILLCHAIN_AUTH_BUDGET_NEGATIVE_CONTROL=1 without consuming a hosted
identity. After step 5 copied the gate, run:
CONTROL_WAVE=$(jq -r '.waves | keys | map(tonumber) | min' "$PLAN_DIR/wave-gate-config.json")
EXPECTED_AUTH_RC=$(jq -r '.auth_budget.exhausted_exit_code // 75' "$PLAN_DIR/wave-gate-config.json")
set +e
bash scripts/wave-gate.sh --auth-budget-negative-control "$CONTROL_WAVE" <X> <theme>
CONTROL_RC=$?
set -e
[ "$CONTROL_RC" -eq "$EXPECTED_AUTH_RC" ]
jq -e --argjson code "$EXPECTED_AUTH_RC" \
'.ralph_status == "infrastructure_failed" and .infrastructure_failure.exit_code == $code' \
"specs/PROJ-<X>-<theme>/5_progress/ralph-wave-${CONTROL_WAVE}.json"
This exercises preflight_cmd and, when configured, rate_limit_evidence_cmd
through the real shared-lock/runtime path and must produce retained non-empty
evidence. It is the required negative control for an auth-budget project. For a
project without auth_budget, pick the cheapest safe control: temporarily break
an AC assertion, authorization/RLS policy, or the build; run its real gate red,
restore the exact file, and re-run it green. Record the command, expected red
reason, retained evidence, and restored green result under ## Negative controls
in 5_progress/PROJ-<X>-progress.md. Never use a production mutation, destructive
migration, or syntax-only failure. If no safe control exists, STOP; an untested
verifier is not a P0 success.
6. Context system (compile bundles, ground file, injectors)
6a. Compile the context bundles.
node scripts/compile-context-bundles.mjs compile <X> <theme>
- A role over budget is written to
context/blocked-roles.jsonand receives no bundle; all fitting roles are still compiled. Do not spawn a blocked role (the injector refuses it). Condense that role's sources, or raise only its manifest budget when its documented scope genuinely requires it. - Record the hashes in state:
bash scripts/state.sh set <X> <theme> .context.bundles "$(jq -c . specs/PROJ-<X>-<theme>/context/bundles.lock.json)" - The compiler also projects
.claude/agents/skillchain-<role>.mdagent files — onlyskillchain-*files are ever written, existing agents are never touched.
6b. Generate the ground file — the one bounded judgment step in P0.
Write specs/PROJ-<X>-<theme>/ground-file.md: assumptions the plans rely
on (stack versions, conventions, data-model facts), each VALIDATED
against the codebase, and ONLY what docs/ does not already state (§5
redundancy rule — the ground file never duplicates curated docs). Then
recompile (6a) so the bundles carry it.
6c. Activate the injector adapters.
- Claude: the preflight (step 4) already merged the SubagentStart hook
(
node scripts/context-injector.mjs claude) and removes anyPONYTAIL_SUBAGENT_MATCHERfrom.claude/settings.json, so Ponytail's native all-subagent path also covers generic implementation fallbacks.bash scripts/merge-project-settings.shadditionally merges the execution permission allowlist. Subagents then receive their type-scoped bundle automatically — never paste bundles into spawn prompts. - Codex: prompt-file delivery — a codex lane reads
specs/.../context/bundle-<role>.codex.mdbefore implementing (node scripts/context-injector.mjs codex <role> --path); the runner's lane prompts point there. - Both providers receive the same canonical bundle hash (recorded in 6a); the injector refuses a stale bundle (hash mismatch → injects nothing and warns).
6d. Ponytail parity is already gated inside step 4's preflight
(ponytail-check.sh: absence or version/mode mismatch across active
providers blocks P0). Never work around a red gate; PONYTAIL_ENFORCE=0
is the loud, recorded escape hatch — it lands in state.json and the
reports, never silent.
7. Seal P0
bash scripts/state.sh transition <X> <theme> P0 done- Commit everything from steps 1–6 on the PROJ branch:
chore(PROJ-<X>): P0 setup — branch, preflight, framework scripts, context bundles
→ NEXT ACTION: start execution — either the phase runner
(runner/run-phase.sh P5 <X> <theme>, autonomous dual-lane) or the
executing skill (5) directly in this session.
When P0 is runner-managed, the runner detects the registered PROJ worktree,
re-executes itself there once, and starts every later phase from that path. The
SKILLCHAIN_WORKTREE_REEXEC guard prevents a re-exec loop.
Completion Checklist
- state.json was
CP1:approvedbefore starting; nowP0:done - wave plan consistency validator passed before worktree creation
- persistent
proj/PROJ-<X>worktree exists;base_sha,branch, and.worktreemetadata are in state.json -
.env.localis an ignored control-checkout symlink (or explicitly absent); dependencies are isolated -
preflightblock in state.json;degradedset truthfully -
.context.ponytailin state.json (parity gate result, enforced truthfully) -
specs/.../context/has canonical + claude/codex bundles;.context.bundleshashes in state.json -
ground-file.mdwritten (only assumptions docs/ does not state) - SubagentStart injector hook merged into project settings (Claude host)
-
scripts/+templates/contain the framework copies, executable -
.coderabbit.yamlpresent at repo root - One safe negative control was observed red, restored, and recorded
- One setup commit on the PROJ branch
Failure Behavior
Any hard preflight failure or git error is a stop condition (§8): state
→ P0:blocked with the exact cause in .stop.reason, stop report
written, nothing half-configured left silently in place. P0 is
idempotent — after fixing the cause, re-run this skill; completed steps
(existing branch, identical script copies) are skipped, not duplicated.
Legacy Folder Layout
PROJ folders created before the layout rename use different subfolder names. Mapping, old → current:
2_visual-companion/ → 1b_visual-companion/ · 4_design/ → 1c_design/ ·
5_mockups/ → 1d_mockups/ · 3_PRDs/ → 2_PRDs/ ·
8_handoff/ → 2b_handoff/ · 6_plan/ → 3-4_plan/ ·
7_progress/ → 5_progress/
If an expected folder is missing but its legacy twin exists, read from the legacy one and keep writing where the existing files already are. Never create a second folder next to it — a split PROJ is worse than an old name. Say it once, then continue either way:
"This PROJ uses the old folder layout (
<old>). Rename the folders to the current names, or continue with the existing layout?"
Renaming is a git mv per folder plus a search for the old paths in the
PROJ's own documents. It is never a precondition for this skill.