InsanelyGreat's Shippable States Development

The complete guide

A pragmatic, AI-powered engineering discipline for solo developers through giant orgs — maintain a deployable, production-ready state at all times, without a large engineering org.

Describes skills library v2.11.0 (SSD 2.0)  ·  orchestrator /ssd v2.10.0  ·  bootstrap /ssd-init v1.13.0  ·  /feynman v1.1.0  ·  VERSION  ·  CHANGELOG

Verify locally: cat ~/.claude/skills/VERSION

Jump to Getting Started

Overview

InsanelyGreat's Shippable States Development (SSD) is a pragmatic engineering discipline designed for solo developers and small teams who use AI — specifically Claude Code — to build production software. The system maintains a deployable, production-ready state at all times throughout the development cycle.

The core principle:

If you can't ship it right now, you don't have a product — you have a construction site.

SSD synthesizes lessons from continuous deployment, trunk-based development, feature flags, and decades of software engineering failures where "90% done" meant "months from shipping." It was designed from the ground up to be operated by one person or a handful of people, with AI as a force multiplier at every step of the workflow.

The methodology is simple: maintain a deployable state at all times. The discipline is hard: no shortcuts, no "we'll fix it later," no broken code on main. The payoff is enormous: no death marches, predictable delivery, high quality, low stress — without needing a 10-person eng team to enforce it.

Why It Matters

The "90% Done" Problem

Traditional development creates a predictable trap:

Week  1–8:  "Making good progress!"
Week  9:    "We're 90% done!"
Week 10:    "Still 90% done..."
Week 11:    "Uh, still 90%..."
Week 12:    Panic, cut features, ship something broken

Why? The last 10% includes all the work no one budgeted for:

  • Integration between components
  • Error handling and edge cases
  • Performance under real load
  • Production deployment and data migration
  • Security hardening and cross-browser testing
  • Accessibility and documentation

The InsanelyGreat's SSD solution: Do the hard "last 10%" work incrementally throughout development, not as a crisis at the end. Claude Code skills handle the checklist so you stay focused on shipping.

The Iron Law

Every project has exactly three variables:

  1. Scope — what features and capabilities ship
  2. Time — when it ships
  3. Quality — how well it works
Iron Law: You can fix at most ONE of these. The other two must flex.
Constraint What Flexes When to Use
Fix Time Scope reduces, quality preserved Hard deadlines (conference, contract, funding round)
Fix Scope Timeline extends, quality preserved API compliance, feature parity requirements
Fix Quality Time and scope flex Medical, financial, safety-critical systems

Most projects are time-constrained. Declare your constraint at kickoff. Adjusting scope to meet a deadline is not failure — it's engineering judgment.

Principle 1: Constant Production Parity

Your development environment must match production as closely as possible from Day 1.

Traditional

  • Weeks 1–8: Local development
  • Week 9: "Okay let's deploy to staging..."
  • Week 10: "Why doesn't it work in staging?"
  • Week 11: "Production is different from staging..."
  • Week 12: "What do you mean SSL certs take 3 days?"

InsanelyGreat's SSD

  • Day 1: Deploy "Hello World" to production
  • Day 2: Deploy first feature to production
  • Day 3: Deploy improved version
  • Day 30: Deploy to production (like every day)

Why this works: Deployment is never "the hard part" because you do it constantly. Production issues surface immediately when they're easy to fix. You know your deployment budget from Day 1.

Principle 2: The Shippable State Invariant

At the end of each work session, the system must be in a state where:

  • All tests pass
  • No compilation errors
  • No broken user-facing features
  • Documentation matches implementation
  • Could be deployed to production without embarrassment

Not required: feature-complete or meeting all goals. Just that what exists actually works.

The practical test: "If I got hit by a bus right now and someone else had to ship what I've committed, would they hate me?"

Principle 3: Feature Flags Over Feature Branches

Long-lived feature branches are antithetical to shippable states.

# Problem: Feature branch divergence
Main:    A---B---C---D---E---F---G---H
              \
Feature:       I---J---K---L---M
                                \
                          (days of merge conflicts)

# SSD: All work on main, behind flags
Main:    A---B---C---D---E---F---G---H

Day 1: Add feature code (flag off by default)
Day 2: Expand feature (still flagged off)
Day 3: Feature works, flip flag on

All work happens on main/trunk. The feature exists in production but is invisible until ready.

if feature_flags.is_enabled("new_checkout", user=user):
    return new_checkout_flow(user, plan)
else:
    return legacy_checkout_flow(user, plan)
Flag rollout order: Internal team → 1% → 10% → 50% → 100% → remove flag and dead code.

Principle 4: The Ratchet Principle

Forward progress only. Each commit improves the system in some measurable way.

Banned commits:

  • "WIP" or "checkpoint" commits
  • "Broken, will fix tomorrow"
  • Commented-out code "for later"
  • Partially implemented features visible to users

The ratchet mechanism — every commit must:

  • Pass CI/CD
  • Maintain or improve code coverage
  • Be deployable

If you need to save work that's not ready: use local stash (not committed), Draft PR with "DO NOT MERGE" (not on main), or a feature flag (committed, but invisible).

Principle 5: Scope Flexibility Is a Feature

Traditional thinking: "We must deliver all planned features by the deadline."
Result: Deliver nothing on time, or deliver broken features.

SSD thinking: "We deliver whatever is shippable by the deadline."
Result: Deliver working software, adjust scope based on reality.

Reducing scope is not failure — it's engineering judgment.

How to cut scope well:

  • Cut entire features, not the quality of existing features
  • Cut depth, not breadth (fewer powerful features beats many broken features)
  • Hide features behind flags rather than deleting (easy to resurrect)
  • Communicate cuts early and often to stakeholders

Pattern 1: Deployed Day One

Before writing any business logic, establish a real deployment to your distribution channel. The specifics vary by platform — pick yours:

  • Frontend deployed to a real URL (even if it just renders a title)
  • Backend API deployed and reachable from the frontend
  • Database provisioned and migrated
  • CI/CD: push to main → deploy to staging automatically
  • One authenticated route working end-to-end
  • Error tracking (Sentry) wired up in frontend and backend
  • Domain + SSL configured
  • App builds and runs on minimum target device/simulator
  • Main tab/navigation structure in place (empty screens are fine)
  • One piece of data persisted end-to-end: create → persist → relaunch → still there
  • Authenticated session working: login → token in Keychain → cold launch restores
  • CI: Xcode Cloud or GitHub Actions builds and runs tests on every push
  • App archived and submitted to TestFlight (even a Hello World build)
  • Crash reporting wired up (Sentry, Crashlytics, or Bugsnag)
  • App Store Connect record created with bundle ID matching the app
  • App builds and runs on minimum target API
  • Hilt dependency injection wired and working
  • Navigation structure in place with NavHost
  • One piece of data persisted end-to-end in Room: create → persist → kill app → relaunch → still there
  • Authenticated session working: login → token in DataStore → relaunch restores
  • CI: GitHub Actions or Bitrise builds debug APK and runs unit tests on every push
  • Internal Testing track on Play Console with a working build uploaded
  • Firebase Crashlytics (or equivalent) initialized and sending test crashes
  • App builds and launches on minimum target OS
  • Main window with placeholder navigation
  • One real piece of persisted data: create it, see it in the UI, relaunch — still there
  • Basic Settings window
  • CI: Xcode Cloud or GitHub Actions builds and runs tests on every push
  • Archive and notarization working (even for a Hello World app)
  • Crash reporting wired up (Sentry, Bugsnag, or Crashlytics)
  • Service containerized and deployed to production environment (even returning {"status": "ok"})
  • Health endpoints (/health, /ready) responding correctly
  • Database provisioned, connected, and one migration applied
  • Structured logging with request_id propagation on every request
  • Error tracking (Sentry or equivalent) capturing unhandled exceptions
  • One authenticated endpoint working end-to-end
  • CI/CD: push to main → container built, tests run, deployed to staging
  • .env.example documenting every required environment variable

