Spine-first + fan-out: the two defaults for any new unit of work
These are defaults, not ceremonies: they fire for every new feature, leaf,
verb, demo, or process change unless explicitly waived — the same way “default:
ship” and “proof by default” fire in AGENTS.md.
Default 1 — the minimal working end-to-end spine ships first
In the same session the work starts, ship the smallest runnable path that exercises the real seam end to end — as minimal and as working as possible:
- User-facing surface → meet the LCD demo bar
(
docs/run-the-demos.md): one command, deterministic, no key, no network, no GPU, with a-selfcheckinvariant. - Library leaf / verb → a test that drives the real object plus one
captured live run of the verb (a
--json/--dry-runinvocation is fine). - Process / doctrine change → the machinery that makes it a default (a gate, a skill, a verb), not a paragraph asking agents to remember it.
If a working spine is not achievable this session with high confidence, the
spine itself becomes the first issue — filed gen/now, milestoned at
creation, with the missing witness named. The spine is never silently deferred:
either it exists as a witness, or it exists as a tracked issue. fak issue
fanout enforces this mechanically — it refuses to plan without a
--spine witness.
Why: a spine is a witness; a plan is a claim. The repo refuses unproven claims
(not yet discipline) — the spine is what converts “we will” into “it does”.
It is also the cheapest moment to discover the design is wrong.
Default 2 — the follow-on backlog is filed at creation time (3..50+)
The moment a spine ships, fan out its hardening backlog while context is hot — not “later”, which on a shared trunk means never:
fak issue fanout --title "my feature" --leaf myleaf \
--spine <commit-sha | demo command | doc path> [--parent '#<epic>'] --json
The planner (internal/issuefanout)
expands a fixed 15-template taxonomy — qa (edge sweep, failure paths,
determinism/race), dogfood (self-run on the repo’s own work, usage ledger),
product (CLI reference, LCD demo, error-message UX), observability
(outcome counters, scorecard fold), integration (advisory guard gate,
dos.toml wiring, super-loop hookup), docs (doctrine + doc-map linkage), and
release (CLAIMS.md tag + note) — into candidates that each carry the full
issuecontract scope contract
(working spine, done condition, witness, acceptance gate, closure binding,
route, step budget). Every candidate is dispatchable the moment it is
filed, proven by go test ./internal/issuefanout.
- 3 is the floor, not the target (
issuefanout.MinFanout); the full taxonomy is 15, and a large feature composes several fan-outs (one per leaf). - File with
gh— milestone + labels at creation (the issue-hygiene default) — or wave-plan first:fak issue fanout ... --json > plan.json && fak issue cohort --from-plan plan.jsongives concurrency-safe, leased waves. - Dedupe before filing: the candidates carry
fanout-<leaf>-*marker keys, so a rerun is detectable against existing issues.
How this binds into the machinery (shipped vs planned)
| Seam | State |
|---|---|
fak issue fanout planner + verb (internal/issuefanout) |
shipped 5b8f0bd1 |
Spine-first as an issue property (issuecontract.WorkingSpine required + spine-priority scoring) |
shipped (pre-existing) |
Wave planning over the fan-out (fak issue cohort --from-plan) |
shipped (pre-existing) |
/spine-fanout skill (agent front door) |
shipped (.claude/skills/spine-fanout/) |
Advisory pre-commit nudge (PRIOR_ART pattern, internal/hooks) when a new leaf ships spine-less |
planned — #2521 |
dos.toml lane for issuefanout + advisory reason token |
planned — #2522 |
| Super-loop / dispatch hookup (fan-out fires automatically at spine-ship) | planned — #2523 |
fak new-leaf scaffold prompting the spine + fan-out |
planned — #2530 |
| Adoption scorecard (spines shipped vs fan-outs filed) | planned — #2532 |
This doctrine was dogfooded on itself: the planner above is the minimal
working spine of the concept, its own fan-out was generated by the verb, and
the “planned” rows above were filed from that output as epic #2510’s
children (#2511–#2532). Each planned row is dispatchable by its issue number;
pick one up with fak issue contract --issue <n>.