InsanelyGreat's Shippable States Development

The complete guide

A harness for agentic coding. The model proposes. Executable gates, grants outside the repository, and a human ship command enforce the rest. You still keep a deployable state, from one person to a large org.

Library 3.0.0 on main, checked at d6f23bd, untagged.  ·  /ssd v3.0.0  ·  /ssd-init v1.15.0  ·  /feynman v1.2.0  ·  how 3.0 works

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.

SSD 3.0

Agentic coding moved from prompt engineering to harness engineering. SSD 2.x put the rail in the skill text and asked the model to follow it. SSD 3.0 runs the rail. The default is still propose. Shipping stays a human action. There is no --force.

The harness is concrete. Grants for consequential actions live outside the repository, one use, bound to a snapshot, and a worker cannot mint one. Gate rules and schemas execute. .ssd/ keeps the record, doctor reads it, and resume continues it without restoring a grant. Three role files describe design, code, and review so the reviewer cannot edit the diff. A host has not dispatched them. Hooks reinforce that wall in process tests. They are not the authority, and a Claude Code session has not delivered an event. The offline eval harness records what ran. The live driver is wired and has not called a model. The clone install stays, and a plugin manifest sits beside it. AGENTS.md carries the contributor guide. A user project receives instructions only with consent. A session load of that file was not measured.

The rail, as AGENTS.md and the project-instruction block state it, is brief → design → code → review → gate → ship. ssd/rails.md still lists deploy, rollout-advance, and flag-removal after the gate. Those steps, and ship, sit outside an unattended run. Live model comparisons, the five-user pilot, and in-session instruction loading are NOT_RUN. A result that was not produced is not a pass. That is the same demand as True As Of: name the regime, the test, and the date. The short argument is on the SSD page. The exact boundary is what’s in 3.0, and what is not.

On main, untagged. Checked against commit d6f23bd. Pull request #58 merged to main on 12 October 2026 as squash commit c6dd481. Those two commits have the same tree. VERSION is 3.0.0. There is no v3.0.0 tag and no GitHub release. A clone of main prints 3.0.0. The tag, the GitHub release, and marketplace submission remain owner actions. The 2.x sections below stay. The migration note is From 2.x.

Install

Clone installation is kept:

git clone https://github.com/AlexHorovitz/skills ~/.claude/skills
cat ~/.claude/skills/VERSION

That clone follows main. main is library 3.0.0, so VERSION prints 3.0.0. The tag and the GitHub release are still unpublished. The tree this page describes is commit d6f23bd, which matches main as of 12 October 2026.

The same tree carries a plugin manifest. .claude-plugin/marketplace.json names the marketplace ssd. .claude-plugin/plugin.json names the plugin ssd at version 3.0.0, lists the skill directories, and has no bin/. The Claude Code commands that match those names are:

/plugin marketplace add AlexHorovitz/skills
/plugin install ssd@ssd

The release record does not include those two commands being run. Marketplace submission and a clean-host install were not run. The owner ran claude plugin validate . on a Mac with Claude Code installed. The result was “Validation passed with warnings.” Both warnings are addressed in the tree: the marketplace has a top-level description, and checkout memory lives at .claude/CLAUDE.md (@../AGENTS.md) because a CLAUDE.md at the plugin root is not loaded as plugin context. The implementation environment had no claude binary and did not re-run the command. Until you install the plugin yourself, use the clone path. Record: plugin-validate.md.

On a host that namespaces plugin skills, names look like /ssd:coder. The clone path keeps the short names (/coder). Both installs can be present. Deleting one is not required to finish the other.

AGENTS.md is the contributor guide for the skills repository. A checkout of that repository loads it from .claude/CLAUDE.md. Plugin users get the rail from the skills, especially ssd/SKILL.md. The block a user’s project receives, and only with consent, is ssd-init/references/project-instructions.md, inside a managed block. Whether a session actually loaded AGENTS.md was not measured.

