The complete guide
A pragmatic, AI-powered engineering discipline for solo developers through giant orgs — maintain a deployable, production-ready state at all times, without a large engineering org.
Describes skills library v2.11.0 (SSD 2.0) · orchestrator /ssd v2.10.0 · bootstrap /ssd-init v1.13.0 · /feynman v1.1.0 · VERSION · CHANGELOG
Verify locally: cat ~/.claude/skills/VERSION
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:
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:
- Scope — what features and capabilities ship
- Time — when it ships
- Quality — how well it works
| 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.
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)
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.
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_idpropagation 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.exampledocumenting 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
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
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
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-initonce 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), createsdocs/decisions/,docs/runbooks/,docs/architecture/, and runs prerequisite checks - All
/ssdphases 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 startto 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?
Platform-specific Day 1 checklists for iOS, Android, macOS, Web, and Headless are in Pattern 1: Deployed Day One above.
Claude Code Skills
cat ~/.claude/skills/VERSION. If it is older, /ssd upgrade reports what has drifted; the changelog is the full record.InsanelyGreat's SSD is implemented as a set of orchestrated skills for Claude Code — this is what makes the methodology practical for a single developer or small team. The /ssd orchestrator sequences the right sub-skills for each development phase, giving you the equivalent of a senior architect, systems designer, and code reviewer on call at all times. The full skill set is free for personal and internal organizational use — github.com/AlexHorovitz/skills (library v2.7.0).
Skill Taxonomy
| Type | Skills | When you invoke directly |
|---|---|---|
| Bootstrap | /ssd-init |
Once, at project start (or when .ssd/ has drifted) |
| Orchestrator | /ssd |
Always — start here after init |
| Domain | /architect, /coder, /systems-designer, /refactor |
When working outside the SSD workflow |
| Review | /code-reviewer, /codebase-skeptic, /software-standards |
On-demand or via SSD |
| Reference | /methodology |
When you want to understand SSD doctrine or score self-adherence |
Step 1: /ssd-init — Project Bootstrap
Run once per project before any /ssd phase. First-run housekeeping: creates .ssd/ (working directory with a selective .gitignore — design docs committed, machine state ignored), writes .ssd/project.yml (detected language, framework, platform, distribution channel), writes .ssd/gate.yml (committed gate inputs — see below), creates .ssd/current.yml (active workstreams pointer), creates docs/decisions/ / docs/runbooks/ / docs/architecture/ (committed decision records), and runs SSD prerequisite checks.
Idempotent — safe to re-run. It never overwrites existing files, never deletes anything, and appends to .ssd/init-log.md on each run.
/ssd-init./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 repo | Detected test_command |
|---|---|
Makefile with a test: target | make test |
package.json with scripts.test | npm test |
A Python marker file — pyproject.toml, pytest.ini, or setup.py | pytest |
go.mod | go test ./... |
Cargo.toml | cargo test |
*.xcodeproj / Package.swift | xcodebuild test … / swift test |
Note that a bare tests/ directory is not a pytest signal on its own — Rust keeps integration tests in tests/ beside Cargo.toml, and Go and JS projects use the name too, so inferring pytest from the directory alone would write a confidently wrong test_command into exactly the projects ADR-0015 exists to protect. A pytest detection requires one of the Python marker files above.
A genuinely polyglot repo with two top-of-table signals prompts rather than guessing. If nothing is detected the key is written commented out with a one-line explanation — the reader skips comment lines, so it degrades to the old SKIP with no regression, while making the missing piece visible in the file you'll actually open. An undetected or ambiguous test command is logged at MAJOR so it never sits silently inert.
The result lands in .ssd/gate.yml — the one committed .ssd/* config file, carrying only portable gate inputs:
# .ssd/gate.yml — committed gate inputs (ADR-0015). Portable across clones and CI runners.
# Machine-specific state stays in .ssd/project.yml (gitignored). gate-rules.sh reads project.yml
# first (local override), then this file (the committed floor).
test_command: pytest
# feature_flag_marker: <regex> # set once a flag mechanism exists (see the flag BLOCKER)
feature_flag_marker can't be detected before a flag mechanism exists. It's written when a known library is present (unleash, launchdarkly, growthbook — their documented call markers) and otherwise left commented, tied to the feature-flag BLOCKER that init already reports: whoever establishes the flag mechanism sets the marker.
/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..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).
--apply · --adopt · --elect)Parallel Feature Workstreams Since v1.17
The orchestrator now treats multiple in-flight features as first-class. Up to four active workstreams per project (soft advisory limit, no hard cap), each with its own branch and an optional git worktree so two features can be edited side-by-side without checkout churn. The single-feature flow remains the default; parallel is opt-in.
feature newAt gate time, /ssd gate intersects the gated workstream's tracked file footprint with every other active workstream's footprint and surfaces overlaps as SUGGESTION-tier findings — never BLOCKER, never MAJOR. Overlap is often intentional (layered features, one workstream extends a file the other added); the orchestrator surfaces, the user judges. Running bare /ssd on any branch auto-resolves to the correct workstream via branch name → recorded mapping → prompt. The Shippable State Invariant still holds per workstream: parallelism reduces switching friction, it does not lower the bar.
Full design notes: ADR-0007 — Parallel features as first-class workstream artifacts.
Iterations Inside a Feature
A feature is one cycle by default (one design → build → review → deploy). Real features sometimes ship as multiple iterations. As of v1.5.0 of /ssd, this is a first-class concept — append #<iter-id> to any phase command:
/ssd code my-feature#3a
/ssd review my-feature#3b
/ssd ship my-feature#3b
Iter-ids match [A-Za-z0-9_-]+. The first #iter reference promotes the feature non-destructively to the multi-iteration layout (iterations/<iter-id>/ under the feature root). Single-cycle features keep the flat layout.
Multi-Round Gates
If /ssd gate fails (BLOCKER or MAJOR found), the workstream returns to coder. Re-running the gate after fixes produces a round-2 review at 04-code-review-round-2.md (or iterations/<iter>/code-review/round-2.md for multi-iter features). The orchestrator auto-numbers rounds, increments current.yml.gate_rounds, and requires closed_from_previous_round discipline on round 2+ — every closure is verified against the code, not copied from coder-status. gate_rounds: 3 on a workstream is a useful budget signal that scope cut may be wiser than another fix attempt.
The Rails — Canonical Opinionated Path
The eight-step canonical sequence (brief → design → code → review → gate → deploy → rollout-advance → flag-removal) lives in ssd/rails.md. That file is the single source of truth for what no-arg auto-detect proposes and what the eight critic-grade invariants are. A workstream that skips a step records the deviation in current.yml.active[].rail_deviations. Deviations are not failures — they are engineering judgment captured for the record. Teams with genuinely different needs fork rails.md and point project.yml.rails: at the fork.
Progressive Disclosure Changed in SSD 2.0
SSD 2.0 removed the developer-profile concept. Earlier versions carried a developer_profile field (novice | standard | expert) plus a teaching_mode toggle. One system now serves both the newcomer and the expert through progressive disclosure instead: the bare /ssd reads your state and proposes the next step in plain language, naming the explicit command it's running so you can see what the orchestrator chose and why — while every manual verb stays one hop away for power users (NeXTSTEP: lead the newcomer, never take the Terminal from the expert).
The old keys are no longer read. A pre-2.0 .ssd/project.yml that still carries developer_profile or teaching_mode is simply ignored (no crash); run /ssd upgrade for a guided clean-up that deletes the dead keys. See ADR-0012 for the rationale.
Milestone → Verify Loop
Every milestone takes a before/after snapshot and requires explicit verification:
- Snapshot: record git SHA and metrics to
.ssd/milestones/<topic>/sha-beforeandmetrics-before.yml. - Deep audit:
codebase-skepticwritesskeptic-before.md. - Refactor planning:
refactoremitsrefactor-plan.md— every item cites a specific finding ID fromskeptic-before.md. No cite → not in scope. - Validate:
code-reviewerwithremediation_mode: trueon each refactor PR. - Deploy and confirm production health.
- Verify (mandatory): re-run
codebase-skeptic→skeptic-after.md; diff frontmatter; re-runcode-revieweron 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.
/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 concept | GitHub issue | Label | Title |
|---|---|---|---|
| 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:featureissues whose body referencesEpic: #<n>, matched on word boundaries so#27never 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.
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.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
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.gh CLI, authenticated. With the toggle off, gh missing, or no GitHub repo resolvable, the mirror is a silent no-op — best-effort by design, so a sync failure (offline, rate-limited) never blocks your SSD work. A project without the toggle behaves byte-for-byte as it did before the feature existed.Full design notes: ADR-0014 — GitHub issue state tracking.
Sub-Skill Reference
| Skill | Role in SSD | Phase |
|---|---|---|
/ssd-init (v1.11.0) |
First-run housekeeping: creates .ssd/ tree, writes project.yml + current.yml (v2 schema) + current.notes.yml, runs prerequisite checks. Idempotent. v1.11.0 added Step 6.5 — test-command detection written to a committed .ssd/gate.yml, so tests-pass and feature-flag-present stop SKIPping (ADR-0015); v1.10.0 dropped the developer_profile / teaching_mode defaults (removed in SSD 2.0); v1.3.0 added the v1→v2 prompted migration. |
prerequisite to all phases |
/architect (v1.3.0) |
Design: models, services, API contracts, ADRs, current-scale baseline. Platform-adaptive (web, iOS, Android, macOS, headless); web guides cover Next.js, Django, FastAPI, Rails, Laravel, Angular, Vue/Nuxt, Spring Boot, ASP.NET Core. Integration has a first-class contract. | start, feature |
/systems-designer (v1.5.0) |
Production readiness: reliability, observability, deployment safety. Validates architect spec in Phase 0. Covers AI/LLM integration, compliance & data lifecycle, cost observability, and chaos/failure injection. | start, feature, ship |
/coder (v1.4.0) |
Implementation from spec (Python, TypeScript, Swift, Ruby, Java, C#, PHP, Go, Rust, C/C++, Obj-C). Halts if the architect spec omits a feature flag. Spec-drift check amends ADRs. Emits 03-coder-status.md with test/lint/typecheck results. |
feature |
/code-reviewer (v1.7.0) |
PR gate: BLOCKER/MAJOR findings block merge. Phase 1.5 prior-review follow-up (remediation mode) and Phase 3.5 fix-introduces-edge-cases. Red flags include LLM prompt injection, IntegrityError fetch mismatch, cache-without-race-test, release theatre. Loads examples.md reference. |
feature, milestone, gate, verify |
/codebase-skeptic (v1.5.0) |
Deep architectural critique through 15 expert voices, of which 2–15 activate per codebase (an inapplicable voice is noise, not signal). Mandatory Phase 2.5 Operational Failure Modes Sweep. Forward-Looking Pass in Phase 4. Incident-Story attestation (Beck), Domain-Modeling Stance (Evans), Deployment-Gate Hardening (Humble). | milestone |
/feynman (v1.1.0) |
Epistemic audit, not a structural one: builds a claim ledger of every assertion the project makes about itself — READMEs, ADRs, CI badges, test names, tickets, status reports, and the framing of the request itself as claim C0 — then grades each against evidence on six grades (Verified / Unverified / Unfalsifiable / Misleading / Contradicted / Theater). Also inventories recurring rituals for ones nobody can trace to a decision they changed. Mandatory Phase 7 lean-over-backwards: the report publishes what it did not examine (machine-readable not_examined) and separate executed-vs-read evidence counters. Emits feynman.md with a one-sentence verdict and a posture (Calibrated / Drifting / Self-Deceiving / Cargo Cult). Doctrine: Fooling Yourself. |
milestone (Step 0.5), verify, audit, pre-ship — proposed, never auto-run |
/software-standards (v1.1.1) |
Adversarial comparative audit. Two modes: Comparative and Adversarial Single. Requires 2–3 evidence citations per /10 score. For vendor selection / legacy onboarding / pre-acquisition — not routine review. |
audit |
/refactor (v1.3.0) |
Post-ship targeted improvement. Every item cites a specific finding from skeptic-before.md. Step 4.5 Budget Check with halt-and-rollback. Step 5 per-item re-check loop closure. Step 6 systems-designer coordination trigger. Loads patterns.md reference. |
milestone |
/methodology (v1.7.1) |
SSD doctrine reference — Iron Law, Five Principles, Decision Framework. Provides machine-checkable rule source for /ssd gate. /methodology score emits a self-adherence metric. |
reference / any phase |
Review Tier Selection
Four skills do "review" work. Never chain all four — pick the right tier:
/code-reviewer— every PR, always, no exceptions (≤500 changed lines)/codebase-skeptic— milestone reviews and pre-release audits of an owned codebase/software-standards— comparative/adversarial evaluation only (vendor selection, legacy onboarding, pre-acquisition)/feynman— when the question is not "is this good" but "is what we believe about it true": before a release or a status report that will be believed, after an incident that shouldn't have been possible, or when the build is green and you don't feel safe deploying
/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.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.
| Voice | Activates when |
|---|---|
| Fowler | Any architecture decisions are present — always a candidate |
| Uncle Bob | Object-oriented or class-based codebase with modularity choices |
| Liskov new | Inheritance hierarchies, subtyping, or polymorphism carry behavioral contracts |
| Metz new | OO design where coupling and change-ripple between collaborators is in question |
| Wirfs-Brock new | Objects have responsibilities and collaborations worth assessing; assignment is unclear |
| Beck | A test suite exists, or conspicuously does not |
| Feathers | Codebase is > ~2 years old, has low coverage, or is being modified carefully |
| Evans | Business logic is non-trivial; a domain model exists or should |
| Hohpe | Services communicate with each other; queues, events, or APIs are present |
| Humble | There is a deployment pipeline, or the absence of one is notable |
| Forsgren new | Delivery performance is claimed or measured; DORA metrics, batch size, velocity/reliability tradeoffs |
| Kleppmann | Data persistence, replication, caching, streaming, or consistency are in scope |
| Jobs | The system is user-facing, or product/API design coherence is in question |
| Wozniak | Low-level design, resource usage, clever optimizations, or embedded/systems code |
| Fournier new | Multiple teams or unclear ownership; boundaries mirror the org chart; bus-factor concerns |
v1.5.0 added the five marked new, taking the roster from ten to fifteen and widening the activation range to 2–15. The additions cover ground the original ten left thin: subtyping contracts (Liskov), coupling and message-passing (Metz), responsibility-driven design (Wirfs-Brock), empirical delivery performance (Forsgren), and organizational scaling / Conway's Law (Fournier).
The SSD Artifact Tree
Every SSD invocation produces artifacts at well-known paths relative to the project root. Sub-skills read from and write to this tree — that is what lets a session resume, a reviewer verify, and a teammate onboard. As of v1.3.0 of the orchestrator, the working directory is hidden (.ssd/) — the visible ssd/ name collided with the orchestrator skill source directory in the SSD skills repo itself.
<project-root>/
├── docs/ # committed decision records
│ ├── decisions/ # ADRs from architect
│ ├── runbooks/ # runbooks from systems-designer
│ └── architecture/ # component diagrams, data models
└── .ssd/ # working dir — selective .gitignore (see below)
├── gate.yml # portable gate inputs: test_command, flag marker [COMMITTED]
├── project.yml # language, framework, platform, distribution channel [ignored]
├── current.yml # v2 schema: machine-managed workstream state [ignored]
├── current.notes.yml # free-form context for next agent / human [ignored]
├── features/
│ └── <slug>/
│ ├── 00-brief.md # epic-level for multi-iter features
│ ├── 01-architect.md
│ ├── 02-systems-designer.md
│ ├── 03-coder-status.md # — single-cycle features only
│ ├── 04-code-review.md # — single-cycle features only
│ ├── 04-code-review-round-2.md # — multi-round gate output
│ ├── 05-deploy.md
│ └── iterations/ # — multi-iteration features only (opt-in)
│ └── <iter-id>/ # e.g., 3a, 3b, auth-flow
│ ├── brief.md
│ ├── coder-status.md
│ ├── code-review/
│ │ ├── round-1.md
│ │ └── round-2.md
│ ├── deferred.yml # carry-over ledger
│ └── deploy.md
├── milestones/
│ └── YYYY-MM-DD-<topic>/
│ ├── sha-before
│ ├── metrics-before.yml
│ ├── skeptic-before.md
│ ├── refactor-plan.md
│ ├── refactor-prs.md
│ ├── skeptic-after.md
│ ├── feynman.md # — /feynman epistemic audit (also <feature>/feynman.md)
│ └── verification.md
├── audits/ # [ignored]
│ └── YYYY-MM-DD-<scope>/
│ └── standards-report.md
└── archive/ # closed feature + milestone directories [ignored]
.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..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 | |
|---|---|
| Gitignored | All of .ssd/ — including .ssd/gate.yml — plus docs/decisions/, docs/runbooks/, and docs/architecture/ |
| Also suppressed | The add- branch prefix (branches become plain {slug}), GitHub issue tracking (forced off), and the CLAUDE.md SSD section |
| Kept | The 🛠️ 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 --cacheddoes 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.
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.ymlcan exist, sotest_commandandfeature_flag_markerlive in gitignoredproject.ymland 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-deltaandfeynman-cleanuse 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 fallbackadr-deltawould deadlock againstno-leaky-stateand 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
| Layout | One store repo, one subdirectory per project: ~/private-ssd/<project>/ |
| Requires | gitignore_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 |
| Config | store_root, store_dir, store_auto_commit in project.yml |
| Consumer changes | None. 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.
unlink verb to move back out, and a migration-manifest entry so /ssd upgrade can offer this to an existing project. The store is also the newest thing in the library and the least exercised outside its own tests — treat it accordingly.Methodology-Backed Gate Enforcement
As of v1.4.0 of the orchestrator, gate enforcement is an executable shell script, not LLM-internal checks. Before /ssd gate passes, the orchestrator invokes:
bash methodology/gate-rules.sh --base <base-branch> --json
Each rule emits PASS | FAIL | SKIP. Any FAIL exits non-zero and the gate refuses to pass. The same script is invocable from CI for parity.
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.
| Rule (script) | What it checks | Source |
|---|---|---|
wip-commits | git log <base>..HEAD --grep='WIP|checkpoint|TODO.*tomorrow|FIXME.*later' -i is empty | core.md §4 |
tests-pass | Project's test_command exits 0. Input resolved .ssd/project.yml → .ssd/gate.yml (see below) | core.md §1 |
feature-flag-present | Project's feature_flag_marker appears in non-doc changed files (skipped for doc/config-only diffs). Same input fallback | core.md §3 |
adr-delta | If architectural diff > 200 lines outside test/doc/migration scope, docs/decisions/ has a new or modified ADR | core.md §2 |
frontmatter-valid | Changed .ssd/features/ + .ssd/milestones/ artifacts carry schema-valid YAML frontmatter. Since v2.6.0 the PASS line reports the unvalidated count too (N artifact(s) validated against schemas; M unvalidated (no matching schema)). v2.11.0 added schemas for briefs and deploy logs — the two largest unvalidated classes, and in the deploy log's case the artifact that records what actually shipped. Seven types now validate; milestone artifacts (skeptic reports, refactor plans, verification records) still pass through unchecked and say so. v2.11.0 also fixed a message that reported "no SSD artifacts in scope" for a change set whose artifacts the validator had seen and had no schema for — a false statement that stood for four releases. SKIPs cleanly without python3 + PyYAML, or where the validator isn't reachable from the project root | ADR-0006 |
rails-walked new in v2.11.0 | The 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 shipped | ADR-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 directory | ADR-0018 |
no-leaky-state | Machine state (current.yml, project.yml, archive/, …) isn't staged for commit under the selective-gitignore split (SKIPs in blanket mode) | ADR-0008 |
skill-version-sync | Version 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 coverage | ADR-0009 |
migration-manifest-current | The 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 that | ADR-0013 |
feynman-clean | Any feynman.md in the change set reports zero contradicted and zero theater claims — the project's own account of itself survived contact with evidence. Reads the counters, not gate_pass (a rule cleared by flipping one boolean would let the report judge its own verdict) and frontmatter only, never the body (report prose legitimately contains lines that look like counters). SKIPs when no audit is in scope — not running /feynman is not a violation — so a PASS means "no failing audit in this change set", not "this project's beliefs are calibrated" | ADR-0016 |
issue-sync-current | Informational. 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 call | ADR-0014 | Its workstream parser was rewritten in v2.9.0 — before that it could not pass.
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.
--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.
/ssd ship --force as a logged override leaving a durable rail_deviations trace. Neither half was ever implemented: no script accepts --force, and rail_deviations: has never been written by any tool in the library's history. An epistemic audit established both by execution, and the claim was struck rather than softened. What actually happens when you override a red gate is that you merge it deliberately and write the reason down by hand in the deploy log's rail-deviations table. Building the real mechanism is tracked work, not a shipped feature.See ADR-0005 for why the rules run as a bash script rather than orchestrator-internal LLM checks.
Hard Rules
1. No merge without a clean /ssd gate
No BLOCKER or MAJOR findings from the code-reviewer. No exceptions.
2. No incomplete work on main without a feature flag
WIP commits on main are banned. Use a feature flag or a local stash.
3. Tests must pass before and after every change
"I'll fix the tests tomorrow" is not a shippable state.
4. Refactor only after shipping
Separate PRs, never mixed with feature work. Milestones run after shipping, never instead of it.
5. Deploy beats perfection
Reduce scope rather than delay a deploy.
6. Production parity from day one
If you haven't deployed to production yet, that is your next task.