This is your MVP. It does nothing useful, but it's real. If deployment takes 2 weeks and you budget 0 weeks, you're starting 2 weeks late on Day 1.

Pattern 2: Walking Skeleton

Build one feature end-to-end before building any feature fully complete.

Wrong order

  • Design all UI screens
  • Build all database tables / persistence
  • Write all API endpoints / services
  • Connect everything
  • Discover they don't fit together

Right order

  • Build login flow end-to-end
  • Build "add item" end-to-end
  • Build "edit item" end-to-end
  • Each step shippable as-is

Never build all UI then all backend/persistence. One complete flow first, then expand breadth. "End-to-end" means different things by platform: on web, UI → API → DB → response. On iOS, View → persist → relaunch → verify. On Android, Compose → Room → relaunch → verify. The principle is the same: one complete flow before breadth.

Pattern 3: Dark Launching

Launch features in production before they're visible to users. The pattern works across all platforms:

if feature_flags.new_dashboard and user.is_internal_tester:
    return render_new_dashboard(user)
return render_old_dashboard(user)
if featureFlags.isEnabled("newDashboard", user: user) {
    return NewDashboardView()
}
return OldDashboardView()
if (featureFlags.isEnabled("newDashboard", user)) {
    NewDashboardScreen()
} else {
    OldDashboardScreen()
}

Benefits:

  • Test in production without risk
  • Gradual rollout (internal → beta → everyone)
  • Easy rollback: flip flag
  • Development never blocks deployment
Critical rule: A feature is not "done" until the flag is removed. Flag code is technical debt — pay it off quickly once at 100%.
Platform note: Web feature flags can be server-side (hot-swap, no deploy needed). Mobile and desktop flags use an SDK (Firebase Remote Config, LaunchDarkly) and typically take effect on next app launch — flag changes are not instant on mobile.

Pattern 4: Timebox with Eject

For risky or exploratory work, timebox it with a pre-committed eject plan.

"We'll spend 3 days exploring this approach.
On day 3, we decide:
  - Ship it
  - Iterate it (extend timebox)
  - Abandon it (revert to last shippable state)"

This prevents:

  • Sunk cost fallacy ("we've invested so much...")
  • Endless exploration without shipping
  • Half-finished experiments in the codebase
The eject plan must be decided before starting, not when you're attached to the code.

Pattern 5: The Nightly Ritual

End each day with a shippable state. Spend the last 30 minutes on this checklist:

  • All tests pass locally
  • Code committed and pushed
  • CI/CD pipeline green
  • Feature flags set appropriately
  • Documentation updated if APIs changed
  • Tomorrow's first task identified

Your future self (or your teammate) should be able to pick up exactly where you left off, with no confusion about what state things are in.

Decision Framework

Choosing Your Constraint

At project kickoff, declare your primary constraint and communicate it explicitly.

Constraint Type Example Projects Scope Quality
Time-Constrained Conference demos, MVP for funding, contractual deliveries Flexes Preserved
Scope-Constrained API compliance, platform migrations, feature parity Fixed Preserved
Quality-Constrained Medical devices, financial systems, infrastructure Flexes Fixed

When to Cut Scope

Scope cuts should happen early and often, not as last-minute panic.

Cut scope now if you see these signals:

  • It's Wednesday and you're not confident about Friday's shippable state
  • You're accumulating technical debt faster than paying it off
  • Tests are being skipped "temporarily"
  • "We'll clean it up after shipping" is appearing in conversations

Metrics That Matter

Traditional (misleading) SSD (actually useful)
Lines of code written Days since last production deployment
Number of commits Mean time to deploy a change
Features "in progress" % of code behind feature flags (target: <5%)
Percentage complete Test coverage (and is it passing?)

Deployment Frequency

This is the single most important SSD metric:

  • Once per month — Traditional waterfall
  • Once per week — Decent
  • Once per day — Excellent
  • Multiple times per day — World-class
If you can't deploy daily, you don't have shippable states — you have deployment problems masquerading as development problems.
Platform note: "Deploy" means pushing to your primary distribution channel. For web: production server. For mobile: TestFlight / Play Internal Testing (daily). For App Store / Play Store production releases, weekly is excellent. The metric that matters: how quickly can a committed change reach a real tester?

Common Objections

"This sounds like more work"

You're doing the work either way. Option A: days 1–85 ignore deployment, days 86–100 frantic debugging, ship broken. Option B: do the hard parts incrementally every day, day 90 ship the fully-working subset you completed. Same total effort, drastically different stress and quality.

"Our stakeholders need to see progress"

SSD gives better demos. Traditional: "Here's a mockup... this button doesn't work yet... imagine when this is connected to the backend..." SSD: "Here's the actual working product. Press any button." Which demo builds more confidence?

"We need to iterate quickly"

False dichotomy. Shippable states don't slow iteration — they enable it. Every iteration is testable by real users. No integration phase blocking feedback. Pivots are cheap because sunk cost is always minimal.

"My team isn't disciplined enough"

This is exactly why you need this. Discipline problems are solved with systems, not willpower. CI/CD forces tests to pass. Can't commit broken code. Daily deployments force completion. Visible production state keeps everyone honest. SSD creates discipline through automation and forcing functions.

"This doesn't work for mobile apps"

It works. You cannot deploy to the App Store daily (review takes 1-3 days). But you CAN deploy to TestFlight / Play Internal Testing daily. SSD targets the internal deployment pipeline, not the store review process. TestFlight is your "production" for SSD purposes until you cut a release.

Feature flags on mobile use an SDK (Firebase Remote Config, LaunchDarkly). Flag changes take effect on next app launch, not instantly. When you cut a store release, it should be a non-event — you've been shipping to testers daily. For macOS desktop: notarization is your deployment gate. Automate it in CI from Day 1.

Getting Started

Four weeks to establish the SSD rhythm. Success criteria: on day 30, deploy to production with confidence in under 10 minutes.

Day 0: Bootstrap

  • Install the skills: git clone https://github.com/AlexHorovitz/skills ~/.claude/skills
  • Run /ssd-init once at the project root — creates the .ssd/ working directory (selective .gitignore: design docs committed, machine state ignored), writes .ssd/project.yml (detected stack/framework/platform), creates docs/decisions/, docs/runbooks/, docs/architecture/, and runs prerequisite checks
  • All /ssd phases refuse to proceed until init has run

Week 1: Foundation

  • Set up CI/CD pipeline
  • Deploy "Hello World" to your distribution channel (production server, TestFlight, Play Internal, notarized build)
  • Configure automated testing
  • Establish feature flag system (server-side for web, SDK-based for mobile/desktop)
  • Invoke /ssd start to run the Walking Skeleton playbook