Every library SKILL.md starts with Agent Skills frontmatter: name matches the directory, and description is at most 1,024 characters. The title and the **Version:** banner follow that block. ssd-init, feynman, software-standards, and codebase-skeptic set disable-model-invocation: true. A person invokes them by name. The portable export strips that field; the body sentences stay. The skill-metadata gate rule runs the checker only inside this library and SKIPs everywhere else (ADR-0021).

From 2.x

Bare /ssd still reads state and proposes the next action. An absent autonomy: block still makes no extra call. There is still no --force.

/ssd upgrade can apply the format-marker migration. It writes .ssd/format containing 3 when that file is absent, and it refuses to overwrite a marker that is not major 3. An absent marker means an older project is left unchanged. Libraries released before 3.0.0 do not read the file, so a v2.14 binary will not refuse on its own. Rollback is the backup migrate.sh writes before it mutates state. The migration does not rewrite workstreams, store links, or records (ADR-0024).

Optional v3 frontmatter fields (reviewed_snapshot, execution_method, result_status, provenance) are optional. Historical artifacts that omit them stay valid.

Doctor and resume

/ssd doctor is read-only. From the library root:

python3 methodology/ssd_cli.py doctor

Resolve that script from the SSD library, not by searching the project for a copy. Doctor reports the installation source and version, duplicate skills, the library, project, worktree, and storage roots, state health (missing, valid, corrupt, or unreadable), required dependencies, and a capability report of supported, unsupported, or unknown. Each capability names its evidence. Doctor does not install dependencies, edit permissions, repair state, or open a network connection. A corrupt state file is corrupt. It is not an empty project. A requested run mode the report cannot support is reported as effective mode propose. The configuration file is left as you wrote it.

/ssd resume <run-id> continues the same autorun record:

bash methodology/autorun.sh resume --run-id <YYYY-MM-DDTHHMMSSZ>-run.md

Resume does not reset consumed transitions, does not restore a shipping grant, and does not invent a finish when stop_reason is null. A worker that may still be alive is FM-2. A conflicting fingerprint stops with STOP-4. Clearing the lock and starting /ssd run again is a new run with fresh budgets, not a resume.

Setup, managed blocks, and removal

/ssd-init inspects before it writes. The plan command is:

python3 methodology/ssd_cli.py setup-plan --project <root>

The plan lists files that would be created or kept, the storage mode, discovered test commands, and optional permissions that stay off until you consent (git hook, GitHub issue mirror, private mode). It returns writes: 0. A discovered test command is named. It is not executed. Writing happens after you approve the list.

Text SSD adds to AGENTS.md or CLAUDE.md sits in a managed block:

<!-- ssd:managed id=project-instructions -->
… SSD text …
<!-- /ssd:managed -->

A merge replaces only the named block. Existing text outside it stays. An unclosed block is refused rather than guessed. Removal strips those blocks and deletes .git/hooks/pre-commit only when that hook is a symlink to methodology/hooks/pre-commit-no-leaky-state.sh. Removal keeps .ssd/, your source, and a foreign hook or a regular-file hook.

Roles, delegation, and hooks

Three role files describe subagents. The host did not dispatch them in the release checks, so observed delegation is NOT_RUN.

FileTools the file requestsWhat it must not do
agents/ssd-architect.mdRead, Grep, GlobWrite the artifact, widen the objective, or ship
agents/ssd-coder.mdRead, Grep, Glob, Edit, WriteShell is not in the list. A test it did not run is NOT_RUN. It does not certify the gate
agents/ssd-reviewer.mdRead, Grep, GlobNo Bash, Write, or Edit. It does not write gate_pass

The delegation envelope schema matches only a file named delegation-envelope.yml. It is not a rail artifact. A schema-valid envelope is not authorization and is not a gate result (ADR-0022). Tests that must run go through methodology/ssdlib/executor.py: unshare user, mount, and net namespaces, then chroot. If that mechanism is missing, the result is NOT_RUN and strong-assurance review is unsupported. Inline review is labeled inline.

