Imported from bllshttng/footnote (
skills/target/SKILL.md). Install upstream withnpx skills add bllshttng/footnote --skill target. Copyright stays with the author.
Target
Get it done. From idea to a green, reviewed PR.
When $CODEX_THREAD_ID is nonblank, before any routing or work, Print exactly once:
codex posture: target uses the native Stop loop on the main thread; delegated work uses spawn_agent; bg dispatch runs on claude and opencode.
The spine (happy path - read this first)
resolve node → fno do target start <node> worktree off origin/main + claim + init prints the orienter
→ Step 0 (only if STALE) orienter says boundary-reconcile: STALE -> read blocker diffs, append landed-facts sections
→ implement edit the plan; atomic commits as you go
→ validate fno doctor test (real exit code; not bare pytest)
→ review fno do target request-self-review on local HEAD (pre-push, no --pr)
→ fix + re-review only when the round raised findings; one clean round is enough
→ /pr create Haiku worker opens the PR; the body carries the reviewed-at line
→ <promise>MISSION COMPLETE... PR green + reviewed = done; merge if config.auto_merge.enabled
That is the whole job when a backlog node or plan is already bound. fno do target start prints an orientation report (node, worktree, tests, done-when) - read it and go. Everything below is detail on a spine step or an "only if" branch you skip unless its trigger fires.
Which path you are on is a READ, not a guess - dispatch off the node's real state (fno backlog get <id>), never self-classification:
- ready node (a plan is bound and its frontmatter reads
status: ready): you are on the spine above. The idea-first work (think, blueprint, discovery) is already done - do not re-open it or load its references. The size profile (S/M/L) still sets the post-build phases (M adds docs + external; L adds adversarial, browser, clean - references/pipeline-and-philosophy.md). Multi-wave plans continue in this session across wave boundaries. A flat single-file plan also skipsph_write. - design-rung node (a plan is bound but its frontmatter still reads
status: design): take the/blueprint-first branch below, then the spine. - bare idea (no plan): take the
/blueprint-first branch below, then the spine.
Enter the worktree after the receipt (harness step, do this before implementing). The fno do target start receipt names a worktree: path and ends with a cd <path> to continue line - but a shell cd does not persist across tool calls, so prefixing every later command with cd <worktree> && is the failure this step prevents. Instead call the harness EnterWorktree tool with path set to that receipt worktree line; the session then runs from inside the worktree and every file edit is worktree-relative. This is location-agnostic: any path in git worktree list is enterable on first entry, so it works the same for a configured worktrees_base, the deprecated conductor base, or the harness-native .claude/worktrees/ default - never hardcode a base path, read it from the receipt. Two caveats worth knowing: ExitWorktree never removes a path-entered worktree (it only returns you to the launch dir; removal is scripts/setup/archive-worktree.sh's job), and after entering, same-session switches to another worktree are restricted to .claude/worktrees/ - irrelevant for a one-node session, surprising only if you try to hop worktrees mid-run.
If the invocation carried a --no-merge flag, pass it through to fno do target start (or to fno do target init). The flag is the sole prose-level carrier of a refusal. start resolves its argument to a bare node id before calling init, and the fold reads no free text (x-9d11). A no-merge word in the argument sets nothing. There is deliberately no --auto-merge twin: granting stays on config / TARGET_AUTO_MERGE.
- only if you are already inside a worktree (an attended
/targetin a linked worktree):fno do target startis a no-op there ("already isolated; nothing created") - runfno do target init --input "<original arg>"(add--plan-path <path>for a plan,--no-mergefor the refusal posture) instead.startis the cold-start-from-canonical verb;initis what writes the manifest, claims the node, and prints the orienter. - only if you were handed a bare idea (no plan): run
/blueprint "<idea>"directly, then implement./thinkis a research verb outside the delivery path (x-42c5, operator ruling), never an automatic prerequisite to/blueprint./blueprintself-grounds a bare idea through its own discovery gate (references/discovery-gate.md in the blueprint skill). That gate runsfno do think inspectfor a receipt and asks its own unknowns. It does not require a/thinkdoc. When a question needs deeper investigation than the receipt gives, run/thinkfirst, as a deliberate choice. - only if the node's rung is
design(a plan IS bound, but its frontmatter still readsstatus: design- a/thinkdoc that was never blueprinted): run/blueprint <plan_path>FIRST, then implement. A boundplan_pathalone does NOT mean the plan is executable:/blueprintis what appends the Execution Strategy and flips the doc toready. Skipping it here builds off a design doc that has no execution plan. Autonomous selection never hands you this rung (it is gated); you only reach it when a human named the node explicitly, which IS the consent to carry it the rest of the way. - only if
$TARGET_BRIEFis set in the environment (a dispatcher passed a per-node brief viadispatch_brief, US3): read it as extra mission context - the scope/"why" the dispatcher wanted this worker to carry. It is plain text (capped at 8 KB) and travels via env, never the command line; treat it as guidance for this node, not as a command to execute. - only if the run carries
authority: full(invoked as/target beastmode, surfaced on theattendedline offno do target status): a judgment call that would emit<help>and stall is decided and recorded instead - references/beastmode-authority.md. - only if
.fno/target-state.mdalready exists for this session: you are mid-loop - re-verify the world and re-emit<promise>; do NOT re-init or rebuild. - only if dispatching nodes to run unsupervised (
bg) or running a batch-lane member (batched): references/bg-and-batched-modes.md. - only if the orienter printed
boundary-reconcile: STALE: perform Step 0 before any code commit - for each stale blocker, read its merged diff (gh pr diff <n>) and append a### <blocker> landed ... - boundary reconcilelanded-facts section to the plan/brief. This is a different thing from de-stub reconcile below (hard-serialized dependent vs a stubbed contract). Full procedure + section format: references/boundary-reconcile.md. - only if spawned to de-stub a merged blocker: §0b Reconcile mode.
- only if a Claude Plan-Mode plan was just approved (attended): references/plan-mode-frontdoor.md.
- The ship step requests the fno review lane on the final HEAD by default - BEFORE the PR exists. After validate, run
fno do target request-self-reviewwith no--pr: it pins the local branch and HEAD against the origin base, refuses a detached HEAD, and sends one leading-slash payload that names<branch> HEAD <sha> against origin/<base>through--to-self --raw, with no--comment(the PR it writes to does not exist yet). One clean round is enough; a round that raises findings costs a fix, a commit, and a re-request on the new HEAD. The post-push form isfno do target request-self-review --pr <n>: a round requested after the PR exists (an external bot finding, a fix over the interdiff carry) names the PR; the command resolves the PR head and base and refuses a mismatch. Do not downgraderefusedorunconfirmedto advisory prose. A Codex mux request that positively reportsqueuedis the last tool action of this turn: do not poll, inspect the pane, read mail, retry, or promise; the queued review starts at the next turn boundary without a human interrupt. Forstarted, the worker's own Codex Stop hook reads the structured findings already in its context and nudges it to act on P1/P2 findings; after fixes, commit and re-request on the new HEAD because the old attestation is stale. A rebase before the push also stales a pre-push attestation: the ledger row keeps the branch name, but its pinned sha is no longer HEAD, so coverage reads uncovered - re-run the bare form on the new HEAD instead of pushing a stale verdict. Do not route this act path through a king, daemon notification, or external reader. Do not emit<promise>until the clean head-pinnedcode-reviewattestation exists. The decision ispreship_review_plan(config.review.reviewers)incli/src/fno/review_capability.py, and the named reviewer is the lane, which runs inline on every harness - a transport that cannot fire it is a config error the init gate catches, not a harness limitation to route around.sigmais retired: a config still naming it is refused at init with the lane named as the replacement, and no post-ship panel run exists to defer to (references/ship-and-promise.md).
Multi-CLI: If not on Claude Code, see references/cli-tool-mapping.md for tool equivalents.
Provider parity is hook-driven. The shared state machine and completion gates stay canonical; provider-specific behavior must come from the hooks layer and provider-scoped agent artifacts rather than from forked per-provider pipelines. Gemini runs sequential main-thread only: a parallel wave downgrades and records the reason. There is no project-agent upgrade path, that mode was retired when Google deprecated the Gemini CLI.
Gotchas
Environment-specific traps that defy reasonable assumptions. Read these before you hit them.
fno doctor test, not barepytest.fno doctor test [paths...]pins the worktreePYTHONPATH, bypasses the rtk tee wrapper, and returns the real exit code. Barepytestin a worktree imports the wrongfnoand can report a false green;cmd | tailmasks the real$?.- Read the RESOLVED
auto_merge_approvedfrom the manifest, neverfno config get auto_merge. Init folds config with this run's modifiers./target bginjects--no-mergeby default. The raw config tells you to merge against an explicit per-run prohibition. git checkout -- <file>destroys uncommitted work and is NOT stash-recoverable. In a stale-base worktree,git add -Acan revert an unmerged merge - stage named files, never-A.- The manifest is write-once. After
fno do target init, the only legal write is first-filling an emptyplan_pathviafno do state set. There are no gate booleans or status fields to set; any other field write exits 5. - A
bgworker is unsupervised, NOT headless./target bgdispatches and continues without blocking, but the worker is not invisible: it registers an agent-view row and keeps an attachable pane, so it stays observable and drivable after launch (fno agents logs <name>, or attach). "Fire-and-forget" describes only the dispatching session's non-blocking stance, never the worker's nature.
When another node costs you time
A target run that loses time to a node it does not own is exactly what the demand signal records. fno backlog encounter <node-id> --evidence "what it cost you" votes that node up the demand read; evidence is required, and the cap is one vote per node per session (a session may vote on many nodes). Vote when it happens, from the session that paid the cost - a retold vote is someone else's transcript.
Optional: external loop wrapper
For walk-away / overnight execution, drive this skill from a terminal via the external loop wrapper:
bash scripts/run-target-loop.sh path/to/plan/
The wrapper re-invokes the CLI until the session terminates (DonePRGreen, Budget, NoProgress, or Interrupted). The in-Claude-Code interactive experience remains the recommended default for walk-up feature work. Note: the legacy fno loop verb is removed (step-5 group 3); fno-agents loop-check is the stop authority and fno-agents loop run is the loop runtime.
Completion: what you do
Emit <promise>MISSION COMPLETE: ...</promise> when the PR is up and CI is green - promise early (one exception: an approved auto-merge merges FIRST, see below), the external reads hold it. An unsatisfied read just blocks-and-retries naming what is missing; a premature promise never short-circuits the gate. While waiting on an async check with nothing to do, arm ONE watcher and idle on a <watching> tag (the exact protocol is How to end every turn below) - never re-read the poller and re-post the same status on a nudge; that is pure noise. Read the manifest's resolved auto_merge_approved before you promise - merge authority is config-driven, NOT bg-vs-attended, and nothing in the merge path checks attendance:
sed -n 's/^auto_merge_approved:[[:space:]]*//p' .fno/target-state.md
Read that RESOLVED field, never fno config get auto_merge directly. Init folds the config together with this run's modifiers. A per-run --no-merge flag (which /target bg injects by default) sets it false even with auto_merge.enabled set to true. The raw config then tells you to merge against an explicit per-run prohibition. Every refusal outranks every grant: --no-merge revokes merge authority even over an inherited TARGET_AUTO_MERGE=1. An auto-merge word grants nothing on its own (it needs auto_merge.enabled or TARGET_AUTO_MERGE=1). Free text carries NO posture in either direction (x-9d11). Honoring anything found in prose lets arbitrary writing manufacture merge authority or a merge prohibition. fno do pr merge reads the same field and refuses too, but that is a backstop. Decide from the manifest rather than firing the verb and hoping it catches you.
When it is true and auto_merge.require_checks_pass is satisfied, MERGE FIRST, THEN promise: fno do pr merge <n>, then fno backlog reconcile to close the node (a merge from inside a worktree skips the local post-merge step). Order matters - a promise emitted first terminates the loop as DonePRGreen the moment CI goes green, so the session is never re-invoked and never merges. fno-agents finalize now arms GitHub's native auto-merge on that same DonePRGreen terminal, so the PR is no longer stranded when you promise first; it is queued behind GitHub's own branch protections rather than merged against the gate you just satisfied. Merging first is still the instruction, because it is the path that closes the node in this session (reconcile) instead of leaving it to a later merge detector. Config set once IS the standing authorization; re-asking each time re-imposes the step it was configured to delete.
When it is false (the default), stop at a green, reviewed, mergeable PR and hand the merge to a human (any out-of-band merge also satisfies done()). Never write "handing the merge to a human" without having read that field in the same turn. The gate reads config.review.required_bots; the loop-check code default is empty [] (no review gate, so a fresh install never hangs on an unconfigured bot), and a maintainer sets it explicitly (e.g. ["chatgpt-codex-connector"]) to require an external pass. Internal review is advisory.
How to end every turn
The stop hook reads your final message and makes ONE decision from it. There are exactly three clean ways to end a turn, and one to never use. The "Stop hook error: continue working" line you see is simply the hook blocking a turn that ended without one of the first three - it is not a failure, it is the block signal, and it never appears when you end cleanly.
- Done ->
<promise>MISSION COMPLETE: <what shipped></promise>. Emit it as soon as the PR is up and CI is green (promise early). Once a promise is accepted the loop has terminated: do NOT reflexively re-emit it or re-post the same status on later conversational turns - answer what is asked and stop. - Waiting on an async check with nothing to do (CI pending, or a bot review not yet posted) -> arm ONE harness-tracked watcher whose command carries a hard timeout, then end with the tag and NOTHING else. Both waits are ONE plain command polling REST at a 60s interval; NEVER
gh pr checks --watchor agh pr viewloop - those spend the per-USER GraphQL quota every session on the machine shares, and a fleet of GraphQL watchers is what starves the merge guard. The plain-command shape is also the only one a worktree session's Bash isolation always admits: an inlinewhile/$(...)watcher loop is refused there as too complex to verify, which wedges the very turn the watcher was meant to unblock:- CI: background Bash
fno do pr wait <N> --until settled --timeout 30m(the sanctioned watcher; every tick rides the coalescing cache so N waiters on one PR cost one network read per TTL, a rate-limit backoff is ridden out, and it exits on the POSITIVE settled marker - a rate-limited read answerssettled: false, so an unanswered API keeps the wait going instead of reading as "nothing pending". Exit code is the status verb's: 0 green, 1 red, 2 still unsettled at the bound. Never hand-roll awhile/sleep/greploop in its place - each one is an uncoordinated poll against the shared quota) - review: background Bash
fno do pr wait <N> --until review --timeout 30m(wakes when a new review posts, or after ~30m; the review-count read is per_page=100 inside the verb, because gh api fetches ONE page and the default 30 saturates the count. Exit 0 = a review posted, 2 = the bound fired with none), NOT a checks watcher (it exits the instant CI is green, so on a review wait it wakes immediately and re-blocks) - then:
<watching reason="ci|review" pr="<N>" timeout="30m">loop-check verifies the wait against external truth and idles the session to ZERO re-invocations until the watcher fires. The watcher MUST be harness-tracked (background Bash / Monitor) - a detached process (nohup,disown, or a trailing&on the task itself) exits without waking anyone and the session idles forever. On wake: if it settled, proceed; if the bound fired and it is still pending, re-arm and re-emit. Never nametimeoutin a watcher. It is GNU coreutils and is ABSENT on a stock macOS, which ships nogtimeouteither, so the command dies withcommand not foundbeforeghever runs - the watcher no-ops, nothing ever wakes you, and the session idles forever on a wait that never started. That is the exact failure this section exists to prevent. Bound the wait with shell builtins or a verb flag (--timeout 30m), as above: both are always there, so there is no fallback branch to get wrong.
- CI: background Bash
- Still working -> just take the next action (a tool call). The stop hook only fires when you STOP, so mid-work turns never reach it.
Never end a turn with tag-less prose while the mission is incomplete. That is the ONLY thing that blocks - the hook re-invokes you with "continue working" for zero progress. If you have nothing to add and are not done, do not post: arm-and-tag (2) or take the next action (3).
Residual-turn austerity. The arm-and-tag turn (and any timeout re-arm) must be near-empty: the tag plus at most one short line. No status recap, no "waiting for it to settle", no restating what you armed. The transcript is the operator's review artifact; the wait machinery's job is to be invisible in it.
Only Claude sessions idle on <watching> today; codex/gemini keep the block-every-tick behavior until their daemon waker ships, so on those harnesses ending a wait turn near-silently still costs a nudge, but keep it terse anyway.
Drain reviews BEFORE you promise - even when required_bots is empty. CI green is NOT "ready to promise": the codex bot posts COMMENTED reviews carrying real inline findings while leaving reviewDecision EMPTY, so the shortcut "green checks + empty decision = ready" has shipped unaddressed findings. Read every posted review's inline comments (gh api repos/{owner}/{repo}/pulls/<n>/comments), at first-post rather than only at green, and run any configured local review gate + /codex:review --background before <promise> (local runtime, separate quota from the chatgpt-codex-connector App - an App usage limit never means codex coverage is gone). The full ordering, cursor-file watch protocol, the three review-gate flavors, and head-pinning are in references/ship-and-promise.md - load it when the PR is up.
Run tests with fno doctor test [paths...] (pins worktree PYTHONPATH, bypasses rtk, returns the real exit code) and read a PR's CI with fno do pr status <n> (one green|red|pending|unknown verdict) - not bare pytest or hand-rolled jq. To cancel: touch .fno/.target-cancelled.
Preflight is opt-in, CI is the gate. A stock config never runs scripts/ci/preflight.sh before a push. Every ship path asks fno do pr evidence-required, which reads config.preflight.required (default false) plus the FNO_SKIP_PREFLIGHT=1 escape. The Python lanes and the bash ship path cannot disagree. The pre-push obligation is the focused checks. They are cargo fmt --check for rust changes. For markdown changes run fno doctor lint style --surface markdown --files <changed .md> --diff-base origin/main. Commit first, and not for hygiene. The line numbers come from git diff <base>...HEAD, so they describe the COMMITTED file. The content is read from the working tree. On a dirty tree those are two different files. The gate then reads a violation against a line nobody touched, while the added line goes unread. The inspected N added line(s) count it prints is how you tell a clean scan from an empty one. A file-wide style-exception marker exempts that whole file from every rule, permanently, whatever narrow reason it states. The receipt names each file it skipped that way, so read those paths and not the count alone. 0 added line(s) across 1 changed file(s) reads the same for a file with nothing to inspect and a file skipped by its marker. This very file carries such a marker, so the gate never checks the paragraph you are reading. The tests covering the diff's blast radius complete the set. A project that wants the full 22-to-47-minute local rehearsal back sets preflight.required = true. Full contract: references/ship-phase.md and the repo-root docs/preflight.md.
Completion is decided by fno-agents loop-check from external truth (PR + CI + review), not from any file you write - you cannot self-authorize. The full machinery (immutable manifest, stop-hook shim, done() read list, fingerprint backstop, TerminationReason, degraded modes) is one Read hop away in references/completion-model.md.
The full pipeline + phase philosophy
For a from-idea run, a multi-phase run, or any M/L ready node, the whole phase map (think -> blueprint -> do -> clean -> review -> validate -> docs -> ship -> external) and the compose-don't-hardcode rationale (which skill runs each phase, on which model, and when a phase applies) live in references/pipeline-and-philosophy.md - load it to see which post-build phases (docs, external, adversarial) your size profile runs. A flat or small (S) ready plan does not need it: the spine is the happy path. Completion is the world (PR green + reviewed), never a phase checklist.
Usage
# Size profiles (primary interface)
/target S "fix the login bug" # small: do + PR, no ceremony
/target M "add user auth" # medium: operator + docs + external
/target L "rebuild billing" # large: everything including adversarial
/target "add user auth" # no size = medium (default)
# From existing plan or graph node
/target path/to/plan # default size (M)
/target L path/to/plan # override: run with full ceremony
/target M ab-9f5a1f8c # graph node ID resolves to plan_path
# Execution modes (combinable with sizes)
/target agent "feature" # subagent dispatch
/target fork path/to/plans-folder/ # worktree isolation per plan
/target bg ab-A ab-B # dispatch-and-continue: ready node(s) as claude --bg /target workers, unsupervised (US5)
/target bg --all-ready # dispatch every ready, non-deferred node; planning session keeps going
# Modifiers
/target beastmode <node> # walk-away authority: decide judgment calls, never stall
/target beast <node> # same thing (also accepts a mobile-autocorrected "beast mode")
/target clean "feature" # run /simplify after execute
/target adversarial "feature" # add adversarial challenge
/target auto-merge "feature" # auto-merge after external approves (needs auto_merge.enabled or TARGET_AUTO_MERGE=1; the word alone grants nothing)
/target --no-merge "feature" # disable auto-merge for this run (the flag is the sole carrier)
/target combo my-stack "feature" # route via a provider combo (Plan B, ab-0e5a921e)
# Controls
/target --max-iterations 20 "feature"
/target --budget 25 "feature"
/target resume
/target cancel
For the full execution-mode comparison, interactive-mode wizard, override-flag table, and context lifecycle (interactive vs unattended), load references/usage-detail.md.
| Subcommand | Name | Executor | Ceremony |
|---|---|---|---|
S / small |
Small | do | Build + PR only |
M / medium (or omit) |
Medium (default) | operator | + external, docs |
L / large |
Large | operator | Everything: research, adversarial, browser, clean |
Load references/size-profiles.md for the full capability matrix and references/flag-migration.md for the override-flag list.
For walk-away / overnight execution, run target unattended via the external loop wrapper (scripts/run-target-loop.sh); fresh-context restarts are governed by config.target.restart_after_n_turns.
Process
0. Cancel / Override
If cancel is passed: run the /fno:cancel-target command (display current state, drop a session-keyed .fno/.target-cancelled-final tombstone, remove target-state.md, exit). The tombstone lets the orphan detector allow a clean exit instead of re-blocking (ab-e95531e2). If no state file exists, report "No active session."
If override <reason> is passed: the operator-override machinery was removed in the control-plane collapse (no gates to bypass, no status field to flip). Override is no longer a supported subcommand. To close an off-ceremony session manually, touch .fno/.target-cancelled (which signals Interrupted to the loop-check verb), then run fno backlog done <node-id> to mark the backlog node complete. Acknowledge this to the user and stop.
0a. Background dispatch (bg) and batched mode (batched)
only if the argument leads with bg <node...> / bg --all-ready (dispatch ready nodes as unsupervised claude --bg /target workers - each keeps an agent-view row and an attachable pane, so "unsupervised" not "headless") or batched <node> (a batch-lane member run on a shared branch): neither is a normal pipeline. Load references/bg-and-batched-modes.md for the constrained flow before doing anything else.
0b. Reconcile mode (--reconcile <manifest>)
If ARGUMENTS carry a --reconcile <manifest-path> token, this is a G4 de-stub pass for a contract dependent whose blocker just merged (spawned by fno backlog advance / backlog.reconcile_dispatch). It is a constrained /target: pull main, run the executable drift gate (fno do pr stub-manifest reconcile-validate), de-stub + finalize + flip the EXISTING draft PR ready on authorize, or refuse (carveout + draft-held PR comment) on drift/missing-manifest. It never creates a new PR and never merges. Load references/reconcile-mode.md for the full contract before proceeding.
1-3f. Initialization
The full initialization sequence (load workspace config, codemap, project config, size profile, init state, detect input type, cross-project, Linear, plan validation, domain resolution, discovery gate, checkpoint, kill criteria) lives in references/init-state.md.
Quick summary:
-
HARD-GATE (location), resolved from policy, never asked: Before invoking
init-target-state.sh, consult the shared location verdict (the SAME one/execute,/fix, and the SessionStart heads-up use, so there is no per-skill drift):PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT:-${CODEX_PLUGIN_ROOT:-$(cat "$HOME/.fno/plugin-root" 2>/dev/null || git rev-parse --show-toplevel 2>/dev/null)}}" LOC_HELPER="$PLUGIN_ROOT/hooks/helpers/check-impl-location.sh" [[ -f "$LOC_HELPER" ]] && bash "$LOC_HELPER" || echo "verdict=ok"If the output carries
verdict=canonical-protected(you are on the canonical checkout's protected branch, where sibling terminals share.fno/): runfno do target start <node>and continue the pipeline from theworktree:path in its receipt. This holds for attended and unattended runs alike - there is nothing to ask./target <node>is itself the consent, worktree-first is principle 0, and.claude/rules/worktrees.mdis the single place that settles where the worktree goes.fno do target startis the documented one-verb cold start (worktree offorigin/main, no hand-rolledgit worktree add, no hardcoded base path) and is exactly what a canonical-protected verdict calls for; it never prompts. The helper emits no attendance signal and runs beforefno do target init(the only thing that resolves attendance), so a branch on attended-vs-unattended here has no machine input to branch on - resolve unconditionally instead of asking.- Refusal backstop:
init-target-state.shitself refuses a canonical-protected branch and prints the worktree /git checkout -b feature/<slug>/TARGET_LOCATION_OK=main-acknowledgedoptions. See backlog ab-efcde945.
- Refusal backstop:
-
MANDATORY: Bootstrap the session with
fno do target init --input "<original arg>"(add--plan-path <path>for plan inputs). This discoverable verb wraps the canonicalhooks/helpers/init-target-state.sh(withTARGET_START=1+TARGET_INPUT/TARGET_PLAN_PATH), records theowner_cwdworktree binding, and REFUSES to write a stub. Do NOT substitutefno do state init- it writes an empty stub the stop hook archives (and will redirect you here). Iffnois unavailable, runhooks/helpers/init-target-state.shdirectly withTARGET_START=1andTARGET_INPUTset. This is a DEGRADED path, and the degrade is not free - the wrapper does eight things the script does not do on its own, two of which the script now delegates back tofno, leaving six genuinely uncovered. All eight are listed so the cost is stated once where you decide to take it:Wrapper-only behavior Covered on the direct path config.review.reviewerscapability refusalyes - the script calls fno do target check-review-gateconfig.review.github_appsreachability refusalyes - same call TARGET_BEASTMODEscrub (never self-grant from an inherited env)no - set it explicitly, see below retro-triage dispatch preflight no blast-radius size modulation no node -> plan_pathback-fillno TARGET_UNATTENDEDderived fromFNO_AGENT_SELF/FNO_BGno --no-mergecarrierno The two review refusals are covered because the script shells back to
fnofor them; iffnois genuinely absent rather than merely unwrapped, it prints a note saying the capability gate was not checked and proceeds. That is the honest limit: the verdict tables are Python, so a trulyfno-free bootstrap cannot run them at all. On this path you MUST also setTARGET_BEASTMODEexplicitly -1when the invocation carried thebeastmode/beastmodifier, empty otherwise. The helper reads the bare env var and cannot tell an explicit grant from one inherited from an ancestor shell or a spawning parent;fno do target initscrubs that for you, and this path has no such scrub. -
fno do target initowns the node claim - do NOT claim it yourself. Init acquiresnode:<id>viafno agents claim(TTL-anchored to the durable session PID) and recordstarget_claim_key/holder/ttlin the manifest on success. Anote: legacy graph-claim skipped (non-fatal)line is EXPECTED and is not a failure - the authoritativefno agents claimruns right after it. Never runfno agents claim acquiremanually to "fix" it: a claim from a transient shell PID dies instantly and goesstale, clobbering init's good claim. To confirm ownership, runfno agents claim status node:<id>and check that the liveholderequals your own session_id (fno whoamiprints it). Do NOT trust thetarget_claim_*manifest fields for this: they are an init-time SNAPSHOT and can lie after the supervisor PID is respawned - the live lockfile holder is the only ownership truth (x-ba4b). Asuspectstate (TTL-unexpired but dead pid) still belongs to your session; it is never up for grabs. -
Join is the caller side of a wide plan, and init is where it fires. When the bound plan carries
join: auto, init measures the plan width and hands the remainder out. It reads the width withpython -m fno.backlog.join_trigger width, then runsfno backlog join <node>. When the plan carriesjoin: manual(the default), init fires nothing. The remaining waves then wait for a person or a/king-for-a-daysession to runfno backlog join <node>. Join refuses by exit code. 2 is no live node claim, 3 is width 1, 4 is no usable bound plan, 5 is already joined. A refusal is non-fatal and never blocks init. The joiner side of the same contract is the joiner posture in /execute waves. -
For every plan input, run
validate-plan.shagainst the resolved plan file or folder, including single-file quick plans. -
Resolve domain from CLI flag → plan → settings →
codedefault. -
For idea inputs, run the discovery gate before /blueprint.
-
Evaluate the plan's
kill_criteria:block every iteration (see references/kill-criteria.md).
3f-pm. Plan Mode Front Door (Mode 1, Claude Code only)
only if a Claude Code native Plan-Mode plan was just approved AND the run is attended: after init and before preflight, back-fill the approved native plan into an executable one. This whole step is a no-op on CLIs without the capture hook, in any unattended / headless run, and whenever no fresh sidecar exists - so /target behaves exactly as today there (US4). Full procedure (detection results, backfill adapter, synthesize-validate-confirm): references/plan-mode-frontdoor.md.
3g. Preflight Check (MANDATORY unless --skip-preflight)
After init-target-state.sh completes and before any pipeline phase fires, run the environment preflight:
if [[ "${TARGET_SKIP_PREFLIGHT:-0}" != "1" ]]; then
bash "${SKILL_DIR}/scripts/preflight/run-checks.sh" || {
PREFLIGHT_EXIT=$?
# Touch the cancel sentinel so the loop-check verb terminates with Interrupted.
touch .fno/.target-cancelled
echo ""
echo "preflight failed: one or more environment checks failed."
echo "Fix the issues listed above, then re-run /target."
echo "To skip preflight (not recommended): /target --skip-preflight \"...\""
echo "<promise>MISSION BLOCKED: preflight failure - fix environment checks then re-run</promise>"
exit 0
}
fi
Override: Pass --skip-preflight to bypass (not recommended - blocks surface real problems): /target --skip-preflight "my feature". Even when skipped, the skip is recorded in target-state.md so it's auditable.
What gets checked: See references/preflight-checks.md for the full check catalog (working tree clean, branch state, deps installed, auth valid, disk space, codemap freshness). Checks that produce warn or unknown do not block - only fail status blocks.
3h. Phase Handoff Artifacts (best-effort)
Flat single-file plans skip ph_write entirely - it is scaffolding a one-shot change never misses (G4). For a multi-phase run only, each phase may write a small structured artifact and read the prior phase's, so a transition has a clean handoff without reconstructing context from the full transcript. It is best-effort, never a gate: loop-check never reads these, so a missing artifact never blocks completion. The helper, per-phase write/read schema, prior-phase map, and concurrency note are in references/phase-handoff.md - load it only when the run spans phases.
4. Execute Pipeline
Capability escalation + cross-project routing. Context pressure is handled by compaction and never triggers a fresh session. Continue across blueprint/do and wave boundaries in this session. Only an external operator or supervising king may invoke the explicit capability-escalation transaction after selecting a stronger destination. Its proof and decision-line contract are in references/self-handoff.md. A legacy cross_project: true manifest still routes foreign work through spawn-into-project rather than a removed parallel pipeline.
PHASE EXECUTION PROTOCOL (MANDATORY)
After completing each phase:
- INVOKE the next skill immediately - do NOT stop between phases
The delivery pipeline runs in order (blueprint/plan -> do -> validate -> docs -> ship -> review -> external review). /think is a research verb outside this path (x-42c5). It never runs automatically as a pipeline phase. When deeper investigation is warranted, run it as a deliberate choice before /blueprint. The ship step requests the fno review lane on the final HEAD by default. Phases are not enforced by gates. Completion proof is the world itself: PR green and reviewed. The acceptance-criteria check that runs before /execute waves is documented in references/phase-transition-guards.md.
The phase-routing table, invocation logic, scratchpad writes (after think and after spec), confirmation check (for confirm: true skills), Linear status sync, and the validate-phase artifact write live in references/phase-invocations.md and references/scratchpad-writes.md.
Postcondition Checking
Per-phase postcondition verifiers were removed in the control-plane collapse (ab-d0337fbc). Postcondition checking collapsed into the external-truth done() reads performed by fno-agents loop-check when a <promise> is seen: PR exists for HEAD + CI green + reviewed. Run the review and validate phases thoroughly; the loop-check verb verifies the outcome against the world, not against state booleans.
Atomic Commit Discipline (NON-NEGOTIABLE for M/L)
For Medium and Large size profiles, each completed task or wave MUST produce an atomic commit before moving to the next. Do NOT accumulate all changes into a single kitchen-sink commit at the end of execution.
Rules:
- After completing each task in
fno:execute waves, create a commit scoped to that task's files with a conventional commit message referencing the task - If a wave has multiple sequential tasks, commit after each task
- If a wave has parallel tasks (subagent mode), each agent commits its own work
- Commit messages follow the project's
commit_stylefrom config.toml - Never
git add .orgit add -A- only stage files relevant to the task
Commit message format:
feat|fix|refactor(scope): what changed
Task {N.M}: {task title from plan}
Why this matters: Large undifferentiated diffs are hard to review, hard to revert, and break git bisect. Atomic commits per task make the review phase more effective and let the user revert individual tasks without losing everything.
For Small (-S) size: Atomic commits are encouraged but not enforced. A single feature may only have one logical commit, which is fine.
FORBIDDEN: Deferring all commits to the end of execution. If you reach the review phase with a single commit covering multiple tasks, that is a failure of commit discipline.
Phase Bodies
The clean phase (3.5), review phase (4) deferred-gate semantics, and direction-alignment check (every 2 phases) all live in references/phase-bodies.md.
Failure Recovery
When validation fails or the same error fires repeatedly, target has structured recovery: validation-failure recovery (rollback to checkpoint after 3 same-phase failures), circuit breaker (rotate approach after 3 same-error failures), and standard error responses for /execute waves and review failures. See references/failure-recovery.md.
Secondary Repo Inline Commit
When a plan task touches a secondary repo (e.g., a frontend plan with a backend migration), branch+commit+PR inline before returning to the main repo. See references/secondary-repo-commit.md. For >3 files or meaningful parallel work, model the other repo as its own backlog node (linked by blocked_by) and let spawn-into-project dispatch it.
Auto-Merge Mechanics
If auto_merge_approved: true in target-state.md, Phase 6a runs rebase-resolve.sh before /pr create, and Phase 8a runs pr-merge.sh after external_review_passed succeeds. See references/auto-merge-mechanics.md and the cross-skill protocol in references/auto-merge.md.
5. Log Metrics
Append feature-level metrics to .fno/ledger.json. Per-wave cost estimates use scripts/metrics/cost-tracker.sh. See references/settings.md.
6. Completion (Pre-Promise Sequence)
The full pre-promise sequence is mandatory and must run in order: calculate session cost → handoff artifact → completion summary → task registry update → plan stamp → plan graduate → cross-project ship recap → promise output. See references/pre-promise.md.
Record any decision this run made that a later session would ask about. fno backlog decide <node|pr-N|area> "<what>" --rationale "<why>", one call per decision; fno backlog decisions <same> reads it back. A ruling you took in the ship phase (what to defer, which of two designs won, why a finding was waived) lives only in this transcript, and the next session gets a compacted copy or none. Full contract: docs/architecture/decision-record.md in the repo root.
The cross-project completion-gate variant (all projects must have status COMPLETE + pr_url + pr_number) and the post-promise behavior contract (STOP IMMEDIATELY, no AskUserQuestion, ignore late notifications) are in the same reference.
State Files
| File | Purpose | Owner |
|---|---|---|
.fno/target-state.md |
Immutable session manifest (written once by fno do target init) |
target |
.fno/STATE.md |
Wave/task progress | /execute waves |
.fno/SUMMARY.md |
Task completion notes | archer |
.fno/ledger.json |
Feature metrics | target |
.fno/events.jsonl |
Loop-check events (loop_check, termination, etc.) | fno-agents loop-check |
Model Fallback (Interactive)
If the API returns a rate-limit or overload error during execution, present the user with wait/switch/pause options via AskUserQuestion. See references/model-fallback.md.
Resume
/target resume # Continue from target-state.md
Reads state, skips completed steps, continues from last position. After every resume, re-read project.vision and project.goals from config.toml — context lost during compaction is reconstructed there. See references/resume.md.
Settings
Configuration lives in .fno/config.toml (project-local) with ~/.fno/config.toml as global fallback. The full schema (project topology + execution defaults + worktree config + autonomous defaults) is in references/settings.md.
References
Loaded by state — the "read X when Y" load conditions are inline above; this is the index.
- references/beastmode-authority.md - Walk-away authority grant (load when
authority: full) - references/bg-and-batched-modes.md -
bgdispatch-and-continue (unsupervised, still observable) +batchedmember runs - references/plan-mode-frontdoor.md - Attended Claude Plan-Mode backfill front door
- references/plan-mode-backfill.md - Backfill adapter mechanics (deeper contract)
- references/ship-and-promise.md - Draining reviews + local review gates before
<promise> - references/self-handoff.md - Explicit capability escalation + retired cross-project routing
- references/phase-handoff.md - Best-effort per-phase handoff artifacts (multi-phase only)
- references/pipeline-and-philosophy.md - Full phase map + compose-don't-hardcode rationale
- references/completion-model.md - Completion internals: manifest, stop-hook shim, loop-check verb, done() reads, backstop, TerminationReason
- references/state-schema.md - Immutable manifest field list and write-once rule
- references/size-profiles.md - Size capability matrix
- references/flag-migration.md - Override-flag list
- references/usage-detail.md - Interactive wizard, execution modes, context lifecycle, model optimization
- references/init-state.md - Steps 1-3f initialization sequence
- references/boundary-reconcile.md - STALE boundary reconcile procedure
- references/reconcile-mode.md -
--reconcilede-stub pass contract - references/phase-transition-guards.md - Acceptance criteria gate before /execute waves
- references/phase-invocations.md - Phase routing + invocation logic + Linear sync + validate artifact
- references/scratchpad-writes.md - Cross-phase state files (think findings, plan summary)
- references/phase-bodies.md - Clean, review, direction-alignment phases
- references/failure-recovery.md - Validation-failure recovery + circuit breaker + error responses
- references/secondary-repo-commit.md - Inline secondary-repo commit pattern
- references/auto-merge-mechanics.md - Phase 6a + 8a + resolution chain
- references/pre-promise.md - Full pre-promise sequence + promise output
- references/model-fallback.md - Rate-limit handling
- references/resume.md - Resume protocol + project vision re-read
- references/settings.md - config.toml schema + state files + cost tracking
- references/multi-plan.md - Multi-plan worktree mode
- references/domain-profiles.md - Domain phase resolution
- references/kill-criteria.md - Kill-criteria predicate syntax
- references/ship-phase.md - Ship-phase exit-42 dispatch loop
- references/iteration-loop.md - Bounded iteration protocol
- references/preflight-checks.md - Preflight check catalog
- references/cli-tool-mapping.md - Multi-CLI tool equivalents
- references/gate-artifacts.md - SUPERSEDED: see docs/architecture/control-plane-loop.md
- references/phase-verifiers.md - SUPERSEDED: see docs/architecture/control-plane-loop.md
docs/architecture/control-plane-loop.md(repo root) - Post-wedge stop hook architecture: shim + loop-check verb + immutable manifest
Known Limitations and Deferred Work
- Sandbox enforcement can fail open on some hosts. See LIMITATIONS.md.