Week 2: First Feature

  • Build one feature end-to-end
  • Deploy to production behind flag
  • Verify in production
  • Enable for internal users

Week 3: Rhythm

  • Deploy to production daily
  • Every commit passes CI
  • All incomplete features behind flags
  • Documentation current

Week 4: Optimization

  • Reduce deploy time to under 10 minutes
  • Increase test coverage
  • Remove old feature flags
  • Retrospective: what's working?
If you can't deploy daily after Week 1: Stop adding features. Fix the deployment pipeline. That is your only priority.

Platform-specific Day 1 checklists for iOS, Android, macOS, Web, and Headless are in Pattern 1: Deployed Day One above.

Claude Code Skills

This page documents library v2.9.0. Check yours with cat ~/.claude/skills/VERSION. If it is older, /ssd upgrade reports what has drifted; the changelog is the full record.

InsanelyGreat's SSD is implemented as a set of orchestrated skills for Claude Code — this is what makes the methodology practical for a single developer or small team. The /ssd orchestrator sequences the right sub-skills for each development phase, giving you the equivalent of a senior architect, systems designer, and code reviewer on call at all times. The full skill set is free for personal and internal organizational use — github.com/AlexHorovitz/skills (library v2.7.0).

Skill Taxonomy

Type Skills When you invoke directly
Bootstrap /ssd-init Once, at project start (or when .ssd/ has drifted)
Orchestrator /ssd Always — start here after init
Domain /architect, /coder, /systems-designer, /refactor When working outside the SSD workflow
Review /code-reviewer, /codebase-skeptic, /software-standards On-demand or via SSD
Reference /methodology When you want to understand SSD doctrine or score self-adherence

Step 1: /ssd-init — Project Bootstrap

Run once per project before any /ssd phase. First-run housekeeping: creates .ssd/ (working directory with a selective .gitignore — design docs committed, machine state ignored), writes .ssd/project.yml (detected language, framework, platform, distribution channel), writes .ssd/gate.yml (committed gate inputs — see below), creates .ssd/current.yml (active workstreams pointer), creates docs/decisions/ / docs/runbooks/ / docs/architecture/ (committed decision records), and runs SSD prerequisite checks.

Idempotent — safe to re-run. It never overwrites existing files, never deletes anything, and appends to .ssd/init-log.md on each run.

Prerequisite checks reported: CI/CD pipeline, test harness, linter/formatter, pre-commit hooks, feature flag system, deployed "Hello World", secrets management, README with setup steps. Missing items are flagged BLOCKER / MAJOR / MINOR — the user fixes them, not /ssd-init.
Private mode. /ssd-init --private bootstraps a project that tracks nothing SSD produces — see Private Mode. Without the flag, behavior is byte-identical to the default.

Gate Readiness Since v2.5

Init now leaves the gate functional, not merely present. Two gate rules — tests-pass and feature-flag-present — need inputs (test_command, feature_flag_marker) to do anything. Before v2.5 nothing wrote them, so both rules SKIPped in every project SSD had ever initialized, and because the inputs would have lived only in gitignored project.yml they could not travel to a second clone or a CI runner. Step 6.5 closes both holes.

/ssd-init detects your test command most-specific-first — a project's own declared entry point beats a language default:

Signal in the repoDetected test_command
Makefile with a test: targetmake test
package.json with scripts.testnpm test
A Python marker file — pyproject.toml, pytest.ini, or setup.pypytest
go.modgo test ./...
Cargo.tomlcargo test
*.xcodeproj / Package.swiftxcodebuild test … / swift test

Note that a bare tests/ directory is not a pytest signal on its own — Rust keeps integration tests in tests/ beside Cargo.toml, and Go and JS projects use the name too, so inferring pytest from the directory alone would write a confidently wrong test_command into exactly the projects ADR-0015 exists to protect. A pytest detection requires one of the Python marker files above.

A genuinely polyglot repo with two top-of-table signals prompts rather than guessing. If nothing is detected the key is written commented out with a one-line explanation — the reader skips comment lines, so it degrades to the old SKIP with no regression, while making the missing piece visible in the file you'll actually open. An undetected or ambiguous test command is logged at MAJOR so it never sits silently inert.