hooks/hooks.json calls methodology/hooks/ssd_hook.py for PreToolUse on Bash, PostToolUse on Write, Edit, and Bash, and for SessionStart, Setup, and Stop. The hook is an adapter around the authorization boundary. An empty or malformed payload grants nothing. A PreToolUse command-hook timeout does not block, which is why the control directory remains the authority. Post-tool output is feedback. Process tests of empty, malformed, deny, post-tool, and stop payloads passed. Claude Code did not deliver an event, so host delivery is NOT_RUN. The older git pre-commit script under methodology/hooks/ is a separate opt-in symlink. It is not this adapter.

Shipping grants

The default autonomy mode is still propose. /ssd ship is the human’s command. There is no --force flag. A missing autorun lock is not approval.

Grants for ship, deploy, publish, release, tag-default, rollout-advance, and flag-removal live outside the repository: SSD_CONTROL_DIR, or ~/.ssd-control/ plus the first 16 hex characters of the SHA-256 of the project path. The control directory must be outside the project. A grant is bound to the action, the repository path, and the snapshot. One use is the default. A different snapshot does not match. SSD_WORKER=1 cannot create a grant and cannot consume one. An in-repo file that says approved: true, including .ssd/control/grants.yml, is not consulted. If the guard is missing, the check fails closed (ADR-0023).

Unattended run is certified only for that jail plus the external control directory, on the Linux host that ran the suite. On any other path, doctor reports the gap and the effective mode stays propose. Your configuration is not rewritten to hide that. --until ship is still FM-4.

What was actually checked

These commands were run while preparing the release. The record is docs/releases/3.0.0/evidence.md.

