Build with /ssd. Watch the orchestrator do the work.
One natural-language prompt — /ssd create a simple web-based multimedia address book — and Claude Code, driving the SSD skills, scaffolds the project, picks the stack, writes the code, runs a code review, and ships a running app to localhost:5000. Your job is to read along and accept proposals. This tutorial walks you through every step and shows what was happening behind the scenes so you can drive /ssd on your own next time.
Overview
This tutorial has two layers running in parallel:
- The happy path. What a beginner sees, types, and gets back. Mostly natural language.
- Behind the scenes. Which sub-skill
/ssdinvoked, which artifact got written under.ssd/features/.../, and what the explicit command equivalent would have been if you'd typed it directly.
By the end you will have built a real Flask + SQLite address book with photos, and you will know how to drive /ssd on your own projects — both via natural-language one-liners and via explicit phase commands.
/ssd enforces — you'll see it in action.
Why This Tutorial Was Rewritten
An earlier version of this page taught beginners to hand-write JavaScript using sql.js. It was a fine little app, but it taught the opposite lesson from what this site is about: SSD is about getting the orchestrator to do the work for you, not about writing every line by hand.
If you came here to learn raw JS + SQLite-in-the-browser, that page no longer exists — but sql.js is well documented if you want it. This page teaches something more valuable: how to use AI orchestration to build real software fast and well.
What You'll Build
By the end you'll have a folder called address-book/ containing a working Python + Flask + SQLite app that:
- Stores contacts (name, phone, email, photo, notes) in a real SQLite database
- Lets you add, view, and delete contacts via a browser UI
- Stores photos as BLOBs and serves them from a route
- Has a feature flag system so half-built features can ship hidden
- Has a one-line dev-server start (
python app.py) and a clear path to production
And — more importantly — you'll have an .ssd/ directory with the full provenance of how the app was built: every architect spec, every code review, every deploy log.
Prerequisites
- Claude Code installed — the CLI from Anthropic. Install instructions: claude.ai/code.
- Python 3 — already on most Macs and Linux machines. Type
python3 --versionto check. Windows users: install from python.org. - A terminal you're comfortable opening. No prior Python or Flask experience needed.
auto mode after they trust the workflow.Step 0: Install the SSD Skills
The SSD skills are a set of Markdown files Claude Code loads when you type /ssd. Install them once into your Claude Code skills directory:
$ git clone https://github.com/AlexHorovitz/skills ~/.claude/skills
$ cat ~/.claude/skills/VERSION
2.7.0
That's it. Open Claude Code in any folder and you can now type /ssd-init, /ssd, /architect, /coder, etc.
~/.claude/skills/ on startup. Each subdirectory with a SKILL.md becomes a slash command. The version line you just printed (2.7.0) should match the version stamp at the top of the Guide. If it doesn't, you have an out-of-sync install — git pull in ~/.claude/skills fixes it.Step 1: Make a Project, Run /ssd-init
Make an empty folder, open Claude Code in it, run init.
Your transcript will look slightly different from what's below — ssd-init only asks for fields it can't auto-detect, and your environment may already have some of them.
$ mkdir address-book && cd address-book
$ git init
$ claude # opens Claude Code in this folder
> /ssd-init
I'll set up SSD for this project. A few questions:
1. Platform target? [web | headless | ios | android | macos]
2. Distribution channel? (URL, TestFlight, container registry, "TBD")
You: web ; TBD
Detected: no language yet (empty repo). Will re-detect after first commit.
Created .ssd/ tree, .ssd/project.yml, .ssd/current.yml (v2),
.ssd/current.notes.yml, docs/decisions/, docs/runbooks/, docs/architecture/.
Wrote a selective .gitignore: SSD design docs (briefs, specs, reviews,
deploy notes) are committed; machine state (current.yml, project.yml,
init-log) stays local.
Gate inputs → .ssd/gate.yml (committed):
test_command — nothing detected yet, written commented out (MAJOR)
feature_flag_marker — no flag library present, left commented
Prerequisite checks:
❌ CI/CD pipeline — missing (BLOCKER for /ssd start)
❌ Test harness — missing (BLOCKER for /ssd start)
⚠ Linter — missing (MAJOR)
❌ Feature flags — missing (BLOCKER for /ssd feature)
❌ Hello-world deploy — missing
⚠ README — missing (MAJOR)
Recommended next step: /ssd start — this is a greenfield project.
Walking Skeleton will address the BLOCKERs above as part of foundation.
The skill noticed that you have nothing yet. That's fine — it's recommending the right next move.
gate.yml: two gate rules — tests-pass and feature-flag-present — need to know your test command and your feature-flag marker. On an empty repo there's nothing to detect yet, so /ssd-init writes both keys commented out and logs the test command at MAJOR rather than pretending. Those two rules will SKIP until the keys are real. Once the Walking Skeleton in Step 3 gives you pytest and flags.py, set them:
# .ssd/gate.yml
test_command: pytest
feature_flag_marker: 'flags\.[A-Z_]+'
This is the one .ssd/* config file that gets committed — gate inputs are useless if they can't travel to a second clone or a CI runner. Machine-local state stays in gitignored project.yml, which can override either key for just your machine.
• Skill invoked:
ssd-init (v1.11.0)
• Artifacts written:
.ssd/project.yml, .ssd/gate.yml, .ssd/current.yml, .ssd/current.notes.yml, .ssd/init-log.md, .ssd/README.md
• Explicit equivalent: there's only one way to invoke this —
/ssd-init. It's a prerequisite to every other /ssd command.
• Why: SSD doctrine says "Production parity from day one." The init skill tells you on day zero what you don't have — and what you'll need before you can ship.
Step 2: The One-Liner
Now the magic line. You're back at the Claude Code prompt:
> /ssd create a simple web-based multimedia address book
I see no active workstreams in .ssd/current.yml. With your one-line brief
and an empty repo, this is a Walking Skeleton job.
I propose: /ssd start address-book
This will:
1. Run /architect to design the stack, models, API contract, and ADRs.
2. Run /systems-designer for the day-1 deploy + observability plan.
3. Help you stand up a "Hello, world" deploy.
4. Then we'll iterate features one by one with /ssd feature.
Proceed? [y/N] You: y
Under the hood: I called auto-detect because you didn't type a phase. With
no active workstream, my decision tree proposed /ssd start. SSD always names
the explicit command it's running (that's progressive disclosure) — so you
can see what the orchestrator chose, and copy the verb any time you want to
drive it yourself.
• Skill invoked:
ssd (v2.4.0) — no-arg auto-detect, then /ssd start.
• Artifact written:
.ssd/features/address-book/00-brief.md capturing your one-liner.
• Explicit equivalent:
/ssd start address-book — same effect, no auto-detect proposal step.
• Why: auto-detect lets a beginner stay in natural language. The orchestrator never silently advances — it always proposes; you accept or redirect. That's the rails in action.
Step 3: Walking Skeleton
Now the orchestrator runs the design pass. You'll see two artifacts get written, and you'll be asked to confirm each one before the orchestrator moves on.
Invoking /architect with brief: "simple web-based multimedia address book"
[architect output — full file at .ssd/features/address-book/01-architect.md]
Stack pick:
Language: Python 3.11+
Framework: Flask 3.x
Database: SQLite via stdlib sqlite3 (no install needed)
Templates: Jinja2 (comes with Flask)
Photos: stored as BLOB in the contacts table
Flag system: simple Python dict in flags.py
Data model (schema.sql):
CREATE TABLE contacts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
phone TEXT,
email TEXT,
notes TEXT,
photo BLOB,
photo_mime TEXT,
created INTEGER NOT NULL
);
ADR-0001 (docs/decisions/ADR-0001-stack.md): why Flask + stdlib sqlite3
over Django, FastAPI, or sql.js. (Beginner ergonomics; smallest install
footprint; one-file app shape.)
API contract:
GET / — list contacts
GET /contact/<id> — detail page
POST /contact — create (form-encoded)
POST /contact/<id>/delete — delete
GET /photo/<id> — serve BLOB
Approve architect spec? [y/N] You: y
Invoking /systems-designer with the architect output as input.
[systems-designer output at .ssd/features/address-book/02-systems-designer.md]
Day-1 deploy plan:
Local dev: flask --app app run --debug (port 5000)
Production: gunicorn app:app behind nginx; SQLite on disk; SQLite-WAL
mode for concurrent reads.
Observability: standard Flask logging to stdout; structured JSON in prod.
Health: GET /health returns {"status":"ok"}
Failure modes considered:
- Photo BLOB grows DB beyond 1 GB → mitigation: cap upload at 5 MB.
- SQLite write contention → WAL mode + single-writer assumption.
- File-upload XSS → use Content-Disposition: attachment for /photo.
- CSRF on delete → Flask-WTF with secret key.
Approve readiness checklist? [y/N] You: y
Foundation phase complete. Now exiting to the Walking Skeleton's exit
gate: a Hello-World deploy. (Right after this, we'll go straight into
the first end-to-end slice in Step 4.)
$ python3 -m venv .venv
$ source .venv/bin/activate
(.venv) $ pip install flask flask-wtf gunicorn
(.venv) $ cat > app.py <<'EOF'
from flask import Flask
app = Flask(__name__)
@app.get("/")
def index(): return "Hello, address book."
@app.get("/health")
def health(): return {"status": "ok"}
EOF
(.venv) $ flask --app app run
* Running on http://127.0.0.1:5000
Open http://localhost:5000 in your browser.
Walking Skeleton complete. Phase: code → review → deploy in next iteration.
Open the browser. There's nothing useful yet — but there's a real Flask app running. You shipped Hello World on day one. That's the most important moment in any project.
• Skills invoked, in order:
architect → systems-designer → coder (for the Hello-World scaffold).
• Artifacts written:
01-architect.md, 02-systems-designer.md, 03-coder-status.md, plus committed docs/decisions/ADR-0001-stack.md.
• Explicit equivalents:
/ssd design address-book would have run architect + systems-designer back-to-back without the Hello-World scaffold step.
• Why: SSD's first principle is Constant Production Parity. Deploy "Hello, world" before any business logic exists, so deployment is never "the hard part" later.
Step 4: First Feature — Add a Contact
Your one-liner from Step 2 was the brief for the whole address book. The orchestrator broke it into features. The first one is "user can add a contact." Continue:
> (just press enter — auto-detect resumes)
Active workstream: address-book (phase: design, last touched 2 min ago).
Walking Skeleton is up. Next slice: "user can add a contact."
Proposed: /ssd feature add-contact
Proceed? [y/N] You: y
Running /ssd feature add-contact:
1. /architect — adds form spec, INSERT route, schema migration
2. /systems-designer — confirms migration is reversible, flag plan
3. /coder — writes routes/templates/migration
4. /code-reviewer — gate
5. Deploy to local
[architect spec excerpt — full at iterations/add-contact/01-architect.md]
Route: POST /contact
Form: name (required), phone, email, notes, photo (file)
Schema: already created by Walking Skeleton; nothing new
Flag: flags.ENABLE_ADD_CONTACT (default: True for dev, False for prod)
[coder writes app.py, templates/add.html, templates/_form.html.
Output at iterations/add-contact/03-coder-status.md.]
[code-reviewer runs on the diff. Output at .../04-code-review.md]
finding_counts: { blocker: 0, major: 0, minor: 1, suggestion: 2 }
gate_pass: true
MINOR-1: name field has no maxlength; consider 200 to match the column.
[methodology/gate-rules.sh — the executable half of the gate]
GATE 4 pass · 6 skip · 0 fail — a skip is a check that did not run
Gate PASSED. Deploying locally.
(.venv) $ flask --app app run
Running on http://127.0.0.1:5000 — open it.
You can now add a contact. The MINOR finding is logged but doesn't block.
Address it in the next pass or carry it forward.
4 pass · 6 skip line is real output (v2.6.0+), and it is the more honest reading of a green gate: six of the rules that ran did not actually check anything, which is a different thing from six rules that passed. Here they skip for defensible reasons — adr-delta is below its architectural-diff threshold (this is a small feature), migration-manifest-current only applies inside the skills-library repo, issue-sync-current is an opt-in integration you left off, feynman-clean has no epistemic audit in scope (you haven't run /feynman — not a violation), and frontmatter-valid / skill-version-sync need frontmatter-validate.py plus its schemas/ vendored under your project root. The four that ran are the four that matter here: wip-commits, tests-pass, feature-flag-present, no-leaky-state. If a rule you expected to run is skipping — tests-pass, most often, because gate.yml never got a real test_command — that is unfinished configuration, not a pass. Add --json and the same numbers come back as pass_count / skip_count / fail_count for CI.The form works. Add yourself as the first contact. Shippable state, again.
• Phase loop:
architect → systems-designer → coder → code-reviewer → deploy.
• Artifacts written: five files under
.ssd/features/address-book/iterations/add-contact/.
• Explicit equivalents you could have run instead:
/ssd design address-book#add-contact, then /ssd code address-book#add-contact, then /ssd gate address-book#add-contact.
• Why: the feature loop is the daily rhythm of SSD. Each feature ships end-to-end before the next starts. The MINOR is captured but doesn't gate — only BLOCKER and MAJOR do.
Step 5: List + Detail + Delete
Same loop, faster, because the architect spec and stack are settled. You can use one prompt to roll up several small features:
> /ssd feature list-detail-delete: show contacts on /, click for detail,
detail page has a delete button.
Running feature loop. Output to
.ssd/features/address-book/iterations/list-detail-delete/.
[architect — adds GET /, GET /contact/<id>, POST /contact/<id>/delete]
[systems-designer — flags CSRF on delete; produces a follow-up note]
[coder — writes index.html, detail.html, delete handler]
[code-reviewer — gate]
finding_counts: { blocker: 0, major: 0, minor: 0 }
gate_pass: true
Refresh http://localhost:5000.
Now you have a real address book — minus photos. The MINOR from Step 4 is still in the carry-over ledger; we'll close it in Step 6.
• Multiple small UI features bundled into one workstream-iteration is fine. The orchestrator doesn't enforce one-feature-per-iteration; it does enforce a clean gate on whatever lands.
• Look in
.ssd/current.yml now — you'll see elapsed_hours ticking up and gate_rounds: 1 per iteration.
Step 6: Photos (Multimedia)
Now the multimedia part. Note: the database schema already has photo BLOB and photo_mime TEXT from the architect spec in Step 3. So this iteration is wiring the form, the BLOB INSERT, and the /photo/<id> serve route.
> /ssd feature photos: accept <input type="file"> on the add-contact
form, store as BLOB, serve at /photo/<id>, render <img> on detail page.
Cap uploads at 5 MB per the systems-designer plan.
[architect — confirms BLOB strategy from ADR-0001; specifies
werkzeug.utils.secure_filename; specifies max-content-length config]
[coder — adds upload handler, /photo route, max-bytes setting]
[code-reviewer — gate]
finding_counts: { blocker: 0, major: 0, minor: 1 }
gate_pass: true
MINOR-1: serve /photo with Content-Disposition: inline (current) is
correct for <img> rendering, but consider X-Content-Type-Options:
nosniff to prevent MIME-confusion XSS.
Carry-over ledger updated:
closed: name-maxlength (from add-contact iteration)
open: X-Content-Type-Options on /photo (this iteration's MINOR)
Refresh.
Your address book has photos. You built a multimedia app. The carry-over ledger keeps you honest about MINORs — they don't block ship, but they don't disappear either.
Step 7: A Round-2 Gate (When the Reviewer Says No)
So far every gate has passed. They won't always. Here's what happens when the code-reviewer finds something serious. Suppose you'd added the delete route without a CSRF token:
[code-reviewer round 1 — output at .ssd/features/address-book/
iterations/list-detail-delete/code-review/round-1.md]
finding_counts: { blocker: 1, major: 0, minor: 0 }
gate_pass: false
BLOCKER-1: POST /contact/<id>/delete has no CSRF protection.
A malicious page can submit this form on behalf of a logged-in user.
Doctrine: methodology/core.md §2 (deployable / no broken features).
Fix: wrap the form in {{ csrf_token() }} from Flask-WTF (already
in deps from systems-designer's day-1 plan).
Gate FAILED. Returning to coder.
[coder reads the BLOCKER, fixes the template, regenerates.]
> /ssd gate
[code-reviewer round 2 — auto-numbered to round-2.md, increments
current.yml.gate_rounds to 2.]
round: 2
closed_from_previous_round: [BLOCKER-1]
finding_counts: { blocker: 0, major: 0, minor: 0 }
gate_pass: true
Gate PASSED on round 2. Proceeding to deploy.
Two things to notice:
- The gate won't pass while a BLOCKER is open, and the orchestrator surfaces it loudly instead of advancing silently — Hard Rule #1. SSD 2.0's enforcement is warnings, not walls: it never quietly lets a broken state through, but it doesn't physically lock the door either — shipping anyway means merging past a red gate on purpose and writing the reason down by hand. (There is no
--forceflag. The library's own docs claimed there was one until v2.11.0 struck the claim; see the gate enforcement section.) The right move is to fix the BLOCKER, which is exactly what happens here. - Round-2 is a separate file (
round-2.md) that explicitly lists which findings from round 1 closed. The reviewer verifies closure against the code itself, not against the coder's claim.current.yml.gate_roundstracks how many rounds it took — useful budget signal. If you hitgate_rounds: 3+on a small feature, the orchestrator will suggest scope cut.
closed_from_previous_round, the closure is auditable.Step 8: /ssd ship — Production Readiness
Local dev works. Now: what would it take to put this in front of real users? Run:
> /ssd ship
Invoking /systems-designer for production-readiness checklist.
[output at .ssd/features/address-book/iterations/.../05-deploy.md]
Production checklist:
✅ App runs locally
✅ Health endpoint /health responding
✅ Migration is reversible (just delete the .sqlite file in dev)
❌ Deployed to a public URL — not yet (out of scope for this tutorial)
❌ HTTPS termination — not yet
❌ Backup strategy for the SQLite file — not yet
❌ Production secrets management — not yet (Flask SECRET_KEY in env)
⚠ Error monitoring (Sentry) — recommended for production
Ready to ship to production? Not quite — the ❌ items are real work.
For local-only use, you're done. For real production deploy, see the
runbook at docs/runbooks/address-book-production.md (just generated).
That runbook covers fly.io, Railway, and self-hosted on a VPS.
This is where the tutorial honestly stops. Going from localhost:5000 to a public URL is real work — DNS, hosting, HTTPS, backup strategy. The orchestrator generated a runbook to help you, but the actual deploy is yours to do (and yours to pay for).
/ssd doesn't do: register domains, pay for hosting, manage your DNS, or guarantee uptime. It can scaffold the deploy, write the runbook, and tell you what's missing — but production is still a human decision.Step 9: /ssd milestone — A Glimpse Forward
You don't need this yet — milestones run after 4-8 weeks or 10+ features land. But here's what it looks like, so you know when to reach for it:
> /ssd milestone
Snapshot:
git SHA → .ssd/milestones/YYYY-MM-DD-q2/sha-before
Coverage / metrics → metrics-before.yml
Invoking /codebase-skeptic — architectural critique from a roster of 15
expert voices. Output: .ssd/milestones/YYYY-MM-DD-q2/skeptic-before.md
Scoring voice activation against this codebase (a small Flask app with
SQLite, no services, one developer) — 6 of 15 earned a section:
Fowler (architecture decisions — always a candidate)
Beck (a test suite exists)
Evans (contact/photo domain logic is non-trivial)
Humble (deployment pipeline is the weak spot)
Kleppmann (SQLite persistence + BLOB storage in scope)
Jobs (user-facing app; UI coherence in question)
Not activated: Uncle Bob, Liskov, Metz, Wirfs-Brock (no class
hierarchies worth critiquing), Feathers (codebase is days old and
covered), Hohpe (no inter-service communication), Forsgren (no
delivery metrics claimed), Wozniak (nothing low-level), Fournier
(one developer, no org boundaries).
Findings: 12 architectural concerns; 4 high-priority for refactor.
Invoking /refactor with skeptic-before.md as input.
Output: .ssd/milestones/YYYY-MM-DD-q2/refactor-plan.md
Each item cites a finding ID. No cite → not in scope.
After refactor PRs ship and merge:
/ssd verify (mandatory)
Re-runs codebase-skeptic to confirm findings closed.
Output: skeptic-after.md, verification.md.
Milestone is complete only when verification passes.
Notice that nine voices stayed silent. That's the skill working correctly — activating an inapplicable voice produces noise, not signal, so codebase-skeptic scores activation before it reviews. A distributed system owned by five teams would earn most of the fifteen; your address book earns six. The Guide lists the full roster and what activates each voice.
Milestones are not for everyday work. They're the consolidation pass that keeps an SSD project from drifting into spaghetti over months. Run them on a calendar, not a feature trigger.
Command Map
Every /ssd phase command, what it does, and when auto-detect would propose it for you. You almost never need to type these explicitly — but knowing they exist helps you read the orchestrator's "Behind the scenes" lines.
| Command | What it does | Auto-detect would propose when… |
|---|---|---|
/ssd-init | One-time project bootstrap | Always — it's a prerequisite, not auto-proposed |
/ssd (no args) | Read state, propose next action | Default. Use this most of the time. |
/ssd start <slug> | Walking Skeleton for new project / major feature | Empty repo, no active workstreams |
/ssd feature <slug> | Full feature loop end-to-end | Active workstream, phase=brief or design |
/ssd design <slug> | Architect + systems-designer only | You want a spec without committing to build yet |
/ssd code <slug> | Coder only, from existing spec | phase=design, spec approved |
/ssd review <slug> | Alias for /ssd gate — same effect, phrased as the phase you're entering | phase=code; this is the wording auto-detect proposes |
/ssd gate <slug> | Code-reviewer + methodology rules | phase=code, ready to verify |
/ssd ship <slug> | Production-readiness checklist | Gate passed, ready to deploy |
/ssd milestone | Deep audit + refactor planning | 4-8 weeks since last; never auto-proposed mid-feature |
/ssd verify | Re-audit after milestone refactor | Mandatory after a milestone refactor lands |
/ssd audit | Adversarial comparative review | Never auto-proposed; nuclear option you reach for |
/ssd upgrade | Migrate a project to the latest SSD conventions (drift report, then --apply / --adopt; --elect for opt-in migrations such as private mode) | After updating the skills library; surfaces convention drift |
/ssd feature new <slug> | Start a second workstream on its own branch (optional --worktree) | You want parallel features; single-feature flow is the default |
/ssd switch <slug> | Pause the current workstream with a handoff note, check out the target | Multiple active workstreams exist |
/ssd worktree <slug> add|remove | Explicit git worktree lifecycle for a workstream | Never auto-proposed; you manage worktrees deliberately |
Append #<iter-id> to any command to operate on a specific iteration of a multi-iteration feature: /ssd code address-book#3a.
/ssd verb. The table above is what the orchestrator sequences for you. Three more answer questions the daily loop doesn't ask, and you can type them directly at any time: /codebase-skeptic (deep architectural critique), /software-standards (comparative audit), and /feynman — an epistemic audit that grades what the project believes about itself against evidence. Since v2.7.0 the orchestrator also offers /feynman at the moments that warrant it (milestone Step 0.5, verify, audit, pre-ship) — it offers, you decide, and declining is recorded. Worth knowing about before your first release; see Fooling Yourself for what it does and why.What Got Created
After this tutorial your project looks roughly like:
address-book/
├── .git/
├── .gitignore # selective .ssd/ pattern + .venv/
├── .venv/ # Python virtualenv (gitignored)
├── .ssd/ # SSD working dir (selective: design docs committed, state ignored)
│ ├── gate.yml # committed: test_command + feature_flag_marker
│ ├── project.yml
│ ├── current.yml
│ ├── current.notes.yml
│ ├── init-log.md
│ └── features/
│ └── address-book/
│ ├── 00-brief.md
│ ├── 01-architect.md
│ ├── 02-systems-designer.md
│ ├── 03-coder-status.md
│ ├── 04-code-review.md
│ ├── 05-deploy.md
│ └── iterations/
│ ├── add-contact/ # step 4
│ │ ├── brief.md
│ │ ├── 01-architect.md
│ │ ├── 03-coder-status.md
│ │ ├── 04-code-review.md
│ │ └── 05-deploy.md
│ ├── list-detail-delete/ # step 5 (with round-2 gate)
│ │ ├── brief.md
│ │ ├── 03-coder-status.md
│ │ ├── code-review/
│ │ │ ├── round-1.md # gate_pass: false
│ │ │ └── round-2.md # gate_pass: true
│ │ └── 05-deploy.md
│ └── photos/ # step 6
│ └── …
├── docs/ # committed decision records
│ ├── decisions/
│ │ └── ADR-0001-stack.md
│ ├── runbooks/
│ │ └── address-book-production.md
│ └── architecture/
├── app.py # the actual Flask app
├── templates/
├── static/
├── flags.py # feature-flag dict
├── schema.sql
└── requirements.txt
Two important things about that tree:
.ssd/uses a selective .gitignore (the v1.18+ default). Durable design artifacts — briefs, architect specs, code reviews, deploy notes — are committed, so the provenance of how the app was built lives in your repo. Machine state (current.yml,project.yml,init-log.md,archive/) stays gitignored. Solo devs who prefer the old everything-ignored behavior can runssd-init --keep-blanket-gitignore.- Or track nothing at all.
/ssd-init --private(v2.8.0+) setsgitignore_mode: private, which keeps every SSD artifact out of git entirely — the working directory and the decision records both — while every rail step and gate rule still runs. Useful for client work or a shared repo where SSD is your practice rather than the team's. Full detail in the Guide. - Everything under
docs/is committed. ADRs, runbooks, architecture diagrams — durable decisions that future you (or future teammates) need. .ssd/gate.ymlis the one config file that crosses that line. It's committed on purpose: gate inputs that only exist on your laptop can't run in CI or on a fresh clone, which is exactly howtests-passquietly SKIPped in every SSD project before v2.5.
Where to Go Next
- Try it on a real project. Open Claude Code in any folder, run
/ssd-init, type a one-liner. The orchestrator will figure out the stack from what's already there. - Read the Guide. Every doctrine principle this tutorial showed in action — Production Parity, Walking Skeleton, multi-round gates, the rails — is documented in full there.
- Customize the rails. If your team's canonical sequence differs from the default eight steps, fork
~/.claude/skills/ssd/rails.mdand point.ssd/project.ymlat the fork. See ADR-0003 for why. - Drive it yourself. There are no profiles to graduate through — SSD 2.0 is one progressively-disclosed surface for newcomer and expert alike. As you get comfortable, skip the proposals and type the verbs directly (
/ssd code,/ssd gate,/ssd ship); the orchestrator always named them for you, so you already know the set. Coming from a pre-2.0 project? Run/ssd upgradeto clean up the retireddeveloper_profile/teaching_modekeys. - Audit your own claims before you believe them. This tutorial ends with a working, deployed app and a passing gate — which is exactly the moment to notice that "it works" and "the gate passed" are claims, and that several of the gate's rules skipped rather than passed. Run
/feynmanbefore a release or after a surprise and it will build a ledger of every assertion the project makes about itself and grade each one against evidence. Start with Fooling Yourself — the doctrine, then the seven phases. - Read the SSD methodology page. The discipline behind the tooling — without it, the orchestrator is just a code generator.