The result lands in .ssd/gate.yml — the one committed .ssd/* config file, carrying only portable gate inputs:

# .ssd/gate.yml — committed gate inputs (ADR-0015). Portable across clones and CI runners.
# Machine-specific state stays in .ssd/project.yml (gitignored). gate-rules.sh reads project.yml
# first (local override), then this file (the committed floor).
test_command: pytest
# feature_flag_marker: <regex>   # set once a flag mechanism exists (see the flag BLOCKER)

feature_flag_marker can't be detected before a flag mechanism exists. It's written when a known library is present (unleash, launchdarkly, growthbook — their documented call markers) and otherwise left commented, tied to the feature-flag BLOCKER that init already reports: whoever establishes the flag mechanism sets the marker.

Already initialized before v2.5? Your project has two inert gate rules until you run /ssd upgrade --apply, which applies the gate-inputs-present and committed-gate-yml migrations. Its detection is close to the table above but deliberately stricter on Python: the migration requires a marker file, so it will not infer pytest from a tests/ directory. Re-running /ssd-init works too — it's idempotent, and an existing gate.yml is read and drift-reported, never overwritten. Full rationale: ADR-0015.
Changed in v2.9.0. On a project with no .gitignore at all, committed-gate-yml and strict-selective-gitignore used to report ERROR :: apply ran but convention still absent and exit 3 — telling you the engine was broken when the project simply was not ready. Both now return NOOP (8) with a note naming the missing precondition and the remedy: run the selective-gitignore migration first.
/ssd refuses to proceed if .ssd/project.yml is absent. Init is not auto-run — the user decides when to commit to the SSD convention.

Step 2: /ssd — The Orchestrator

SSD has one surface, progressively disclosed (SSD 2.0). The everyday path is the bare command: typing just /ssd reads .ssd/current.yml + .ssd/current.notes.yml, surfaces active workstreams, and proposes the next action — naming the explicit step it's taking so you never have to memorize the verb set. The orchestrator never silently advances a phase; it always proposes, and you accept or redirect.

The explicit phase commands below stay a first-class escape hatch — every phase is still directly invokable when you want to force a step. But the command path is a thin alias that lowers into the conversational path: a power-user shorthand, not a co-equal surface with its own state. Everything a command does, bare /ssd can propose; nothing is reachable only by command (ADR-0012).

/ssd (no arg) Auto-detect: read state, propose next action — the path you use
/ssd start Walking Skeleton: architect + systems-designer for Day-1 deploy
/ssd feature Feature loop: architect → systems-designer → coder → code-reviewer → deploy
/ssd design Bundled architect + systems-designer pass (single invocation)
/ssd milestone Deep audit + targeted refactor (runs after shipping)
/ssd verify Mandatory remediation verification after milestone refactor
/ssd gate Shippable-state check (multi-round, auto-numbered)
/ssd ship Deploy readiness check (systems-designer checklist)
/ssd audit Adversarial comparative review — the nuclear option
/ssd upgrade Migrate a project to the latest SSD conventions (drift report · --apply · --adopt · --elect)

Parallel Feature Workstreams Since v1.17

The orchestrator now treats multiple in-flight features as first-class. Up to four active workstreams per project (soft advisory limit, no hard cap), each with its own branch and an optional git worktree so two features can be edited side-by-side without checkout churn. The single-feature flow remains the default; parallel is opt-in.

/ssd feature new <slug> [--branch <name>] [--worktree] [--from <ref>] — create branch (and optional worktree), seed brief, register workstream
/ssd switch <slug> [--no-note | --auto-note] — pause current workstream with handoff note, check out target
/ssd worktree <slug> add|remove [--path <path>] — explicit worktree lifecycle, decoupled from feature new

At gate time, /ssd gate intersects the gated workstream's tracked file footprint with every other active workstream's footprint and surfaces overlaps as SUGGESTION-tier findings — never BLOCKER, never MAJOR. Overlap is often intentional (layered features, one workstream extends a file the other added); the orchestrator surfaces, the user judges. Running bare /ssd on any branch auto-resolves to the correct workstream via branch name → recorded mapping → prompt. The Shippable State Invariant still holds per workstream: parallelism reduces switching friction, it does not lower the bar.

Full design notes: ADR-0007 — Parallel features as first-class workstream artifacts.

Iterations Inside a Feature

A feature is one cycle by default (one design → build → review → deploy). Real features sometimes ship as multiple iterations. As of v1.5.0 of /ssd, this is a first-class concept — append #<iter-id> to any phase command:

/ssd code my-feature#3a
/ssd review my-feature#3b
/ssd ship my-feature#3b

Iter-ids match [A-Za-z0-9_-]+. The first #iter reference promotes the feature non-destructively to the multi-iteration layout (iterations/<iter-id>/ under the feature root). Single-cycle features keep the flat layout.

Multi-Round Gates

If /ssd gate fails (BLOCKER or MAJOR found), the workstream returns to coder. Re-running the gate after fixes produces a round-2 review at 04-code-review-round-2.md (or iterations/<iter>/code-review/round-2.md for multi-iter features). The orchestrator auto-numbers rounds, increments current.yml.gate_rounds, and requires closed_from_previous_round discipline on round 2+ — every closure is verified against the code, not copied from coder-status. gate_rounds: 3 on a workstream is a useful budget signal that scope cut may be wiser than another fix attempt.

The Rails — Canonical Opinionated Path

The eight-step canonical sequence (brief → design → code → review → gate → deploy → rollout-advance → flag-removal) lives in ssd/rails.md. That file is the single source of truth for what no-arg auto-detect proposes and what the eight critic-grade invariants are. A workstream that skips a step records the deviation in current.yml.active[].rail_deviations. Deviations are not failures — they are engineering judgment captured for the record. Teams with genuinely different needs fork rails.md and point project.yml.rails: at the fork.

Progressive Disclosure Changed in SSD 2.0

SSD 2.0 removed the developer-profile concept. Earlier versions carried a developer_profile field (novice | standard | expert) plus a teaching_mode toggle. One system now serves both the newcomer and the expert through progressive disclosure instead: the bare /ssd reads your state and proposes the next step in plain language, naming the explicit command it's running so you can see what the orchestrator chose and why — while every manual verb stays one hop away for power users (NeXTSTEP: lead the newcomer, never take the Terminal from the expert).

The old keys are no longer read. A pre-2.0 .ssd/project.yml that still carries developer_profile or teaching_mode is simply ignored (no crash); run /ssd upgrade for a guided clean-up that deletes the dead keys. See ADR-0012 for the rationale.

Milestone → Verify Loop

Every milestone takes a before/after snapshot and requires explicit verification:

  1. Snapshot: record git SHA and metrics to .ssd/milestones/<topic>/sha-before and metrics-before.yml.
  2. Deep audit: codebase-skeptic writes skeptic-before.md.
  3. Refactor planning: refactor emits refactor-plan.md — every item cites a specific finding ID from skeptic-before.md. No cite → not in scope.
  4. Validate: code-reviewer with remediation_mode: true on each refactor PR.
  5. Deploy and confirm production health.
  6. Verify (mandatory): re-run codebase-skepticskeptic-after.md; diff frontmatter; re-run code-reviewer on the remediation diff. The milestone is complete only when all original BLOCKER/🔴/💀 findings are ✅ closed, no new BLOCKER-severity regression was introduced, and the remediation diff has no BLOCKERs. A refactor that claims to close findings without verification is indistinguishable from wishful thinking.
Step 0.5 — where /feynman fits. The loop above audits the system. A milestone is also the moment to audit the account of the system, which fails differently: step 6's "all original findings ✅ closed" is itself a claim, and a milestone that closes findings on paper is exactly what an epistemic audit is built to catch. As of v2.7.0 (ADR-0016) the orchestrator offers the audit at Step 0.5, before the skeptic runs — its 🔴 contradicted claims become declared scope for step 2, so the skeptic reviews the structures the false beliefs were resting on. /ssd verify re-proposes it, for the reason above.

Offered, not automatic — and that is a design decision, not an omission. The skill's own frequency rule says running it every sprint makes it "the eighteenth ritual nobody can trace to a decision — at which point Phase 3 will catch it, and should." Auto-invoking it would make the skill fail its own inventory. So the orchestrator's job is to make sure you are asked; declining is recorded in the milestone record rather than passing silently, so a later reader can tell a decision from a gap.

GitHub Issue Tracking Since v2.3 · opt-in

Your SSD workstream state normally lives in .ssd/current.yml — invisible to anyone who isn't sitting at your checkout. Turn this integration on and the orchestrator mirrors that state to GitHub issues as each feature advances, so a teammate, a reviewer, or future-you can see live progress in the browser without pulling the repo.

It is a one-way mirror: your local .ssd/ is always the source of truth and drives the issues (create → re-label → close). SSD never reads issue state back to change a workstream — edit an issue on GitHub and the next sync simply overwrites the machine-managed block. There is no two-way reconciliation to get wrong.

Two kinds of issue, mapping onto SSD's existing hierarchy:

SSD conceptGitHub issueLabelTitle
An ADR (a decision) an epic issue ssd:epic [ADR-NNNN] <decision title>
A workstream (the feature implementing it) a feature issue, linked to its epic ssd:feature + one ssd:phase/<phase> <slug>: … — or <slug>#<iter>: … for an iterated workstream

On every phase advance the orchestrator ensures the epic and feature issues exist, swaps the ssd:phase/* label to the new phase, and refreshes a machine-managed block in the issue body. Sync is idempotent by local title-prefix match, and it deliberately distinguishes a gh failure from a genuine empty result — on failure it refuses to create, so a flaky network can never produce duplicate issues.

An iterated workstream syncs under <slug>#<iter>:, so starting iteration B opens a fresh issue rather than re-opening the one iteration A already closed.

Closing Completed in v2.4

Closing is the only outward-destructive action, so it is double-gated: either auto_close: true or an explicit confirmation must be present. When neither is, the sync helper exits with a distinct needs-confirm status, the orchestrator prompts you, and only then re-runs with confirmation. Closing an already-closed issue is a no-op.

An epic close requires two independent answers, and neither party can close alone:

  • "Are all GitHub children closed?" — answered by the sync helper, which discovers children by label query (the ssd:feature issues whose body references Epic: #<n>, matched on word boundaries so #27 never matches #270), not by parsing the epic's task list.
  • "Is another iteration planned?" — answered by the orchestrator reading .ssd/current.yml.

That split is why an epic stays open when its first iteration's feature issue closes but iteration B is still queued.

A companion gate rule, issue-sync-current, watches for mirror drift — see the gate enforcement table. It is informational and SKIPs by default; it FAILs only on a hard contradiction, such as a recorded issue closed while its workstream is still active.

It had almost certainly never passed, until v2.9.0. parse_active_workstreams treated every - line under active: as a new workstream boundary — but rail_deviations, adrs_authored and touches are all documented list fields, so one realistic workstream fragmented into roughly eighteen records. The record carrying issue: had an empty slug, the rule's own guard skipped it, and it emitted SKIP … issue binding(s) present but gh lookups all failed — having made zero gh calls. The parser is now indent-aware: the first list item under active: defines the workstream indent, and only - at that exact indent starts a new one. If you adopted issue tracking before v2.9.0, this rule was reporting a skip it had not earned.
Not available under private mode. Private mode forces this integration off — mirroring workstream state to a public issue tracker is the opposite of running with no paper trail. Enabling both is not an error; the mirror simply never runs.

Enable it in .ssd/project.yml:

integrations:
  - type: github
    enabled: true
    issue_tracking: on     # default off → dormant, zero network calls
    auto_close: false      # default false → prompt once before closing; true → close automatically
Additive actions are automatic; closing is gated. Creating issues and changing labels are low-stakes, so the toggle is your standing consent for them. Closing an issue fans out notifications and is harder to undo, so with auto_close: false (the default) SSD prompts you once before each close — the same "outward + hard-to-reverse → confirm" rule that keeps /ssd ship release-tagging human-gated.
Requires the gh CLI, authenticated. With the toggle off, gh missing, or no GitHub repo resolvable, the mirror is a silent no-op — best-effort by design, so a sync failure (offline, rate-limited) never blocks your SSD work. A project without the toggle behaves byte-for-byte as it did before the feature existed.

Full design notes: ADR-0014 — GitHub issue state tracking.

Sub-Skill Reference

Skill Role in SSD Phase
/ssd-init (v1.11.0) First-run housekeeping: creates .ssd/ tree, writes project.yml + current.yml (v2 schema) + current.notes.yml, runs prerequisite checks. Idempotent. v1.11.0 added Step 6.5 — test-command detection written to a committed .ssd/gate.yml, so tests-pass and feature-flag-present stop SKIPping (ADR-0015); v1.10.0 dropped the developer_profile / teaching_mode defaults (removed in SSD 2.0); v1.3.0 added the v1→v2 prompted migration. prerequisite to all phases
/architect (v1.3.0) Design: models, services, API contracts, ADRs, current-scale baseline. Platform-adaptive (web, iOS, Android, macOS, headless); web guides cover Next.js, Django, FastAPI, Rails, Laravel, Angular, Vue/Nuxt, Spring Boot, ASP.NET Core. Integration has a first-class contract. start, feature
/systems-designer (v1.5.0) Production readiness: reliability, observability, deployment safety. Validates architect spec in Phase 0. Covers AI/LLM integration, compliance & data lifecycle, cost observability, and chaos/failure injection. start, feature, ship
/coder (v1.4.0) Implementation from spec (Python, TypeScript, Swift, Ruby, Java, C#, PHP, Go, Rust, C/C++, Obj-C). Halts if the architect spec omits a feature flag. Spec-drift check amends ADRs. Emits 03-coder-status.md with test/lint/typecheck results. feature
/code-reviewer (v1.7.0) PR gate: BLOCKER/MAJOR findings block merge. Phase 1.5 prior-review follow-up (remediation mode) and Phase 3.5 fix-introduces-edge-cases. Red flags include LLM prompt injection, IntegrityError fetch mismatch, cache-without-race-test, release theatre. Loads examples.md reference. feature, milestone, gate, verify
/codebase-skeptic (v1.5.0) Deep architectural critique through 15 expert voices, of which 2–15 activate per codebase (an inapplicable voice is noise, not signal). Mandatory Phase 2.5 Operational Failure Modes Sweep. Forward-Looking Pass in Phase 4. Incident-Story attestation (Beck), Domain-Modeling Stance (Evans), Deployment-Gate Hardening (Humble). milestone
/feynman (v1.1.0) Epistemic audit, not a structural one: builds a claim ledger of every assertion the project makes about itself — READMEs, ADRs, CI badges, test names, tickets, status reports, and the framing of the request itself as claim C0 — then grades each against evidence on six grades (Verified / Unverified / Unfalsifiable / Misleading / Contradicted / Theater). Also inventories recurring rituals for ones nobody can trace to a decision they changed. Mandatory Phase 7 lean-over-backwards: the report publishes what it did not examine (machine-readable not_examined) and separate executed-vs-read evidence counters. Emits feynman.md with a one-sentence verdict and a posture (Calibrated / Drifting / Self-Deceiving / Cargo Cult). Doctrine: Fooling Yourself. milestone (Step 0.5), verify, audit, pre-ship — proposed, never auto-run
/software-standards (v1.1.1) Adversarial comparative audit. Two modes: Comparative and Adversarial Single. Requires 2–3 evidence citations per /10 score. For vendor selection / legacy onboarding / pre-acquisition — not routine review. audit
/refactor (v1.3.0) Post-ship targeted improvement. Every item cites a specific finding from skeptic-before.md. Step 4.5 Budget Check with halt-and-rollback. Step 5 per-item re-check loop closure. Step 6 systems-designer coordination trigger. Loads patterns.md reference. milestone
/methodology (v1.7.1) SSD doctrine reference — Iron Law, Five Principles, Decision Framework. Provides machine-checkable rule source for /ssd gate. /methodology score emits a self-adherence metric. reference / any phase

Review Tier Selection

Four skills do "review" work. Never chain all four — pick the right tier:

  • /code-reviewer — every PR, always, no exceptions (≤500 changed lines)
  • /codebase-skeptic — milestone reviews and pre-release audits of an owned codebase
  • /software-standards — comparative/adversarial evaluation only (vendor selection, legacy onboarding, pre-acquisition)
  • /feynman — when the question is not "is this good" but "is what we believe about it true": before a release or a status report that will be believed, after an incident that shouldn't have been possible, or when the build is green and you don't feel safe deploying
The first three grade the artifact; the fourth grades the belief. /code-reviewer asks whether a diff is safe, /codebase-skeptic whether the system is well designed, /software-standards how it compares to the field. /feynman asks a question none of them ask — whether the project's own account of itself survives contact with evidence. A codebase can be well designed and thoroughly lied about; a mediocre one can be perfectly well understood. Those audits fail in different ways, so a clean pass from one is not evidence for the others. Its output is also consumable: contradicted claims become structural scope for /codebase-skeptic, confirmed findings feed /refactor prioritization, and since v2.7.0 the feynman-clean gate rule reads its verdict — a report with contradicted or theater claims FAILs the gate. Loudly, not as a wall — see gate enforcement for what overriding a FAIL actually involves, which as of v2.11.0 this guide finally describes correctly.
Skill-overlap priority: when coder and a language-specific coder (e.g. python-django-coder) both apply, the specific one wins. code-reviewer and codebase-skeptic are mutually exclusive on the same scope. codebase-skeptic and software-standards are mutually exclusive. codebase-skeptic and /feynman are coordination, not substitution — both may run at a milestone, and order matters: feynman first, so its contradicted claims become scope for the skeptic.

The Fifteen Voices Roster expanded in v1.5.0

/codebase-skeptic reviews your codebase through the perspectives of fifteen foundational engineering authorities — but it activates only the ones your code earns. A focused greenfield service might activate 3; a distributed monstrosity might activate all 15. Running an inapplicable voice produces noise, so the skill scores activation before it reviews.

VoiceActivates when
FowlerAny architecture decisions are present — always a candidate
Uncle BobObject-oriented or class-based codebase with modularity choices
Liskov newInheritance hierarchies, subtyping, or polymorphism carry behavioral contracts
Metz newOO design where coupling and change-ripple between collaborators is in question
Wirfs-Brock newObjects have responsibilities and collaborations worth assessing; assignment is unclear
BeckA test suite exists, or conspicuously does not
FeathersCodebase is > ~2 years old, has low coverage, or is being modified carefully
EvansBusiness logic is non-trivial; a domain model exists or should
HohpeServices communicate with each other; queues, events, or APIs are present
HumbleThere is a deployment pipeline, or the absence of one is notable
Forsgren newDelivery performance is claimed or measured; DORA metrics, batch size, velocity/reliability tradeoffs
KleppmannData persistence, replication, caching, streaming, or consistency are in scope
JobsThe system is user-facing, or product/API design coherence is in question
WozniakLow-level design, resource usage, clever optimizations, or embedded/systems code
Fournier newMultiple teams or unclear ownership; boundaries mirror the org chart; bus-factor concerns

v1.5.0 added the five marked new, taking the roster from ten to fifteen and widening the activation range to 2–15. The additions cover ground the original ten left thin: subtyping contracts (Liskov), coupling and message-passing (Metz), responsibility-driven design (Wirfs-Brock), empirical delivery performance (Forsgren), and organizational scaling / Conway's Law (Fournier).

The SSD Artifact Tree

Every SSD invocation produces artifacts at well-known paths relative to the project root. Sub-skills read from and write to this tree — that is what lets a session resume, a reviewer verify, and a teammate onboard. As of v1.3.0 of the orchestrator, the working directory is hidden (.ssd/) — the visible ssd/ name collided with the orchestrator skill source directory in the SSD skills repo itself.

<project-root>/
├── docs/                                  # committed decision records
│   ├── decisions/                         # ADRs from architect
│   ├── runbooks/                          # runbooks from systems-designer
│   └── architecture/                      # component diagrams, data models
└── .ssd/                                  # working dir — selective .gitignore (see below)
    ├── gate.yml                           # portable gate inputs: test_command, flag marker   [COMMITTED]
    ├── project.yml                        # language, framework, platform, distribution channel  [ignored]
    ├── current.yml                        # v2 schema: machine-managed workstream state          [ignored]
    ├── current.notes.yml                  # free-form context for next agent / human             [ignored]
    ├── features/
    │   └── <slug>/
    │       ├── 00-brief.md                # epic-level for multi-iter features
    │       ├── 01-architect.md
    │       ├── 02-systems-designer.md
    │       ├── 03-coder-status.md         # — single-cycle features only
    │       ├── 04-code-review.md          # — single-cycle features only
    │       ├── 04-code-review-round-2.md  # — multi-round gate output
    │       ├── 05-deploy.md
    │       └── iterations/                # — multi-iteration features only (opt-in)
    │           └── <iter-id>/             # e.g., 3a, 3b, auth-flow
    │               ├── brief.md
    │               ├── coder-status.md
    │               ├── code-review/
    │               │   ├── round-1.md
    │               │   └── round-2.md
    │               ├── deferred.yml       # carry-over ledger
    │               └── deploy.md
    ├── milestones/
    │   └── YYYY-MM-DD-<topic>/
    │       ├── sha-before
    │       ├── metrics-before.yml
    │       ├── skeptic-before.md
    │       ├── refactor-plan.md
    │       ├── refactor-prs.md
    │       ├── skeptic-after.md
    │       ├── feynman.md                 # — /feynman epistemic audit (also <feature>/feynman.md)
    │       └── verification.md
    ├── audits/                            # [ignored]
    │   └── YYYY-MM-DD-<scope>/
    │       └── standards-report.md
    └── archive/                           # closed feature + milestone directories  [ignored]
Selective commit (v1.18.0+, ADR-0008). .ssd/ is no longer ignored wholesale. ssd-init writes a selective .gitignore by default: durable design artifacts — briefs, architect specs, code reviews, deploy notes — are committed so a feature's provenance travels with the repo, while machine state (current.yml, project.yml, init-log.md, archive/, audits/) stays local. The one config file that crosses the line is .ssd/gate.yml (v2.5.0+, ADR-0015) — committed on purpose, because gate inputs are useless if they can't travel to a second clone or a CI runner. The no-leaky-state gate rule enforces the split, and an optional pre-commit hook catches violations before they land. Solo developers who prefer the old behavior can opt back in with ssd-init --keep-blanket-gitignore (sets project.yml.ssd.gitignore_mode: blanket).

There is now a third mode. gitignore_mode takes selective (the default), blanket, and — since v2.8.0 — private, which tracks nothing SSD produces. See Private Mode.
Action required if you adopted selective mode before v2.6.0. The allow-list was inert. .ssd/* matches depth-1 children only, so once !.ssd/features/ re-included the directory, every file beneath it was committable and the fifteen artifact patterns in the allow-list constrained nothing — a stray secrets.env under a feature directory sailed straight through, and no-leaky-state's fixed baseline would not have caught it. v2.6.0 adds .ssd/features/** and .ssd/milestones/** deep denies plus directory re-includes, which is what makes the list load-bearing. Run /ssd upgrade --apply to receive it — the strict-selective-gitignore migration only touches projects that already carry the selective block, and is idempotent.

The same bug hid a second one: code-reviewer's declared milestone output review-<pr>.md had never been in the allow-list, and those artifacts were only ever committable because the list wasn't doing anything. !.ssd/milestones/**/review-*.md and !.ssd/milestones/**/feynman.md are now explicit.