CommandResult
bash scripts/parity-test.sh384/384, exit 0
python3 scripts/v3_suite.py101 pass, 0 fail, 3 NOT_RUN
python3 scripts/live_eval_test.pyexit 0, fake runtime, no model call
shellcheck -S warning methodology/*.sh scripts/*.shexit 0
bash methodology/gate-rules.sh --base main10 pass, 4 skip, 0 fail

Commit d6f23bd is the CI follow-up from pull request #61, and it changes only the workflow and the way a failed unit test is printed. The v3 suite job checks the repository out with fetch-depth: 0. A depth-1 checkout omits the pinned arm A baseline e732a93, and scripts/live_eval_test.py then fails real-baseline-is-present. A missing baseline stays NOT_RUN and is not copied from HEAD. When that unit-test process fails, scripts/v3_suite.py prints the lines that start with FAIL and SUMMARY. On d6f23bd all four checks completed successfully: SSD gate rules, shellcheck, gate-rules parity test, and the SSD v3 deterministic suite. The workflow still reports. It does not add required status checks. The counts in the table are the ones written in evidence.md. That file was not edited by the CI commit, and this page did not re-run the suite.

The three suite rows that are NOT_RUN are in-session instruction loading, live model repetitions, and the five-user pilot. The pilot protocol is written in usability.md. No pilot session was held.

scripts/eval_driver.py live can call Claude Code headlessly (claude -p, non-bare, so skills, hooks, and project memory can load) or the Anthropic Messages API. It checks SSD_EVAL_SPEND_CEILING before each call and during a Claude Code stream. Unknown cost stays unknown, and later calls do not start. --dry-run prints the plan and does not call a model. Held-out fixtures are not planned. Arm A stays on baseline e732a93 and is not replaced with HEAD. No live model was called while preparing this release. The runbook is live-evals.md.

NOT_RUN is not a pass. A skip is a check that did not run. A gate result names the snapshot it checked. Schema validity is not a gate result. The portable bundle is labeled format-validated only; nobody installed it on a host as part of this release, and its unattended-run flag is false.

Deferred: T26–T33

These optional packages were not started. They are not registered as tools, and this page does not describe them as present (tasks.md, ADR-0024).

TaskNot in 3.0.0
T26Coded workflows
T27Secondary verification of disputed findings
T28Specialist fan-out. The reviewer does not fan out into a milestone audit
T29Cloud routines
T30Usage export beyond unknown token counts
T31MCP
T32A Spec Kit adapter
T33Retiring the clone install. No deprecation date is set

Tagging, a GitHub release, marketplace directory listing, a clean-host plugin install, the five-user pilot, and the live eval baseline are not done yet. No --force, no automated ship, and CI stays informational: those three are excluded by design. The full inventory, with each item in one of those categories, is what’s not in 3.0. Owner actions are listed in owner-actions.md.

Claude Code Skills

A clone of main prints library 3.0.0. Check it with cat ~/.claude/skills/VERSION. Install, doctor, grants, and the evidence table are in SSD 3.0. The changelog at the commit this page checked is CHANGELOG.md.

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. Bare /ssd reads state and proposes the next action. It does not implement, and it does not start an audit. The full skill set is free for personal and internal organizational use — github.com/AlexHorovitz/skills.

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, /feynman On-demand, or proposed by SSD. feynman, software-standards, and codebase-skeptic start only when a person invokes them
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)
# production_runtime: true       # v2.12+ — does this project have a real production runtime?

production_runtime (v2.12.0) is declared, not inferred. Rail step 2 (systems-designer) applies “for projects with real production runtime”, and for years that judgment lived nowhere, so projects it did not apply to filed thirteen near-identical paragraphs under Rail deviations — each one citing the document that sanctioned the skip. A step that never applied is scope, not a deviation. Declaring it here (committed, so it reaches CI and a second clone) is what lets deviations-recorded tell the two apart. Absent, it is assumed true: a project that has not said otherwise is treated as having users.

And then the declaration was tested, which is the part worth reading. The very next feature ran /ssd systems-designer anyway — and it came back blocking, with three findings, two of which the architect spec did not contain. The declaration held for half its reach: deployment checklists, load tests and capacity models were correctly out of scope; an untrusted string reaching a YAML writer and a lost-update race on a state file were not. The condition turned out to be on the wrong axis. “Does this project serve users?” is not what determines whether the pass earns its keep — “does this change touch state that can be corrupted, or input that can be forged?” is. A skills library answers no to the first and yes to the second. So rail step 2 has now run exactly once in the library's history, and it blocked a ship — the strongest available evidence that the step is not theatre, arriving immediately after it was declared out of scope.

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)
/ssd run <slug> (v2.14+, opt-in) Walk the rails unattended to a ceiling of gate — see The Autonomy Ladder. In 3.0 that path is certified only for the tested jail; see grants
/ssd doctor (3.0) Read-only installation, state, and capability report. Writes nothing. See Doctor
/ssd resume <run-id> (3.0) Continue an interrupted run on the same record. Does not restore a shipping grant. See Resume

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 rail in the 3.0 instructions is brief → design → code → review → gate → ship. That is the sequence in AGENTS.md and in the project-instruction block. ssd/rails.md still lists eight steps, and the three after the gate are deploy, rollout-advance, and flag-removal. Ship and those three are outside the ceiling of an unattended run. 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. That last sentence was aspirational until v2.13.0: measured across 15 workstreams, the field had never once been written by anything. methodology/deviation.sh writes it now, and the deviations-recorded gate rule reads it — they shipped together on purpose, because a writer nothing reads decays into the same silence. An auto-run writes zero of them, by construction. 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-skeptic → skeptic-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.15.0) First-run housekeeping: creates .ssd/ tree, writes project.yml + current.yml (v2 schema) + current.notes.yml, runs prerequisite checks. Idempotent. v1.15.0 shows the setup plan and writes only after you approve it. 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.1) 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.6.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.1) 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.9.0) PR gate: BLOCKER or MAJOR findings send the work back. The 3.0 entrypoint requires findings with location, evidence, and verification status. A gate_pass boolean in the review file is a legacy mirror; certification ignores a model-supplied gate_pass. The long v2.14 checklist is in references/archive-v2.14.md. feature, milestone, gate, verify
/codebase-skeptic (v1.6.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.2.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.2.0) 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.1) 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.10.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
Banners at d6f23bd. A skill banner is that skill’s own version, not a copy of the library VERSION, except ssd/SKILL.md, which tracks the library version at the last edit of that file (ADR-0009). Five entrypoints that used to exceed 400 lines — ssd-init, systems-designer, code-reviewer, feynman, and software-standards — keep the previous text in references/archive-v2.14.md. The entrypoint is the current contract. Whether a model read those archives was not checked.

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,
    │                                       #   production_runtime (v2.12+)                     [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
    │       ├── auto-runs/                 # — /ssd run + advance records (v2.14+)  [COMMITTED]
    │       │   └── <timestamp>-run.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.

The Autonomy Ladder Since v2.14.0 · opt-in

Everything above describes an orchestrator that proposes and waits. That is still the default, and the default is still what most projects should run. As of v2.14.0 (ADR-0020) there are two opt-in rungs above it, in which the orchestrator may execute its own proposal — and a wall above those that is not configurable.

The one-line version: delegate walking the rails between the two decisions that bound a feature — the brief at the start and the ship at the end — with the executable gate as referee and a written record of every step.

RungWhat bare /ssd doesPhases per invocationHow you get it
proposeproposes the next action; waits0the default — and the default is absence
advanceexecutes its own top proposal when it is unambiguous≤ 1, unconditionallyautonomy.mode: advance
runoffers /ssd run, which walks to a ceiling≤ budget_transitionsautonomy.mode: run, or /ssd run per-invocation

With no autonomy: block in .ssd/project.yml, the orchestrator makes no additional call at all — not a call that returns the default, no call. That is what makes “byte-identical to v2.13.0” a property you can check rather than a reassurance you have to accept.

Why the feature has this shape

The highest-frequency, lowest-judgment interaction in a mature SSD project is the coder → reviewer → coder loop until the gate passes. Every ingredient of a safe agent loop already existed: the gate is executable, sub-skill outputs carry machine-readable frontmatter, and the rails are a named canonical sequence. What was missing was a sanctioned posture in which the orchestrator may act on its own, and a durable record of what it did unattended.

A feature is bounded by two decisions that are irreducibly human: the brief — what are we building, and is it worth building — and the ship — is this good enough to put in front of people. Everything between them is mechanical and refereed by a script. Autonomy delegates the middle and neither end. That is the whole design, and the rest of this section is the consequences of taking it seriously.

Rule-zero forbids silence, not autonomy

The one genuinely inviolable rule in the library is that the orchestrator never advances a phase without surfacing the decision. The reading that would forbid this feature entirely is “surfaced” = “a human approved it.” That is not what the rule says, and the distinction is load-bearing. Under a rung, surfaced means announce → log → act, in that order:

  1. Announce — the transition is narrated, naming the lowered command (→ /ssd code auth-flow), so an attended user sees it before it happens.
  2. Log — the transition is appended to a durable record on disk before the phase runs.
  3. Act — and only then.

The ordering is the whole claim. Announce-then-act is a courtesy that evaporates when nobody is watching — which is the only condition under which this feature gets used. Log-then-act means state lags reality by at most one announced step, and that is precisely what makes recovery from an orchestrator death possible at all. The orchestrator acts only on a success response from the logger, so a failed write aborts the act.

The delegation wall

--until accepts design, code, review, gate. ship, deploy, rollout-advance and flag-removal are refusals, not settings, at every rung. There is no configuration key that moves this line.

This is not a retreat from “warnings, not walls.” Pillar 5 says SSD trusts the developer and does not lock the door. The developer’s door is untouched — /ssd ship <slug> typed by a human works exactly as it did before. What is walled is an agent’s ability to walk through it unattended, which is a different door and has never been open. Trusting the developer and trusting an agent are not the same decision, and the point of the wall is that nobody converts one into the other by accident.

What an auto-run can and cannot do

Read this before enabling run, because it is the part with real consequences. Under propose, a human reads every proposal before any sub-skill runs. That human has been an unacknowledged security control, and /ssd run removes them for up to budget_transitions consecutive phases. Naming it is the honest way to offer the feature.

At worst, an auto-run canAn auto-run cannot
Write code; write SSD artifacts; commit to a feature branch; fail a gate; consume tokens and wall-clock up to its budgets — leaving a written record of every stepShip, deploy, advance a rollout, remove a feature flag, write a rail_deviations entry, leave the rails, start while another run is in flight, or run without a record

None of those refusals has a bypass flag, and the absence of a bypass flag is the feature. This library already shipped one override that existed only in prose — see the correction in gate enforcement below — and a bypass added for convenience is how you get it back.

Why an auto-run records zero rail deviations

A deviation is engineering judgment captured for the record. An agent that can record its own deviations can rationalize its own shortcuts, and a record whose entire value is that a human decided something and wrote down why becomes a log of the agent agreeing with itself.

So needing a deviation is a stop condition, and the enforcement is structural rather than behavioral: the referee validates every transition against the rails successor table and cannot express an off-rails edge, and because the act is conditional on a successful log, a transition it refuses is a phase that does not run. The autonomy script never calls the deviation writer; the two share one lock file and one of them is not reachable from the other.

Configuration

In .ssd/project.yml under ssd:. ssd-init and /ssd upgrade --apply write this block commented out — uncommenting it is the opt-in:

  autonomy:
    mode: propose                # propose | advance | run   (absent block => propose)
    max_review_loops: 3          # coder<->reviewer rounds per gate attempt before STOP-1
    budget_transitions: 12       # phase transitions per invocation before STOP-3
    budget_wall_minutes: 30      # wall-clock cap per invocation; 0 = uncapped
    announce: full               # full | compact
  • Exactly three literals are recognized for mode:. An unrecognized value is a refusal that quotes what it found — never a silent default. This follows the gitignore_mode precedent, where a misspelled mode once disabled leak detection without saying so.
  • /ssd run works with the block absent, using the defaults above. mode: run only changes what bare /ssd offers.
  • budget_wall_minutes bounds starting new work, not the call in flight. It is checked between phases, so one long sub-skill can overrun it. A 30-minute cap is not a 30-minute guarantee.
  • announce: compact suppresses the reason line in the terminal only. The record always carries it — a record that is thinnest exactly when something went wrong unattended is not a record.

advance — the low-commitment rung

With mode: advance, bare /ssd executes its top proposal iff the existing decision tree resolves to exactly one unambiguous next action for exactly one workstream. It announces the lowered command, appends a record entry, runs that one phase, and stops. At most one phase per invocation, unconditionally — there is no accumulation.

It falls back to propose behavior verbatim when there are multiple workstreams and no branch resolution, when the proposal is a question rather than a command, when the next step is ship or beyond, or when any stop condition already holds. Fallback is not a failure and not a deviation. In practice this rung means you stop typing the obvious next command while staying in the loop at human speed.

/ssd run — the rung where the character changes

/ssd run [<slug>[#<iter>]] [--until <phase>] [--max-loops N] [--budget-transitions N] [--dry-run]

--until defaults to gate. --dry-run prints the transition plan and exits, writing nothing — the right first invocation on any project. The run resolves the workstream, opens a record and a lock, then loops announce → log → act until a stop condition fires, and hands back with a summary and a handoff note.

Interjection is a pause. Any user message mid-run is a stop. The currently executing sub-skill finishes — never a half-written artifact — and the run hands back. Delegation is revocable mid-flight; re-invoking resumes from recorded state as a fresh invocation with fresh budgets.

A stop reason is not an outcome

This one generalizes well past SSD, and it was found in review rather than in design. The first version of the record had a field for why the run ended and none for how it went — so a run that reached the ceiling with a green gate and a run that reached it with a red one produced byte-identical frontmatter, both reading stop_reason: STOP-7, described in the specification as “normal completion.”

The named consumers of these records are /codebase-skeptic and /feynman, whose shared purpose is catching the sentence “the run completed” standing in for “the run ended.” A record that cannot tell those apart launders the distinction. So a run now records gate_result — the exit status of the gate script, a number from a program rather than a boolean an LLM wrote about its own work — alongside stop_reason, and derives an outcome from the pair:

outcomeMeans
greenthe ceiling was reached and the executable gate passed
redsomething judged the work and said no — the gate kept failing, a hard failure, or a failing gate at the ceiling
incompletethe run stopped before anything judged it — off-rails, out of budget, ambiguity, or your interruption

Running out of budget is not a verdict on the code, which is why exhausting a budget is incomplete and not red. Where the reviewer’s own gate_pass and the executable gate disagree, the hand-back names both referees rather than picking one.

The general rule worth taking away: any autonomous process that reports on itself needs a field for the verdict of something that is not it. The loop may trust /code-reviewer to judge the code — that is the job it exists to do — but the run’s verdict comes from the script.

Stop conditions and refusals

Refusals happen before the run starts: an unresolvable workstream, a run already in flight, nothing to do, an out-of-ceiling --until, an unrecognized mode, or closing a gate-phase run with no verdict. Stops happen after it started, and hand back:

StopConditionWhy
STOP-1the gate still fails after max_review_loops roundsdon’t thrash; persistent failure needs human judgment
STOP-2the next action would leave the railsdeviations are engineering judgment; an auto-run writes zero
STOP-3budget_transitions, budget_wall_minutes, or the workstream’s own budget_hours exceededover budget means a scope-cut conversation, not more automated work
STOP-4a sub-skill would have to guess — brief or spec ambiguity, a missing prerequisite artifact, or the orchestrator’s idea of the current phase disagreeing with the recorded oneguesses compound silently in a loop
STOP-5user interjectiondelegation is revocable mid-flight
STOP-6phase-level hard failure (sub-skill error, git error, state-write failure)fail loudly once; never retry past an error
STOP-7--until reachedthe ceiling — and not a verdict; read outcome

The referee

The orchestrator still executes phases in prose, exactly as it does today. A script — methodology/autorun.sh — decides whether it may, because a mechanism described only in prose is a mechanism that does not exist. That is not a general principle borrowed from elsewhere; it is the lesson of this library’s own --force failure, documented in four files for eleven releases while no script accepted the flag.

So every guarantee that could be moved into an exit code has been: the mode literal, the delegation wall, the rails successor table, all three budgets, record-before-act, the lock, and the refusal to close a gate-phase run with no verdict. The subcommands are preflight, status, plan, start, transition, finish and clear. It takes the same .ssd/current.yml.lock the deviation writer takes, so the two writers are mutually exclusive and no new lock file enters your project.

The record lands at .ssd/features/<slug>/auto-runs/<timestamp>-run.md — committed under selective mode, untracked under blanket and private, the same tier as a coder-status report. stop_reason is null while a run is in flight, which is how a crashed run is found.

The failure you will actually hit

An orchestrator death between start and finish leaves the lock held, and every subsequent run refuses while naming it. That is the new operational failure mode this feature introduces, and it is deliberately manual to clear:

bash methodology/autorun.sh status                      # what is held, and its record
bash methodology/autorun.sh clear --slug <slug>         # dry run: prints, changes nothing
bash methodology/autorun.sh clear --slug <slug> --confirm

Read the record first. Its last transition entry names the last phase that was announced, and therefore the last phase that may have run — verify the working tree against it, then clear. Age-based auto-expiry was considered and rejected: a silently-expired lock resumes a run whose working tree nobody checked, and a blocked run is loud and recoverable.

What is not enforced, stated rather than implied

Three things this section would be lying about if it left them out:

  • Interruption is best-effort. It depends on the orchestrator noticing your message. There is no signal handler, and prose cannot build one.
  • Sub-skills are not atomic. “Finish the current sub-skill” is an instruction, not a guarantee. A phase that half-writes an artifact and then fails leaves that artifact behind; the run hands back rather than retrying.
  • advance can act once on misread state. One phase per invocation bounds the damage and the successor table constrains it; nothing prevents it.

One more, on the record itself: gate_result is a script’s exit status relayed by the orchestrator, because the gate script writes no file for anything to read independently. The relay is verifiable after the fact — re-run the gate on the same commit — and capturing the gate’s output to a file is what turns the relay into an artifact.

Should you turn it on?

The useful question is not whether you trust agents in the abstract. It is how executable your gate actually is, because an auto-run inherits the quality of its referee and nothing else.

If tests-pass is a live rule on your project rather than a permanent SKIP, if feature_flag_marker is set, and if your gate census reads mostly PASS rather than mostly SKIP, then the middle of the rails is genuinely mechanical for you and delegating it buys real time. If your gate SKIPs six rules out of ten, a green auto-run means “nothing that ran objected” — a far weaker sentence than it looks, and the census, not the exit code is what tells you which one you have. Finish the gate before delegating to it.

Beyond that, a reasonable progression: run --dry-run first; then live on advance, which never accumulates more than one phase of consequence per invocation; then run with a low budget_transitions on a feature whose brief you are confident in. The brief is the input the ceiling cannot protect you from — the wall stops an agent shipping, it does not stop it building the wrong thing well. That is the decision the design deliberately left with you.

Deliberately out of scope in v1, and not pending demand: any autonomy past gate; parallel auto-runs across worktrees; auto-ssd-init; a gate rule for auto-run records; a configurable ceiling; record rotation. The last of those means records accumulate — a question worth revisiting when a single feature exceeds about 25 of them.

What 3.0 adds on this ladder

The rungs are unchanged. propose is still the default, and the default is still an absent block. 3.0 adds the boundary around them.

  • Resume continues the same record. It does not reset the budget and it does not restore a shipping grant. See Doctor and resume.
  • Fingerprint replay. The same fingerprint on the same edge returns success and does not append. The same fingerprint on a different edge is STOP-4, the existing stop for “continuing would mean guessing.” There is no new stop number. Child transitions count against the same budget.
  • Grants live outside the repo. Ship, deploy, publish, release, tag, rollout, and flag removal need a one-use, snapshot-bound grant in the control directory. A lock is not a grant. See Shipping grants.
  • Unattended run is not certified on every host. The certified adapter is the unshare jail plus the external control directory on the Linux host that ran the suite. Elsewhere the effective mode is propose, and the configuration file is not rewritten.

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. 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. 3.0.0 adds a schema for skeptic reports (skeptic-before.md, skeptic-after.md, skeptic-*.md). refactor-plan.md stays unmatched because the parity fixture frontmatter-valid-names-schemaless requires that. YAML under methodology/schemas/ is the authored contract; JSON under methodology/schemas/json/ is generated from it. The rule uses a validator vendored in the project when one is there, and the library’s otherwise. SKIPs cleanly without python3 + PyYAMLADR-0006 · ADR-0022
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
skill-metadata new in 3.0.0Runs scripts/skill-frontmatter-check.py only when the project is this library (it contains both methodology/migrations.yml and the checker). Anywhere else the rule SKIPs. A missing parser is a FAIL, not a pass. The 3.0 gate reported 11 skill(s) have valid frontmatterADR-0021
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
deviations-recorded new in v2.13.0The reader half of rail deviation records (ADR-0019). Release-scoped: when a change set bumps VERSION, every touched feature directory that shipped code must have either walked each in-scope rail step or recorded a deviation for it. Deliberately narrow so it does not duplicate rails-walked — it checks step 2 (systems-designer) and step 6 (the deploy log), and step 2 only when production_runtime resolves true. That condition matters: without it the rule’s first act would have been to demand deviation records for 13 features that never needed them — manufacturing the 14th instance of the finding it exists to preventADR-0019
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 was, at that point, tracked work rather than a shipped feature.

Update, v2.13.0 — the writer half now exists. methodology/deviation.sh record --slug --step --reason logs a skipped rail step, and override --slug --rule --reason logs a FAILing rule shipped past; both land in current.yml.active[].rail_deviations, and the deviations-recorded gate rule is the reader. There is still no --force flag, and that is deliberate — overriding a red gate remains a merge you perform on purpose. What changed is that the trace it should leave is now a command you can run instead of an intention you have to remember.

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. The gate is loud when that fails, and it exits non-zero. It does not lock the merge. See gate enforcement.

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.