| name | start-mission |
| description | Use this skill to make a change to an SDD project — triggered by a general change request like "add a start-mission skill to sdd", "implement the auth capability", "revise the gateway spec", or "work on <github issue url>". Opens a change request against the durable project spec and runs the mission loop (explore → deliver → handoff). |
start-mission
The single user-facing entry for changing an SDD project. It opens a change request (CR) against the one durable project spec (.agents/specs/<project>/) and runs the mission loop over it. The session that runs this skill is the conductor — the user in the driver's seat, holding the user channel, grilling live, ratifying in-session. This skill is the in-session realization of the conductor role (the automaton agent is the headless realization for an unattended scheduler or a multi-CR fan-out).
Three realizations of the conductor. In-session interactive (default — the grill loop below); in-session plan-mode preview (Step 2 runs the preview branch — reasoning only, renders the drafted spec + suite into the plan file, ends at ExitPlanMode); headless automaton (unattended). The plan-mode branch is detected in-body from the harness plan-mode signal (only the plan file is writable) — never from the description, so a mission triggers on change-intent alone (plan mode or not) and never re-fires per turn: the branch is a fork inside an already-loaded explore phase, not a trigger.
A CR is the unit of change-intent (git-PR-shaped); the mission loop is the workflow that carries it. Whether the CR adds a capability, revises behavior, or reconciles overlap is decided during explore — not by a separate entry skill. A request with no suite-relevant behavior is not a CR and escapes the lifecycle (leave no SDD record).
Advise a capable model (e.g. Opus) on entry. The explore grill runs in this session, so its quality tracks the session model. Surface this before the grill so the user can switch if needed. (The harness cannot switch the session model on your behalf.)
Load sdd:lifecycle-governance (status enum, the freeze re-open transition), sdd:ownership-governance (who writes each field), sdd:spec-format-governance + sdd:suite-format-governance (the node skeleton + suite bars), sdd:spec-producer-governance (the grilling procedure run inline), sdd:impl-producer-governance (what the spawned builder loads), sdd:gate-validation-governance (legal gate-state tuples), sdd:remediation-governance (how a producer answers a change verdict at either gate), and sdd:combat-log-governance (the provenance shapes). The autonomy bar is baked in (below).
Step 1 — intake: open the CR and scaffold the plan
Get the CR into the system and create its plan brief — the plan is a step-1 artifact, not something explore invents later.
- Recover the request. From a general change prompt, name the change and the target. From a source URL (
work on <github issue url>), fetch the issue and read it as the CR body.
- Locate the project spec by running the
discover-specs skill (the corpus/discovery engine) — it returns the TOON list of every project spec at the SDD spec locations (the three fixed conventions plus any declared extra anchors, ADR-0019) with its status and project-path. Resolve the CR's target to one spec by folder slug or project-path; an ambiguous match is disambiguated with the user, never guessed. One project is one spec — there is no spec fleet to pick from. If discover-specs finds no spec for the target project, that is the backfill path (load scaffold-project-spec).
- Scaffold
.agents/plans/<cr-ref>-<what>.plan.md — name the brief with a short kebab <what> slug naming what the CR does, even when the ref comes from an external source (github-34-rejudge-sweep.plan.md, not github-34.plan.md). The source name (github) is optional — most projects draw from one external source, so a bare 34-rejudge-sweep.plan.md is fine; keep the source prefix only when a project mixes sources. Fill from a basic template: frontmatter todos (ordered, status: pending, each content a short summary < 120 chars) + a ## NEXT anchor + the CR link. Keep the plan body concise — it is read by the agent each resume, so phrase for an agent first while staying human-legible (terse lead, no prose padding). This is the portable handoff brief pause-mission / resume-mission operate on. Safe-to-publish floor (the same one the combat log carries in combat-log-governance): the brief is tracked and committed, so it references only repo-relative paths — never an absolute path, $HOME/$USER, an OS username, or any machine-local location outside the repo. When the CR's design came from a plan-mode doc under a machine-local path (e.g. ~/.claude/plans/…), bring that content the repo (a sibling beside the brief) and reference it repo-relative — do not link the external absolute path. The engine enforces this mechanically. (a drafted spec + scenario list from a prior run), the explore phase (see Step 2) rather than re-grilling from scratch.
Step 2 — explore: grill the spec + suite, build to learn
Run authoring in-session as the conductor. Explore builds the implementation to learn (build-to-learn) — implementation is not deferred to deliver; the freeze is the boundary. The phase ends at the spec gate.
Mission statusline (opt-in). On entering explore, overwrite .agents/sdd/statusline with explore — a single-line, best-effort write; a missing file / no init-wired reader is not an error, just skip it. This is the runtime status value, distinct from the lifecycle status frontmatter field, and is written only while a mission is in flight (never at rest, no heartbeat). Skip this write during the Plan-mode preview branch below — it writes no repo files at all.
If plan mode is active, run the ### Plan-mode preview (end of this step) instead of the live grill loop — same reasoning, no repo writes, no build-to-learn spikes; the phase ends at ExitPlanMode, not the spec gate.
Resolution first. Run resolve-governances over only the project registry .agents/universal-plugin.json (never scan plugin dirs), passing the current project's anchors (--project, plus --project-root in a monorepo — you know the project from discover-specs / context). For each touched file's artifact-type it names each production-chain role's agent (a plugin delegate or the SDD default) plus the resolved-actor bar candidates bucketed by tier (project / project-root / plugin / sdd). It does not compose — load each candidate and compose them yourself by precedence sdd-default < plugin < project-root < project (most-specific wins on conflict; a governance's own compose: replace supersedes its bar's lower-precedence candidates); the fixed-universal governances are loaded from the role/agent definition (the matcher does not emit them) — their names up front as a compact digest, each body lazily only at the gate/decision that invokes it (gate-validation/lifecycle at a gate, suite-format when authoring a .feature; sdd governance-resolution), so a one-line change never reads all six. A required role with no real delegate fails closed. A resolved delegate that recuses from a subject (produces nothing, declaring it outside its domain — e.g. a plugin bound by artifact-type meets a subject its lens does not fit) is not a fail-closed: re-resolve that one unit's chain to the SDD defaults (default producer + SDD-default bars + judge) and proceed, recording the recusal as a combat-log line (never a halt); other units keep their squad (sdd:lifecycle-governance). A domain claimed by two plugins → ask (answered live in-session).
For each unit the CR touches:
- Locate or place the node — provisionally. If a
spec.md / README.md already exists at the target → this is a revise (no scaffolding). Otherwise scaffold a new node and drop it in a plausible home under the layout the project declared in its root spec.md placement map — capability-first groups by what the project does, mirror-source mirrors the source tree. Placement is judged within that declaration, never against a preferred one (sdd:spec-structure-governance, "strategy is policy, homes are data"); where no strategy is declared, the capability-first default applies. A layered / framework-first top level stays discouraged under every strategy (it scatters a capability across folders, breaking node↔folder and degrading scheduling). Consult project-spec/place-node (--concept → candidate homes; --name → "belongs near X" duplicate-catch) and the placement-map routing table (root spec.md) for contested overlaps, but do not agonize: placement is provisional and finalized cheaply at handoff (step 4), where a scoped Warden pass relocates it to its blessed home in the same change (a pure rename — freeze survives, sdd:lifecycle-governance). If the user named no capability, propose a capability folder from the CR and confirm.
- Classify the node (declared, never inferred):
spec-type: behavioral (a testable unit → ## Use Cases + a <unit>.feature), reference (a shipped non-testable artifact → ## Subject, no .feature), or descriptive (an index → no marker). Tag the node's cross-cutting concept: (the concern it serves — e.g. lifecycle / resolution; a string or list, orthogonal to spec-type; it feeds project-spec/concept-index). Also classify each touched file's artifact-type (the squad key — resolved per file, not stored): by convention first (skill under skills/, under , …; the extension never decides). On a genuine , consult and record the tiebreaker map and ( model).
The grill loop (the user loop). You are the conductor. Run the spec-producer inline (load sdd:spec-producer-governance, or persona-load a plugin specialist for the artifact-types), dispatch the cold spec-judge each round — through the dispatch capability's intent seam when one is available (preferring its warm unit, context-cleared fresh via npx cyberlegion@0.3.1 unit clear <ref> before each round's judgment), else a portable cold subagent — and for build-to-learn dispatch the impl-producer builder the same way (its warm unit keeps its context across spikes; no reset) in explore mode against the non-frozen suite — spikes are thrown away; their learnings feed the live grill to steer the spec + suite. Set an iteration cap (default 3; honor a user-named cap), then loop:
Governance provenance relay. When you dispatch the cold spec-judge, forward the inline spec-producer's declared governances_loaded (sdd:spec-producer-governance) verbatim through the same dispatch channel, keyed producer_governances_declared — a brief field when the judge is a cold subagent, a mail envelope field when it runs through an agent pool. Forward it as-is, including an empty set — you render no opinion on which governances were actually required; that check is the spec-judge's own pre-flight (sdd:sdd-spec-judge).
- Grill the user live with the node path,
artifact-types, and the seed intent (or backfill / revise); write the draft spec.md + .feature.
- Spawn the cold spec-judge; incorporate its verdict and any
<!-- open: --> markers.
- On convergence → exit to the spec gate.
- On
blocked, or the cap hit without converging → do not auto-accept. Present the failing scenarios and ask the user to accept as-is, keep looping (reset the count), or change direction.
Freeze re-open guard. A node at status: approved or implemented has a frozen .feature. The unfreeze trigger is risk, not phase (sdd:lifecycle-governance), so what you may do to that file depends on the edit class, not on it being frozen — read the edit class structurally, per named Scenario, never from a raw line diff (a step orphaned off a frozen scenario onto a new adjacent scenario shows no - line and would misread as additive): npx gherkin-cli@0.0.2 diff --base <baseref> <file> --format json (addOnly / only added ⇒ additive; any modified/removed ⇒ examine for narrowing), or the spec-gate's plugins/sdd/skills/spec-gate/scripts/classify-edit-class.mts which wraps this same structural diff plus git rename detection. An additive scenario (new behavior, nothing weakened) self-clears — it stays @frozen, needs no re-open; a pure move/rename (git mv, zero content delta) likewise preserves freeze and is not a gate-able edit. Only a narrowing or rewriting edit to an existing scenario is a re-open — a freeze transition and a status write you do not own, so confirm it was ratified (the lightweight async re-open flag) before touching that scenario's content. Never narrow or rewrite a frozen scenario without the ratified re-open; adding and relocating need none.
Route observations. Each production-chain producer (spec-, impl-, solution-producer) may surface typed OBSERVATIONS (architect | strategist); never act on them silently. When several producers surface observations in one segment, forward every producer's observations to the plan — drop or filter none of them — and spawn no spec of your own from them. A granularity / split observation becomes a new node or a project-spec operation (the plan's or the user's call) — never a marker grown into this node, and never a spawn you make here. An observation the plan or user then declines is dropped by that decision — an explicit call, not silent loss.
Plan-mode preview
Run only when plan mode is active (the harness blocks every write except the plan file). Run the explore reasoning in full but write no repo files — the plan file is the single output.
- Reason as normal.
resolve-governances; locate/place the node provisionally in-memory (no scaffold write); classify it (spec-type, concept:, per-file artifact-type); collect seed intent with the same 3–5 targeted questions (backfill reads source instead).
- Draft, don't write. Compose the
spec.md prose and the <unit>.feature scenario list per sdd:spec-format-governance / sdd:suite-format-governance, but render them into the plan file under ## Proposed Spec and ## Proposed Scenarios (scenario titles + Given/When/Then, grouped by # ── <stage> ──, @rubric/@trigger tags noted) — never to their repo paths.
- Keep the cold spec-judge (read-only): spawn it over the in-memory draft as embedded in the plan; fold its verdict and any
<!-- open: --> markers into the preview so unresolved gaps are visible.
- Drop the build-to-learn spikes. Do not spawn the impl-producer — spikes mutate files / run builds (disallowed in plan mode) and are out of scope; note in the preview that the draft is un-spiked.
- No gate, no writes. No spec gate, no
@frozen, no status / approval / ledger writes. End the turn with ExitPlanMode presenting the drafted spec + scenario list.
- Adoption on approval. On approve + exit plan mode, the next non-plan-mode explore run adopts this preview as the settled draft (via the intake
<cr-ref>.design.md seam, Step 1): it writes spec.md + <unit>.feature from the preview, runs build-to-learn to validate, and proceeds to the spec gate without re-grilling seed intent. Guard: if the preview carried a failing spec-judge verdict or unresolved open markers, resolve those first — never blind-adopt a known-incomplete draft.
Step 2 gate — the spec gate (Draft → Approved, internal)
On entering the gate, overwrite the statusline file with spec gate (same opt-in, best-effort write as explore — skip when plan mode escaped this step via ExitPlanMode).
Run the spec gate as an internal step (not a user-invocable skill). Judge each touched unit suite against sdd:suite-format-governance (untagged scenarios boolean; @rubric well-formed) and the spec-format bars; load sdd:lifecycle-governance + sdd:ownership-governance + sdd:gate-validation-governance for the legal state tuple. Never advance with judge failures, open markers, or a suite that does not cover the spec. On a change verdict the findings are evidence, not a work order: substantiate each before acting, state the rule each instantiates and sweep for its other instances, re-derive every correction against the rule governing the artifact rather than against the finding alone, and account for each finding's provenance — a finding naming an artifact the previous round's commits changed is a regression, which stops the loop for a re-plan instead of another round (sdd:remediation-governance). ("Regression" here is finding provenance, distinct from the grill loop's convergence above.) On approve: freeze each touched .feature via its @frozen tag, record a per-CR gate line in your own shard in the ledger/ directory sibling to spec.md, and set status: approved. spec.md stays in sync, never frozen.
Step 3 — deliver: build to keep
On entering deliver, overwrite the statusline file with deliver.
Build-to-keep against the frozen suite. The deliver read-set is scoped (sdd deliver spec): the frozen <unit>.feature (the contract), the optional <unit>.solution.md, and the implementation files for the touched artifact-type (via produced-by / resolve-governances) — not the prose unit spec, which was explore's input and adds no constraint the frozen suite doesn't already carry. Dispatch the impl-producer builder (it loads sdd:impl-producer-governance; a named plugin / model-tuned producer runs at its own model + effort) — through the dispatch capability's intent seam when available, preferring the same warm builder unit reused from explore (it keeps its context; no reset) over a cold one-shot, else a portable cold subagent — to build the artifact and one verification per frozen scenario.
Rebase onto the target — the last deliver act, before the gate. Before running the impl gate, rebase the CR branch onto the current tip of the declared target (for a commit-to-main project, the equivalent pull --rebase onto the latest main), so the impl gate judges the merged tree that will actually land — keeping history linear and leaving handoff a pure consumer that never re-verifies. A textual conflict is resolved as deliver code work against the frozen .feature (never a .feature edit); the gate then runs on the resolved tree. A conflict you cannot resolve confidently is never guess-resolved — the frozen suite covers this CR's behavior, not the incoming change's, so a wrong resolution could still pass the gate and land broken; stop and escalate (in-session ask the user; headless return needs-input up the relay) and record a halt, never land a low-confidence resolution. Rebasing an unmerged CR branch is git-reversible (reflog), so it raises no new hard floor — but a conflict resolution that would narrow a frozen scenario still fires the existing Clearance floor, a semver class over the ceiling Compatibility, and a genuine contradiction Conflict (autonomy bar, below). The rebase-then-gate is optimistic: if the target advances again between the passing gate and the push (another CR merged in the window), re-rebase onto the new tip and re-run the impl gate — do not push until the gate passes on the re-rebased tree, looping until the push wins, so what lands is always a tree the gate saw green. The loop is bounded, not forced — if the target keeps advancing past a small cap of attempts, stop and escalate (record a halt) rather than spinning forever (a liveness stop, same as the unconfident-conflict halt).
The impl gate (Approved → Implemented, internal). On entering the gate, overwrite the statusline file with impl gate. Dispatch the cold impl-judge (sdd:sdd-impl-judge or the covering plugin's judge) — same seam-when-available wiring, preferring its warm unit context-cleared fresh via npx cyberlegion@0.3.1 unit clear <ref> for this judgment, else a portable cold subagent — to run the verification per frozen scenario plus an orthogonal structural/scope read. Advance to status: implemented only when every impl-judge passes (a frozen scenario with no verification blocks the advance — impl-sync is this suite run, not a stored flag). The three actions: approve → implemented; change → fix the code (never the frozen .feature), under the same evidence-not-a-work-order remediation the spec gate uses (sdd:remediation-governance) — including the provenance account that stops a regressing loop; reject → redo, or a Oracle-lens revert (a frozen scenario proved fatal → unfreeze the .feature, return to draft — the only place a frozen .feature reopens).
Step 4 — handoff
On entering handoff, overwrite the statusline file with handoff. Clear the statusline file (delete .agents/sdd/statusline, not just blank it) once the mission lands — a clean handoff is one of the loop's exit paths, and the file is written only while a mission is in flight.
Land per the handoff unit. First finalize placement: run a Warden placement pass scoped to this mission's touched nodes, and relocate any provisionally-placed node to its blessed home (placement-map routing table) via git mv — a pure rename that preserves freeze (sdd:lifecycle-governance), logged as a detail-adjustment, so the delivery shows every node already in the right place (no follow-up formation CR). Then land per the declared delivery shape (branch → PR where the repo is PR-flow), decomposed by unit of work (one co-committable change per commit), conditional status write-back — when the CR's source closes by reference (a same-forge issue, e.g. GitHub/GitLab), write the auto-close reference (Closes #<n>, naming the source) into the PR body so the source auto-closes on merge; a source that does not close by reference (a bare prompt, or a cross-system source like Asana/Jira) gets no closing reference (direct-to-main work transitions it to done on push; a cross-system source is moved natively) — and a distilled public summary. Introduce no new hard floor; keep the combat log in the PR; keep the plan until the CR is done/merged and doctrine-distilled.
Follow-ups: record, classify, propose, drain — only the first always works. A follow-up (work the mission noticed but held out of scope) is carried through four stages. 1. Record — unconditional, first. Before anything else, and before any filing is attempted, append each identified follow-up as a kind: followup line to the CR's own ledger shard (sdd:combat-log-governance) — no permission, no forge, no human, so it cannot be denied; it goes to the ledger, never the combat log (the combat log is deleted from the tree at retro; the record must outlive the mission). 2. Classify — a proposal, not a verdict. Mark it blocking (it contradicts a completion claim the mission already made — name that claim in the line) or backlog (genuinely new territory); a finding that the mission's own frozen contract was wrong is not a follow-up at all — route it as an Oracle-lens revert inside this mission instead. 3. Propose, never admit. Emit the classified proposal plus its evidence; write no node or edge to the mission graph and spawn no mission for it — admission is the graph's single writer's act, out of scope here; filing an issue is not opening a CR, and a filed follow-up re-enters SDD only when a later mission is started from it. 4. Drain — permission-gated, class-agnostic. File one issue per outstanding follow-up, blocking and backlog alike (the class decides graph admission, never filing) — dedupe first against the forge's existing issues, open or closed (at least two keyword combinations: the full title, then the core noun/verb) — and on a mixed set file only the unmatched, skipping the matched, never all-or-nothing. Forge-conditional: a source with no issue forge files none; the records still stand. The followup line carries no filed-state — never edit it to mark it filed — so a later drain re-derives what is outstanding by that same dedupe, which is what makes a retry both correct and idempotent (a follow-up whose filed issue was since closed is a closed match, so it is skipped, not re-filed).
The denial path is first-class. Filing can be refused (an unattended mission has no channel to grant it). When refused: the ledger records stand, you report the refusal loudly, and you never report the follow-ups as filed — a fallback indistinguishable from success is the exact failure this doctrine exists to avoid. The drain retries later from the durable record, filing the still-outstanding follow-ups once permission is granted.
The issue body meets a stricter outward-publish floor than the committed record. Compose it self-contained — a reader who cannot see the mission's internal artifacts can act on it, with no "see the ledger line" and no gate/judge/leash prose — carrying no production-internal artifact reference: no ledger shard filename (it embeds a per-session hash), no combat-log reference, no plan-brief path — even though a repo-relative ledger shard filename passes the committed-record floor cleanly; this bar is stricter and excludes it anyway. Plus everything the committed-record floor already bans (absolute paths, $HOME/$USER, usernames, hostnames, secrets, code, raw numbers). Every filed follow-up also carries a marker identifying it as agent-filed and names the mission (<cr-ref>) it was discovered from, so intake can tell agent- from human-filed follow-ups and the loop's branching factor is measurable.
Before you close out, run the correction-line finalize backstop (autonomy bar, below): flush any correction whose combat-log line was never written, creating the plan's *.log.jsonl if absent.
Also run the plan-brief finalize backstop (autonomy bar, below): reconcile the plan brief's todos and its ## NEXT anchor to the landed state, in this same change — so the delivery never ships a landed mission described as in-progress.
Before closing out, reset the mission's warm units: npx cyberlegion@0.3.1 unit clear <ref> (context-clear, pane stays warm) or tear down every warm unit this mission dispatched — none carries this mission's context into the next.
Once landed, do not spawn the formation Warden. Surface a one-line nudge that a corpus-wide formation pass is due, pointing to sdd:manage ("audit the corpus structure" → formation-loop). The pass is on-demand — run deliberately, not auto-spawned on every landing; sdd:manage owns the trigger. Gate nothing on it.
Autonomy, provenance, and the hard floor (baked in)
- Dispatch transport. Every spawn beyond this session states a dispatch intent — role, brief, expected verdict schema — never a pinned command. When a harness-agnostic dispatch capability is available (detected at runtime; the concrete case is the Legate's
dispatch-governance composing cyberlegion primitives — agent resolve + unit spawn + mail await — with no dispatch CLI verb, the seam named in the SDD project spec's design/harness-spawning node, repo-only), route through its intent seam and let it pick subagent | channel | run-inline, preferring a warm unit over a cold one-shot spawn; with no capability present, fall back to the portable cold subagent (depth-1) default — grader independence intact either way. Warmth is a property of the unit/process; coldness of the context: a judge's fresh-context guarantee (ADR-0016) is transport-agnostic — satisfied by a newly spawned cold subagent or a warm unit context-cleared to a fresh context before each judgment (re-deriving its oracle, carrying none of a prior round's context). Clear a warm unit with npx cyberlegion@0.3.1 unit clear <ref> (<ref> = unit id / handle / worktree branch or CR ref) — it injects the harness's own fresh-context command (/clear on Claude/Codex/Copilot, /new-chat on Cursor; fail-loud on a harness with no honest reset) so the pane stays warm while the context goes cold; it tears nothing down. The impl-producer builder instead stays warm and keeps its context across the explore spikes and the deliver build (never cleared between those uses). Warm units stay warm for one mission — reused within it, then unit clear'd or torn down at handoff, never carrying this mission's context into the next.
- Initial strategy (run start): assess blast radius + the other dimensions and emit a run-level
kind: leash block to your own ledger shard (ledger/<cr-ref>.<hash>.jsonl — mint <hash> as 6 random hex once per session and reuse it for every line you append; sdd:combat-log-governance) — leash (), , . It may be user-specified. This block is , — is the doctrine Scanner's alone. Ledger lines carry .
Suspend and resume
A mission runs as segments (one autonomous sitting each). Position is derived from the artifacts (spec.md, the .feature, frontmatter, the plan), never a stored cursor. To checkpoint a mission into its plan, use pause-mission; to pick one up, resume-mission reads the .plan.md and continues this loop where it left off.
Clear the statusline file on pause too. A pause is an exit path like handoff — before (or as part of) invoking pause-mission, delete .agents/sdd/statusline. This is your write (the conductor's), not pause-mission's — its write scope stays boundaried to the plan brief (todos, ## NEXT, and, with --approve, status); it never touches the statusline file.