Every primary output carries YAML frontmatter (skill, version, produced_at, scope, consumed_by). Review outputs add finding_counts and a computed gate_pass. Design outputs add a deliverables block. This is what makes /ssd gate mechanically checkable and milestone verification a frontmatter diff rather than prose reconciliation.

current.yml v2 carries schema_version: 2 with per-workstream slug, phase, iteration, budget_hours, elapsed_hours, gate_rounds, rail_deviations, and blockers. The free-form sidecar current.notes.yml holds anything that doesn't fit the schema (handoff notes, scope changes, open questions). Legacy v1 files are read in compatibility mode with an opt-in prompted migration that writes current.yml.bak first — no silent rewrites.

Session Continuity

On invocation, /ssd reads .ssd/current.yml + .ssd/current.notes.yml. Each active workstream carries a budget in hours. The orchestrator flags entries that are over budget ("suggest scope reduction, not more work") and entries last-touched more than 3 days ago ("stale work that may need a fresh audit"). Closing a workstream archives its artifacts under .ssd/archive/features/<slug>/; matching notes move to .ssd/archive/features/<slug>/notes.yml so historical context stays with the work.

Private Mode Since v2.8.0 · opt-in

SSD can run with no paper trail in git. For client work, a shared repo where SSD is your personal practice rather than a team standard, an OSS contribution, or a project whose working notes are nobody else's business.

