Milestone-driven ship-readiness loop for any app repo, resumable across sessions. Boots by profiling the stack, running the verification gate once, and fanning out read-only audit lenses into a 9-dimension scorecard plus an append-only numbered backlog; when the repo's overlay declares value journeys, a value ledger (one readiness light per journey) sits above the scorecard, every item carries a journey tag, and milestones are picked as the next slice of one journey. Work runs in user-gated m...
Installs into .claude/skills of the current project.
Are you the author of Ship Loop?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/xkazm04-ship-loop)
---
name: ship-loop
description: "Milestone-driven ship-readiness loop for any app repo, resumable across sessions. Boots by profiling the stack, running the verification gate once, and fanning out read-only audit lenses into a 9-dimension scorecard plus an append-only numbered backlog; when the repo's overlay declares value journeys, a value ledger (one readiness light per journey) sits above the scorecard, every item carries a journey tag, and milestones are picked as the next slice of one journey. Work runs in user-gated milestones (CP checkpoints, single-keystroke questions, an AFK protocol with provisional re-askable picks); one backlog item = one atomic pathspec-scoped commit, premise-checked against current code first; each milestone is certified by the repo's ordered gate (typecheck/lint/tests/build sequentially, slow e2e last, typecheck after any build that rewrites generated types). State lives in .claude/ship-loop/ of the consuming repo; project specifics come from .claude/ship-loop/config.md and the loop runs on generic defaults without it. Invoke with /ship-loop (resume), /ship-loop boot (fresh loop, prior state archived), /ship-loop gate | audit | recall."
category: workflow
memory: project
version: 2.4.1
tags: loop, ship-readiness, scorecard, backlog, milestones, gate, checkpoints, value-ledger
argument-hint: "[boot|gate|audit|recall]"
---
# Ship Loop — milestone-driven ship readiness
A permanent loop that answers one question run over run: **how close is this app to its ship bar, and what is the highest-impact next batch of work?** It is *evaluative* (is the product good enough?) layered on *verification* (does the gate pass?). It is not a per-commit CI gate — it is a deliberate, memory-backed pass on a cadence. Any session can resume it: the state files are the single source of truth, and this file is the procedure.
**The flow:** BOOT → GATE → AUDIT (parallel lenses) → value ledger (if journeys are declared) + 9-dim scorecard → append-only backlog → CP0 → milestones (each: work → gate → CPn).
**Governing rule at every fork:** auto-decide reversible work and log it as re-askable; defer existential and product decisions to the user at a checkpoint.
> Origin note: the loop ran Boot→M7/M8/M9 in several repos during 2026-07 with no skill definition; it was codified from those precedents, grew a value-journey axis in one repo (2.0), and is generalized here (2.1.0). Project facts that used to live in the body — gate commands, use-case tables, LOC rules, billing/auth dimension names — now live in each repo's overlay.
## Project overlay
Read `.claude/ship-loop/config.md` at the start of every run (it sits beside the loop's state files). It is the only hand-maintained file in that directory. **The loop runs with no overlay present** — on generic defaults listed below — and Boot drafts one from what it finds in the repo for the user to confirm at CP0.
| Key (a `## ` section in `config.md`) | Carries | Default when absent |
|---|---|---|
| `Stack` | One-line stack profile (framework, backend, test runners, external bridges). Fixes which gate steps and lenses make sense. | Inferred at boot from manifests/scripts; written into `state.md` harness notes. |
| `Cadence` | `milestone` (batch → gate → CP), `continuous` (proceed by severity, stop only for blockers/product calls), `per-item`. | `milestone`; asked at CP0. |
| `Gates` | **Ordered** table `step / command / ratchet / when`. Order is load-bearing (see Gate). Include slow steps (e2e, tours) last with their trigger condition, and any convention checks (LOC limits, docs-in-sync) that certify a milestone. | Derived from the repo's own scripts: typecheck → lint (0 errors) → unit tests → build → e2e if a script exists; all sequential. |
| `Value journeys` | Optional table `tag / journey / what the loop certifies / owner persona / docs`. Declaring it turns on the value ledger and journey tags. | None — the loop runs the 9-dimension scorecard alone; dimension 9 is the value case. |
| `Dimensions` | Renames/re-points of the 9 defaults (e.g. dimension 5 re-pointed from billing to a product's real trust surface; dimension 9 retired into the ledger). | The default names below. |
| `Conventions` | Repo rules the loop must respect while executing: file-size limits, context-map upkeep, framework docs to read first, docs-as-source-of-truth, parallel-session ledger path, whether the state dir is tracked or gitignored, theme variants to verify, push policy. | Only this skill's invariants. |
| `Lenses` | Optional extra/renamed audit lenses and their dimension mapping, plus scoping hints (context map, size of the codebase). | The seven default lenses in `${CLAUDE_SKILL_DIR}/references/lenses.md`. |
Full template with examples: `${CLAUDE_SKILL_DIR}/references/overlay-template.md`.
## State files (in `.claude/ship-loop/`, repo root)
| File | Contract |
|---|---|
| `config.md` | The overlay above. Hand-maintained; the loop proposes edits at CPn, never rewrites it silently. |
| `state.md` | Current truth: ship bar, **value ledger** (when journeys are declared: one row per journey, 🔴/🟡/🟢 + the one sentence that says what the journey can and cannot do today + next slice + linked items), scorecard (every cell with evidence and top gaps → backlog #), milestone status, checkpoint history, harness notes. Rewrite freely; keep it one screen of load-bearing facts. |
| `backlog.md` | Item table `# / status / [journey] / dimension / size / description` (journey column only when journeys are declared). **Numbering append-only — never renumber.** Statuses: ☐ todo · ◐ in progress · ☑ done · ✕ cut. Impact-ranked (frequency × reachability × cost), not by severity word; inline `[C]/[H]/[M]/[L]` severity is allowed. |
| `journal.md` | Append-only, one line per event (item done w/ commit SHA, CP resolution, gate result, root-caused saga). Never edit past lines. |
| `decisions.md` | CP answers from the user + auto-decisions taken while AFK (each marked "pending user review at CPn"). |
| `value-case.md` | Dimension-9 (value & market) synthesis. Written once by the value lens, corrected only with code-verified evidence. When journeys are declared it is superseded by the ledger — keep it as a pointer. |
| `archive-*/` | Frozen state of previous loops (different app or restarted loop). Read-only. |
Exact formats (backlog header, journal verbs, decisions sections): `${CLAUDE_SKILL_DIR}/references/state-formats.md`. Legacy location: some repos kept this state in a `state/` directory next to a project copy of the skill — on first resume, move it to `.claude/ship-loop/` once and journal the move.
## Two axes: the value ledger (optional) and the 9-dimension scorecard
**Scorecard (the gate axis).** Default dimensions: 1-Build & types · 2-Functional completeness (things actually *produce*, not stubs; claims honest vs docs) · 3-Tests (suite green + coverage of load-bearing paths) · 4-Simulated UAT / e2e · 5-Value capture (billing, tiering, packaging — whatever turns the product into something paid for or installed) · 6-Security (auth boundary, secrets, unsafe input paths) · 7-UX/UI polish (shared primitives, a11y floor, reduced motion) · 8-Ops (CI, release/deploy, signing/updater, migrations, docs in sync) · 9-Value & market. Each is 🔴/🟡/🟢 in `state.md`; every cell carries **evidence** (`file:line`, gate output, or a lens verdict) and its top gaps → backlog #. Dimensions 4 and 9 run as **lenses** (audit passes that emit backlog items), not fixed at boot. A dimension with no surface in this product (no auth, no billing) is **re-pointed at the product's nearest real trust or value surface** in the overlay — never scored 🟢 by vacancy. Hold every dimension as a **ratchet**: a green that goes red files a backlog item.
**Value ledger (the value axis — only when the overlay declares journeys).** A dimension-only scorecard optimizes hygiene while the product's actual jobs stay red. When the overlay lists the journeys the product exists for, `state.md` carries one ledger row per journey: 🔴 journey broken/absent · 🟡 runs but a load-bearing step is missing, dishonest, or manual · 🟢 runs end-to-end, evidence-backed, tested. Each row names the *next slice* (the smallest change that moves the light) and links its backlog items. Every backlog item then carries a journey tag — one of the declared tags or `hyg` (hygiene with no direct journey effect) — and **milestones are picked per journey**: "the next coherent slice of one journey", not a grab-bag by dimension; hygiene items are picked when they block a slice or when the gate is red. Dimension 9 folds into the ledger (the value case *is* the journeys). Re-judge the ledger with a **journey lens** at every checkpoint: walk the journey as its owner persona and ask "can they complete the loop today, honestly?" — reuse the repo's UAT characters when it has a `uat/` overlay. Declaring journeys on a running loop means a one-time tagging pass on the existing backlog at the next resume, journaled.
## Phases
### Boot (`/ship-loop boot` — only for a fresh loop)
1. Archive any existing `.claude/ship-loop/` contents (except `config.md`) to `.claude/ship-loop/archive-<slug>/`.
2. Profile the stack. If `config.md` is absent, draft it: gate commands from the repo's scripts/manifests, the default dimensions, the conventions CLAUDE.md/AGENTS.md already state. Run the gate once for a baseline.
3. If journeys are declared, run the **journey lens** per journey first (walk it end-to-end against the current code; the overlay's docs list the known holes) → ledger rows + tagged items.
4. Fan out the audit lenses — **one read-only subagent per lens**, each returning a 🟢🟡🔴 verdict + top 3-6 gaps with `file:line` evidence + one strength worth protecting, scoped tightly ("verdict + top gaps + evidence, **do not fix anything**, be concise"; use the repo's context map to target files). Seed `backlog.md` + `state.md`.
5. **CP0**: present ledger (if any) + scorecard + backlog; ask four questions, one at a time (AskUserQuestion): ship bar (what *done* means; default with journeys: every light 🟢), cadence, first milestone cluster or journey, UAT depth. Confirm the drafted `config.md`. If AFK, record provisional picks in `decisions.md` and proceed on the least-destructive batch.
### Resume (default)
1. Read `state.md`, then `backlog.md` and the tail of `journal.md`. Do NOT re-audit what the scorecard already scores — but if many commits have landed since the last journal entry, re-run the gate and premise-check open items before trusting the scorecard.
2. Reconcile against reality before proposing anything: `git status` + branch vs the default branch. Parallel sessions ship commits outside the loop (it has happened repeatedly) and leave large uncommitted WIP that is not the loop's; backlog premises may have moved.
3. If the repo keeps a parallel-session ledger (overlay: conventions), register there.
4. Continue: an in-flight milestone → keep executing; a completed one → run/finish its gate; gate green → next checkpoint.
### Checkpoint (CPn — before each milestone)
- Present: ledger delta (if any), scorecard delta, recommended next milestone (a coherent batch of backlog items, usually 3-8 — by theme such as a test-pin batch, an ops unbrick, product decisions; or, with journeys, one journey's next slice with hygiene items only as blockers), and any product decisions the work needs. One question at a time, single-keystroke answerable. Re-ask deferred questions; confirm auto-decisions.
- **AFK protocol**: ask twice ~60s apart; if silent, record a provisional pick in `decisions.md` (least-destructive option, marked for re-ask), avoid boot-path and product-call edits while AFK, and never commit destructive changes on a provisional. Out-of-scope boundaries in the repo's CLAUDE.md (anything the user must push, destructive git, external publishing) are **never** crossable via timeout — explicit consent required.
- Under **continuous cadence**, proceed down the backlog by severity without a CP ask; stop only for blockers or product decisions.
### Execute (milestone)
- One backlog item = one atomic commit, referenced by SHA in `journal.md`. Keep each change small and reversible. Fan out parallel subagents only for disjoint paths.
- **Never bundle the loop's changes with foreign in-flight WIP.** Stage only your paths (one `git reset -q && git add <paths>` invocation) and commit with a pathspec (`git commit -m "..." -- <paths>`); never `git add -A` in a shared tree. If the tree is a WIP soup, defer the commit decision to the user and journal it. Treat any modified file you did not touch as live WIP.
- Defer any item whose files are another session's hot area — mark it in `backlog.md` with the reason, don't fight over files.
- **Premise-check before executing.** Audits overstate ("permanently disabled" that wasn't, a mock that was already real), backlogs go stale (an item already shipped; a "deleted route" that was context-map drift), and parallel sessions close items out-of-band — verify the claim against current code first; correct the backlog item if the premise moved.
- Respect the overlay's conventions (file-size limits — extract before committing an over-limit file; keep the context map accurate; read the framework's bundled docs before writing framework-specific code when the version is newer than training data; docs-in-sync in the same batch).
### Gate (after every milestone — certifies it)
1. Run the overlay's ordered `Gates`. Default order: typecheck · lint (0 errors; a warning baseline is out of scope unless the overlay says otherwise) · unit tests · build · slow e2e/tours last.
2. **Sequential, never concurrent.** Steps that share generated files (schema generation, a build that rewrites generated type files) read an inconsistent tree when overlapped, and CPU contention alone blows e2e bridge windows (bitten twice in one repo). **When the build rewrites generated type files, run typecheck AFTER the build completes** — a concurrent typecheck reports spurious route/type errors that a clean rebuild-then-typecheck disproves (bitten at boot and M2 in one repo). Test-only diffs may skip the build; typecheck then runs against the last build's generated types.
3. A second language touched (e.g. a native backend) → its check + lint + the touched modules' filtered test suites; full-suite failures are triaged pre-existing vs regression — only regressions block.
4. UI touched → e2e/tours, **serialized after the suites**; pre-warm the e2e target AFTER committing; retry once on a settled machine before deeper diagnosis; verify new surfaces in every theme/variant the overlay lists. Test-only diffs may justified-skip e2e — record the justification. Full UAT runs are backlog items (dimension 4), not per-milestone.
5. Overlay convention checks (e.g. a file-size check on touched files) must return zero rows.
6. **Run the gate before any push.** Parallel sessions pushing ungated commits to the default branch is the loop's known failure mode (CI red all day in one repo). The verification gate is the overlay's commands — a product feature that happens to be called "gate" is not it.
7. Record the gate line in `journal.md`; flip milestone ☑ in `state.md`; update the scorecard **and re-judge the touched journey's light** (a milestone that doesn't move or protect a light should say so).
### Wrap (session end)
Update `state.md` + journal (commit SHAs), leave no uncommitted loop work (or an explicit journaled deferral of the commit decision), close your parallel-session ledger entry if the repo keeps one. The next session resumes from files alone.
### Other verbs
- `/ship-loop gate` — run the gate alone, journal the `GATE(...)` line, update dimensions 1 and 3 only.
- `/ship-loop audit` — re-run the lenses without archiving; new findings append as new numbers; a closed finding that reappears is a **regression**, journaled as such.
- `/ship-loop recall` — read the state files and report the trajectory (which dimensions/lights moved run-over-run, open/closed/regressed items, current ship bar + cadence, top of the backlog) — no new scan. A light that moved is the headline.
## Invariants
- **User owns product calls.** Ship bar, scope narrowing, feature hide/delete, pricing/positioning, privacy boundaries (what may leave a user's machine), security trade-offs are CP questions — never auto-decided, only provisionally deferred.
- **Journeys over dimensions** (when declared). A green scorecard with a red ledger is not progress; report the ledger first.
- **Honesty over green.** A gate that passes while the claim is unverified is not done — distinguish "code-verified" from "subagent-claimed" in every journal line (one repo's cold-start corrections exist because this was violated once).
- **Grounding.** No scorecard cell or finding without evidence (`file:line` / gate output / observation). Never fabricate a benchmark, a test result, or a "done". A claim about an external system (a bridge, a device, a service) is not done until an observation confirms it — a green status that isn't derived from that system's truth is a finding, not a pass.
- **Lenses emit items, items get numbers, numbers never change.** With journeys declared, every item carries its tag; untagged items are a boot/resume bug, not a category.
- **Impact over label.** Rank by frequency × reachability × cost, not by the severity word.
- **Shared-tree hygiene.** Re-read before edit; targeted `git add`; pathspec commits; the user pushes unless the overlay says otherwise.
---
<!-- clause: knowledge-sync v2 - stamped by scripts/apply-skill-clauses.mjs from docs/skill-clauses/knowledge-sync.md; edit the template, then re-stamp -->
## Knowledge sync
This skill proposes and executes backlog items. Every item it proposes is judged against the standard this repo subscribes to, so a run moves the codebase toward the registry's golden paths and sends back what it learned - not toward a private notion of "better" that the next skill will undo.
**Subscription** - read once at the start of the run; degrade honestly, never invent a standard:
- `.ai/manifest.yaml` -> `registry.local` (default `../ai-registry`; `$AI_REGISTRY_DIR` wins) and `knowledge.domains` (the bundles this repo consumes - `software-engineering` for code, plus whatever else it declares). No registry declared -> skip this section and say `registry: none` in the run header.
- `.ai/registry-map.json` - the join between this repo's contexts and the bundle's subjects, with a per-pair state (`unknown` / `conformant` / `deviation` / `not-applicable`) that `/conform` fills in over time. Missing while `context-map.json` exists -> build it once, `node <registry>/scripts/build-registry-map.mjs --project <slug>`, and commit it: the map is the repo's subscription to the paths, and it is how a path improved for another project reaches this one. Missing both -> resolve through `<registry>/knowledge/<domain>/index.json` and say `registry: declared, unmapped`.
- The always-on rules `.claude/rules/ai-registry-*.md` carry the subject map. They orient; they do not replace the read below.
**Read before you propose.** For each context in scope, take its subjects from the map and read the golden path (`subjects[<slug>].file`, verbatim from the index - never a path built from a slug; bundles are nested) plus the techniques whose `use_when` matches what you are about to decide. Then every backlog item you emit names the technique it serves or violates - `standard: <subject>/<technique>` - or `standard: none` when nothing governs it. A pair the map already marks `deviation` is a pre-approved item with its fix described; a pair marked `conformant` is a regression guard on anything you change there. A deviation is a finding: never lower the standard to fit the code, and never present a technique's number as a rule - the technique carries the rule, the application carries the measurement.
**Log the read** - one line per context, append-only, to `.ai/consults.jsonl` in the task's MAIN checkout (never a disposable worktree), written before the run's first commit - pre-commit consult checks read it: `{"ts":"<ISO>","bundle":"<domain>","subjects":["<slug>"],"techniques":["<slug>"],"deviations":<n>}`, where `deviations` counts the items this run raised that a technique explicitly names. Bare slugs, never paths. The registry's `signals-collect.mjs` folds these into `signals/` as counts only; it is the only way the corpus learns which paths are load-bearing and which are decoration.
**Send back what a LANDED fix taught.** When a change you made and verified generalizes past this repo - a rule that would transplant to an unrelated team, a case where a technique's rule broke against real code, or a place this repo does it BETTER than the golden path - append one line to `.ai/registry-leads.jsonl`: `{"ts":"<ISO>","bundle":"<domain>","nearest":"<subject-slug or null>","kind":"technique|application|subject","claim":"<when X, do Y, because Z - one sentence>","because":"<what this run measured or broke and fixed>","confidence":"low|medium|high","from":"ship-loop@<version>"}`. Earned only: it came from code you changed, not from a fix you proposed. A lead ORIGINATES a finding and never authorizes one - nothing here edits a bundle; the registry's `leads-collect.mjs` -> `librarian/inbox.md` -> `/intake` decides what survives. Say in the report that you filed one, and say plainly when you filed none. Verdicts on a pair's state belong to `/conform`: close by naming the contexts you touched so it can re-judge them.
<!-- /clause: knowledge-sync -->
<!-- clause: skill-reflection v6 - stamped by scripts/apply-skill-clauses.mjs from docs/skill-clauses/skill-reflection.md; edit the template, then re-stamp -->
## Skill Reflection
After the work, record only useful observations supported by this run. No lesson is
a valid result. Reflection inherits the task's authorization; it grants no additional
permission to edit another repository, send data, commit, or publish.
**Run log.** Unlike a lesson, this is written on every run that started work - failed and
aborted runs included; skip read-only info modes and runs cancelled before any work. Append
ONE line to `.ai/skill-runs.local.jsonl` at the root of the task's MAIN checkout: local,
gitignored run output inside the task's own repository, never a write into the registry.
A run that worked in a linked worktree logs in the main checkout that worktree belongs to
(`git rev-parse --git-common-dir`, minus `/.git`) - a disposable worktree takes the line
with it when it is removed. `log-run.mjs` resolves this on its own.
The registry pulls it later (`/librarian skills` on the same machine). When a registry
checkout is reachable (`registry.local` in `.ai/manifest.yaml`), prefer its writer, which
stamps project, device and version for you:
```sh
node <registry>/scripts/log-run.mjs --skill ship-loop --outcome <o> --difficulty <1-5> \
--provider <claude|openai|xai|qwen|google|other> --model <your model id> [--effort <level>] \
[--tokens-est <n>] --result "<one sentence>" --comment "<self-reflection>"
```
Otherwise write the line yourself: `{"ts":"<ISO, UTC Z>","skill":"ship-loop","outcome":…,
"difficulty":…,"provider":…,"model":…,"effort":…|null,"tokensEst":…|null,"result":…,"comment":…}`.
- `outcome`: `shipped` (the goal landed) / `partial` / `no-op` (ran correctly, nothing to
do) / `parked` (designed or staged, deliberately not landed) / `failed` / `aborted`.
- `difficulty` rates the task as this run met it: 1 trivial - mechanical; 2 routine - the
method as written; 3 demanding - real judgment calls or one detour; 4 hard - dead ends,
rework or an operator course-correction; 5 at the edge - partial or failed on the merits.
- `model`/`effort` as your harness states them (`null` effort when you cannot see it).
`tokensEst` is the drop in the harness's remaining-token counter since this skill was
invoked, or `null` (with `log-run.mjs`, omit `--tokens-est`); exact figures are measured
later from transcripts - never guess one.
- `result` is one line (max 240 chars). `comment` (max 2000) is the self-reflection a
reviewer reads: what worked, what the method made harder, where its instructions were
wrong, missing or ignored. No filesystem paths or email addresses.
- Never read run logs during a run. They are evidence ABOUT this skill for its reviewer;
an executor that reads its own diagnosis contaminates the next measurement.
**Project learning.** Only when this run produced an observation that would change how a
future run behaves. A run that went as the method describes writes nothing: an entry that
restates the procedure, records "no issues", or repeats the task is a defect, not a
deliverable. When there is such an observation and local edits are within scope, put one
dated line in the overlay this skill's `## Project overlay` section names, under
`## Skill improvement log`. **Write only into an overlay that already exists.** If the
project has none, put the observation in the response instead - creating a new tracked
file for a reflection is scope the task did not ask for, and a reader who never asked for
the skill has to review it. If the overlay is a structured config (YAML, TOML, JSON),
record the note as comments so the file keeps parsing, or use the response.
Use a supplied memory contract only when its destination and writes are authorized.
Keep project details out of the shared method.
**Method learning.** Identify the installation before editing anything. A local
`.ai/registry-installation.local.json` receipt can identify development versus release,
the registry revision, and selected skill versions. Verify any link's actual target;
do not assume a skill directory is a writable registry link.
- For a pinned release, marketplace cache, ordinary copy, or unknown installation,
keep a proposal in the project overlay or response. Do not edit the installed method
or silently relink it. Adoption and rollback are explicit installation operations.
- For a development link, edit the registry only when that checkout is already within
the accepted task scope. Otherwise report a proposal. Authorized changes belong in
the source checkout, followed by its gates; commit only when the task authorizes it.
- Record an actual lesson in `LESSONS.md` against the version **used**:
`## <version-used> - <YYYY-MM-DD> - <project-name>` and concise bullets. A proposal
must be labeled as such; structural checks are not evidence of field effectiveness.
- Applied skill changes require a version bump: patch for wording, minor for a step
refinement, major for method redesign. A lesson alone needs no bump. Shared stamped
clauses are edited in the registry's `docs/skill-clauses/` and regenerated with
`scripts/apply-skill-clauses.mjs`, never patched in individual installed skills.
**Domain learning.** Follow `## Knowledge sync` when present, within the same scope
and privacy boundaries. A method lesson and a domain knowledge lead are different
artifacts; do not fabricate either to fill a reflection quota.
<!-- /clause: skill-reflection -->