← All docs

Creating a story — the path from draft to done

A story is a unit of work. In satelle it travels a gated lifecycle: each edge is judged by an isolated reviewer before it is enacted, so quality is managed at the boundary rather than self-asserted by the executor.

1. Draft and create

CLI:

satelle story create \
  --title "Ship the thing" \
  --body "What done looks like / the outcome sought" \
  --acceptance "1. first testable criterion
2. second testable criterion" \
  --priority high --tags mvp,web

A well-formed draft needs three things (the required structure):

  1. a specific title (names the change, not just a noun),
  2. a body stating the goal / what done looks like, and
  3. numbered, testable acceptance criteria.

satelle init seeds [review] gate_create = true (opt out with false). Creation always runs the deterministic structure check (title, goal body, numbered ACs, non-empty category). When the active workflow declares create_review (the embedded default is satelle-story-create-review), an isolated reviewer also judges content/alignment and classification against [[satelle-story-classification]] — e.g. reject an epic draft filed as category: feature (use epic-parent). A reject pushes back with notes; nothing is persisted until the draft is sound. With gate_create = false, only the structure of an unguarded create path remains — the standard is the same, the enforcement is not.

2. Begin work (backlog → in_progress)

Move the story into work:

satelle story set <id> --status in_progress

On the seeded default workflow this edge carries one CODED gate: the estimate/actual check rejects begin-work until a plan estimate is recorded (satelle story estimate <id> --time <dur> --tokens <n>). A repo that authors richer gates (e.g. satelle-story-intent-review, judging the story is well-formed enough to start) adds them to its workflow; a reject keeps the story in backlog with notes on what to clarify.

On first entry into a performing/engaging state (e.g. backlog → plan or backlog → in_progress), satelle records an engagement baseline ledger row (engagement_baseline) with the current git HEAD (and whether the worktree was dirty). Re-entry after park/blocked does not overwrite it. Gates that judge slice scope consume the baseline via enumeration only:

satelle story diff <id>           # changed files + diffstat since baseline
satelle story diff <id> --patch   # plus full unified diff

The command never decides pass/fail — it only lists. A story with no baseline (never engaged, or created before this feature) errors clearly. Gate authors invoke it from functional checks or reviewer prompts (Bash(satelle:*)). The embedded satelle-story-scope-review gate (implementation exit / close) consumes this enumeration to reject bundled sibling work.

A repo may render --patch compact (noise/index-line stripped, see satelle help compact-output) — --json always gets the plain form.

3. Reach done through the workflow's gates

The exact path to done is whatever the active workflow declares — done is always the terminal state, and every gate on the path runs before it (see satelle help reviewer-checks). The seeded default project workflow is the most basic lifecycle — it closes directly (in_progress → done) with no LLM reviewers; only the coded estimate/actual check (the actual cost must be recorded before the close) and the per-transition step summary run. Reviewer gates like satelle-story-done-review — an isolated, read-only reviewer that reads the repository and works through the numbered acceptance criteria one by one — are authored substrate a repo layers into its own workflow (the parent workflow and this repo's workflow both declare it).

A repo may layer extra steps onto the path before done — this repo's workflow adds one in-loop release step: the executor bumps the version, commits the slice, pushes to main, and — rather than block watching CI — refreshes the service during the CI window and records the test + version-gated release run URLs, their conclusions, and the published tag as evidence. The satelle-story-release-review gate is the authority on CI-green, judging that recorded evidence before close:

satelle story set <id> --status release     # executor: bump .version, commit, push, record CI evidence
satelle story set <id> --status done        # gate: release evidence (CI green) + acceptance review

Drive each transition and let its gate judge it; a reject blocks the move and records why. You never self-enact a gated edge.

4. Correct a wrong definition mid-flight (amend)

title, body, acceptance_criteria and category freeze once a story leaves its entry state, so an agent cannot quietly weaken its own acceptance criteria to make a gate pass. satelle story set refuses them from then on. When the definition itself turns out to be wrong — a gate demands a criterion the ACs never named, or an AC is factually false — amend it rather than cancelling and re-raising (which retires the id, its ledger, and its accepted plan):

satelle story amend <id> --acceptance - --reason "AC2 asserted behaviour the system does not have" < ac.md

The freeze is not lifted, it is judged: --reason is mandatory, the repo's amend_review lifecycle hook names the reviewer that accepts or rejects the correction (a reject changes nothing), and an accepted amendment records every field's old and new value on the ledger as definition_amended. A repo that declares no amend_review hook has no amend path — with nothing to judge the correction, the freeze holds.

Amend never moves status. It runs from a performing state and from the parked (blocked) state, so a story parked over a wrong definition is corrected and resumed in place. Terminal and cancelled stories are refused: their record is history — raise a new story carrying supersedes:<id>.

5. Cancel (any → cancelled)

To abandon an item, record why:

satelle story set <id> --status cancelled

What you see

Every transition writes evidence to the ledger (visible on the story detail page and timeline). A per-transition summariser (satelle-step-summary) records a short prose recap of each step. The web project page shows a Progress column of numbered stage lights folded from the ledger: green = accepted, red = rejected, slate = ungated checkpoint, amber pulsing = current stage.

See also: satelle help reviewer-checks.


Mirrored from satelle’s built-in help. Read it in the binary with satelle help create-story, or see the canonical source in the satelle repo.