/ssd-init --private

This sets gitignore_mode: private and writes a .gitignore that tracks nothing SSD produces:

 What happens
GitignoredAll of .ssd/including .ssd/gate.yml — plus docs/decisions/, docs/runbooks/, and docs/architecture/
Also suppressedThe add- branch prefix (branches become plain {slug}), GitHub issue tracking (forced off), and the CLAUDE.md SSD section
KeptThe 🛠️ Crafted with SSD commit and PR footer

Every rail step and every gate rule still runs. Privacy is a storage and visibility posture, never a reduction in rigor. no-leaky-state becomes more load-bearing here than in any other mode, since it is what enforces the boundary.

What "private" does and does not mean

  • Untracked, not encrypted. Artifacts sit in plaintext on disk.
  • Not anonymous. The attribution footer is deliberately kept — anyone reading commit trailers can still tell SSD was used. Privacy here means no SSD mechanics or documentation in the tree.
  • Cannot un-publish history. Switching an existing project stops future tracking. git rm --cached does not rewrite what is already pushed.

Retrofitting an existing project Since v2.9.0

/ssd upgrade --apply private-mode            # DRY RUN — shows everything, changes nothing
/ssd upgrade --apply private-mode --confirm  # applies

This is the only operation in the upgrade engine that can remove anything from git, so it inverts the engine's normal behavior deliberately: --elect mutates nothing and exits 10 (needs-confirm). Only --confirm acts.

The dry run lists every tracked path under .ssd/ and the three SSD docs/ trees — never truncated — and separates files SSD demonstrably produced from files it cannot confirm it produced, so a doc your team owns is never quietly untracked. It states plainly that untracking stops future tracking but does not rewrite published history.

What the retrofit cannot undo. History is not rewritten, files are untracked rather than deleted, and there is no inverse migration — moving back out of private mode is not automated. Branches of workstreams created before the switch are not renamed, so a project can carry both add-slug and slug branches.

An elective migration — a manifest entry that is not drift

Until v2.9.0 every entry in the migration manifest meant one thing: something this project has drifted past. Private mode is a choice, not a convention, and the manifest had no vocabulary for that. Implemented as an ordinary migration it would have been actively harmful — a mechanical entry would have let a routine /ssd upgrade --apply untrack a team's committed ADRs, and a guided one would have nagged every project forever about a posture most should never take.

The new elective: true field excludes an entry from the default sweep entirely: never listed, never PENDING, never applied by --apply, and not a participant in recorded-version advancement. It is deliberately orthogonal to kind rather than a third kind value — kind answers how an entry is adopted, elective answers whether every project should. Absent means false, so every pre-existing entry is unaffected.

Trade-offs, stated plainly

  • Gate config does not travel. No committed .ssd/gate.yml can exist, so test_command and feature_flag_marker live in gitignored project.yml and do not reach a second clone or a CI runner. This knowingly reopens the root cause that gate readiness was built to fix — a cost proportional to your number of collaborators, and private mode's premise is that there are none.
  • adr-delta and feynman-clean use a weaker probe. ADRs are untracked, so they cannot appear in a diff. Both rules fall back to inspecting the working tree, and say so in their output. Without that fallback adr-delta would deadlock against no-leaky-state and make the gate unpassable.

selective and blanket projects are unaffected — every change sits behind a private branch in the engine.

The Private Artifact Store Since v2.10.0 · opt-in

Private mode solves visibility — nothing SSD produces appears in the project's git history. It creates a second problem in doing so: your entire methodology record becomes untracked files on one machine. No history, no branches, no backup, one rm -rf from gone. The store solves durability without giving up the privacy.

.ssd becomes an absolute symlink into a per-project subdirectory of one separate private git repository. You keep writing to .ssd/; the bytes land somewhere version-controlled that the project cannot see.

/ssd store status                    # is this project linked? any drift?
/ssd store init  ~/private-ssd       # create/prepare the store repo (idempotent)
/ssd store link  ~/private-ssd       # DRY RUN — lists every file it would move
/ssd store link  ~/private-ssd --confirm
/ssd store commit                    # LOCAL only — never pushes
/ssd store push                      # explicit, outward, never automatic
  
LayoutOne store repo, one subdirectory per project: ~/private-ssd/<project>/
Requiresgitignore_mode: private or blanket. Not selective — git cannot track files through a directory symlink at all, so a selective project would silently stop committing the artifacts it means to commit. link refuses outright rather than half-working
Configstore_root, store_dir, store_auto_commit in project.yml
Consumer changesNone. Every tool reads .ssd/<path> and the kernel resolves the link

link is dry-run by default and exits 10 (needs-confirm), because it is the one operation in SSD that moves your artifacts. It prints the complete list first, verifies a moved file reads back correctly through the link before declaring success, and never deletes the source on a cross-device fallback.

The leak the obvious implementation would have shipped

Worth stating because it is a general git fact, not an SSD quirk: a trailing-slash .gitignore pattern matches directories only, and to git a symlink is a file. So the natural .ssd/ ignore line does not match a symlinked .ssd. Git would commit the link as mode 120000 with the absolute target path as its content — publishing your home directory layout into the very repository you were keeping private. The gate's leak detector was blind in the same way, for the same reason.

The fix is three independent layers: a bare .ssd line (no slash) in the private pattern file, an exact-match entry in the gate's deny-list, and the store-link-sane rule below. That bare line must never appear in the selective pattern file — a bare pattern excluding a directory makes every ! re-inclusion beneath it inert, which would silently destroy selective mode's entire allow-list.

Not built, and stated rather than implied. Worktree fan-out (several worktrees of one project sharing a store), an unlink verb to move back out, and a migration-manifest entry so /ssd upgrade can offer this to an existing project. The store is also the newest thing in the library and the least exercised outside its own tests — treat it accordingly.

Methodology-Backed Gate Enforcement

As of v1.4.0 of the orchestrator, gate enforcement is an executable shell script, not LLM-internal checks. Before /ssd gate passes, the orchestrator invokes:

bash methodology/gate-rules.sh --base <base-branch> --json

Each rule emits PASS | FAIL | SKIP. Any FAIL exits non-zero and the gate refuses to pass. The same script is invocable from CI for parity.

Invoking it by hand. The path above is relative, which only resolves when the SSD library happens to sit at your repo root (as it does in the skills repo itself). In a normal project the library lives where you installed it, so call it by that path from inside your checkout — the script derives the project root from git rev-parse --show-toplevel, so it reads your project's .ssd/gate.yml regardless of where the script itself lives:
bash ~/.claude/skills/methodology/gate-rules.sh --base main --json
The same relative-path assumption is why frontmatter-valid and skill-version-sync SKIP in most projects: both look for the validator under the project root. To activate them in CI, vendor frontmatter-validate.py together with its schemas/ directory into your repo — the script resolves schemas relative to itself, so the two must travel as a pair.
Its workstream parser was rewritten in v2.9.0 — before that it could not pass.
Rule (script)What it checksSource
wip-commitsgit log <base>..HEAD --grep='WIP|checkpoint|TODO.*tomorrow|FIXME.*later' -i is emptycore.md §4
tests-passProject's test_command exits 0. Input resolved .ssd/project.yml.ssd/gate.yml (see below)core.md §1
feature-flag-presentProject's feature_flag_marker appears in non-doc changed files (skipped for doc/config-only diffs). Same input fallbackcore.md §3
adr-deltaIf architectural diff > 200 lines outside test/doc/migration scope, docs/decisions/ has a new or modified ADRcore.md §2
frontmatter-validChanged .ssd/features/ + .ssd/milestones/ artifacts carry schema-valid YAML frontmatter. Since v2.6.0 the PASS line reports the unvalidated count too (N artifact(s) validated against schemas; M unvalidated (no matching schema)). v2.11.0 added schemas for briefs and deploy logs — the two largest unvalidated classes, and in the deploy log's case the artifact that records what actually shipped. Seven types now validate; milestone artifacts (skeptic reports, refactor plans, verification records) still pass through unchecked and say so. v2.11.0 also fixed a message that reported "no SSD artifacts in scope" for a change set whose artifacts the validator had seen and had no schema for — a false statement that stood for four releases. SKIPs cleanly without python3 + PyYAML, or where the validator isn't reachable from the project rootADR-0006
rails-walked new in v2.11.0The first rule that checks a rails invariant rather than hygiene. When a change set bumps VERSION — i.e. claims to be a release — every .ssd/features/<slug>/ directory it touches must contain a code review whose frontmatter says gate_pass: true. Deliberately release-scoped: you commit a brief long before a review exists, and a rule that demanded one on every push would be switched off within a week. Only code-review*.md / round-*.md artifacts count — feynman.md also carries gate_pass:, and a passing epistemic audit is not a code review. Blind spots, stated plainly: a release touching no feature directory is unchecked, and the rule does not verify the review belongs to the iteration being shippedADR-0003 · rails invariant 4
store-link-sane(v2.10.0+) When .ssd is a symlink into a private artifact store, the link must be safe: gitignored, not tracked, target present, content reachable through it, mode not selective, and project.yml in agreement. Every failure mode is a FAIL, never a SKIP — each one is a silent leak or a data-loss path. SKIPs cleanly for every project whose .ssd is an ordinary directoryADR-0018
no-leaky-stateMachine state (current.yml, project.yml, archive/, …) isn't staged for commit under the selective-gitignore split (SKIPs in blanket mode)ADR-0008
skill-version-syncVersion banners in SKILL.md example blocks match the skill's declared version — relevant when you author or fork skills; SKIPs where there are no example blocks. Since v2.6.0 the PASS line reports how many skills were structurally exempt (no example block to check) rather than implying full coverageADR-0009
migration-manifest-currentThe migration manifest itself is structurally healthy — required fields per entry, unique ids, append-only ascending introduced_in, nothing newer than VERSION. SKIPs in every project except the skills-library repo, so a PASS is not a statement that your project has no unapplied migrations — run /ssd upgrade for thatADR-0013
feynman-cleanAny feynman.md in the change set reports zero contradicted and zero theater claims — the project's own account of itself survived contact with evidence. Reads the counters, not gate_pass (a rule cleared by flipping one boolean would let the report judge its own verdict) and frontmatter only, never the body (report prose legitimately contains lines that look like counters). SKIPs when no audit is in scope — not running /feynman is not a violation — so a PASS means "no failing audit in this change set", not "this project's beliefs are calibrated"ADR-0016
issue-sync-currentInformational. FAILs only on hard GitHub mirror drift — a recorded issue closed while the workstream is active, or a phase label that disagrees with local phase. SKIPs by default (tracking off, no gh, or no issue binding); checks bindings before any network callADR-0014

Two rules read their inputs through a fallback chain (gate_input(), added in v2.5.0): .ssd/project.yml first — your machine-local override — then the committed .ssd/gate.yml, the portable floor every clone and CI runner sees. That is what makes tests-pass and feature-flag-present live rules rather than permanent SKIPs, and what lets the same gate-rules.sh invocation behave identically on your laptop and in CI. See Gate Readiness above.

A SKIP is not a pass. Rules SKIP when their preconditions genuinely don't apply — no diff to inspect, an optional integration switched off, a missing optional dependency. Five SKIPs and one PASS is a much weaker signal than six PASSes, and it usually points at configuration you haven't finished. Run --rules <name> to isolate a single rule while debugging.

As of v2.6.0 the gate says that out loud. Reading the exit code alone was the whole problem: a gate exits zero whether every rule passed or half of them passed and half skipped. Every human-readable run now ends with a census, and --json carries the same numbers as pass_count / skip_count alongside the existing fail_count:

# the SSD skills library's own gate on the v2.11.0 release branch
GATE 8 pass · 3 skip · 1 fail — a skip is a check that did not run

Expect skips, and expect more of them in a normal project than in the library repo — a small feature leaves adr-delta below threshold, migration-manifest-current applies only inside the library, and the two validator-backed rules need vendoring. The point of the line is not to drive the skip count to zero; it is that you can no longer read a green gate without seeing how much of it ran.

Turn a permanent skip into a recorded decision. Some rules will never apply to your project, and that is fine — what isn't fine is being unable to tell that case apart from configuration you forgot. The convention (v2.6.0) is to say so in .ssd/gate.yml, next to the key you deliberately left unset:

# feature_flag_marker is intentionally unset. This repo ships markdown skills and
# bash/python enforcement — it has no runtime feature-flag system to grep for, so
# `feature-flag-present` SKIPs by design rather than by omission.
# feature_flag_marker: <marker>

This is a convention, not a mechanism — nothing parses that comment and no rule changes behavior because of it. The census still counts the skip. What changes is that the next person to read a 5 pass · 4 skip line can tell which skips were chosen and which were merely inherited, which is the whole difference between a known gap and an unknown one.

That line, and the per-rule coverage counts above it, came out of a /feynman audit pointed at the SSD library itself. frontmatter-valid reported "51 artifact(s) validated" while 34 artifacts had no matching schema at all; skill-version-sync reported "9 skill example(s) match" while two skills — including /ssd, the orchestrator — were structurally exempt. Neither line was false. Both were misleading, which is the grade that does the most damage precisely because the sentence is true.

Enforcement is warnings, not walls (SSD 2.0, ADR-0012). A "hard rule" means strongly discouraged and loud when broken: the gate surfaces the violation unmissably and exits non-zero, rather than the system physically locking the merge. SSD trusts the developer; it does not lock the door. The one thing it never does silently is advance a phase without surfacing the decision.

Correction, v2.11.0 — there is no override mechanism, and this guide used to say there was. Four documents in the library, and this page, described /ssd ship --force as a logged override leaving a durable rail_deviations trace. Neither half was ever implemented: no script accepts --force, and rail_deviations: has never been written by any tool in the library's history. An epistemic audit established both by execution, and the claim was struck rather than softened. What actually happens when you override a red gate is that you merge it deliberately and write the reason down by hand in the deploy log's rail-deviations table. Building the real mechanism is tracked work, not a shipped feature.

See ADR-0005 for why the rules run as a bash script rather than orchestrator-internal LLM checks.

Hard Rules

1. No merge without a clean /ssd gate

No BLOCKER or MAJOR findings from the code-reviewer. No exceptions.

2. No incomplete work on main without a feature flag

WIP commits on main are banned. Use a feature flag or a local stash.

3. Tests must pass before and after every change

"I'll fix the tests tomorrow" is not a shippable state.

4. Refactor only after shipping

Separate PRs, never mixed with feature work. Milestones run after shipping, never instead of it.

5. Deploy beats perfection

Reduce scope rather than delay a deploy.

6. Production parity from day one

If you haven't deployed to production yet, that is your next task.