Project Setup
Introducing docs-kit to a fresh repository, from nothing to your first current document. About 15 minutes.
If you already have design docs, read Adopting in an existing project first.
Prerequisites
- Installation is done (
docs-kit --versionworks) - The target is a git repository with at least one commit
The path
Step 1 — build the scaffolding with docs-kit init
Run it at the repository root.
docs-kit init
created: docs.config.yaml
created: docs/adr/
created: docs/spec/
created: docs/arch/
created: docs/plan/
created: docs/INDEX.md
created: CLAUDE.md
created: .github/workflows/docs-kit.yml
created: .claude/commands/docs-verify.md
created: .claude/commands/docs-catchup.md
What gets generated:
| Path | Contents | If it already exists |
|---|---|---|
docs.config.yaml | Config file | Skipped |
docs/{adr,spec,arch,plan}/ | The four layer directories | Skipped |
docs/INDEX.md | Empty index (generated output) | Skipped |
CLAUDE.md | Managed block with the AI protocol | Only the block is replaced. Appended if absent |
.github/workflows/docs-kit.yml | CI | Skipped (--force to overwrite) |
.claude/commands/docs-verify.md | Slash command definition | Same |
.claude/commands/docs-catchup.md | Same | Same |
init is idempotent. Running it repeatedly never damages existing files. For CLAUDE.md it replaces only the range between <!-- docs-kit:begin v1 --> and <!-- docs-kit:end -->, leaving everything else untouched. Rerun it after upgrading to refresh the managed block.
Running outside a git repository fails with exit 3.
Step 2 — tune docs.config.yaml
The generated defaults:
schemaVersion: 1
root: docs
codeRoots: [src]
ignore: []
Only two of these need attention: codeRoots and ignore. Leave the rest alone.
codeRoots — the top-level directories where code lives. A scope pointing outside them raises lint warning W003 (typo detection). Match reality.
codeRoots: [cmd, internal, pkg] # Go
codeRoots: [src, app] # TypeScript
codeRoots: [lib, app] # Ruby / Rails
ignore — files whose changes should not stale a document. Always include test code. Leaving this empty means every one-line test edit stales your design docs, and the workflow collapses.
ignore:
- "**/*_test.go" # Go
- "**/*.test.ts" # TypeScript
- "**/*.spec.ts"
- "**/__snapshots__/**"
- "**/testdata/**"
- "**/fixtures/**"
ignore is applied after the scope match. It is the place for “inside scope, but cannot affect the spec.”
These four keys are the entire configuration. Layer structure, status values, and the INDEX format are all non-configurable — so that the AI reads the same way in every repository.
Step 3 — write the first ADR
Make the first document an ADR. Pick something already decided in the project (language, framework, data store) and write it up.
docs-kit new adr use-postgres --title "Use PostgreSQL for persistence"
created ADR-0001
docs/adr/0001-use-postgres.md
The ADR number is assigned automatically as max + 1. The last line is the created path, so open it directly.
The template has three sections — Situation / Decision / Consequences (headings are Japanese in the shipped template).
---
id: ADR-0001
title: Use PostgreSQL for persistence
status: draft
---
# ADR-0001: Use PostgreSQL for persistence
## Context
(why this decision became necessary)
## Decision
(what was decided)
## Consequences
(what changes as a result; the trade-offs accepted)
Writing tips:
- Put the constraints in “Situation.” What the problem was, what the options were
- State “Decision” in one sentence. Leaving hedges in makes it undecidable for later readers (and for AI)
- Record the rejected options and why in “Consequences.” This is an ADR’s greatest value. Without it you redo the same argument in six months
When finished, change status from draft to current by hand.
status: current
Documents without a scope may be set to current by hand. They are outside the freshness check, so verify would mean nothing. Most ADRs are like this.
Step 4 — write the first spec and set its scope
Next, write “how it works now.” Pick one already-implemented feature.
docs-kit new spec editing-model --title "Editing model"
created SPEC-editing-model
docs/spec/editing-model.md
Replace the scope in the front matter with the code range this document is responsible for.
---
id: SPEC-editing-model
title: Editing model
status: draft
scope:
- src/core/editing/**
depends_on: [ADR-0001]
---
Choose the scope by asking “if this file changes, do I need to reread this document?” If yes, include it.
- Too broad (
src/**) → everything stales it, and eventually nobody looks - Too narrow (one file) → a change next door goes unnoticed
- A good target is one document per module
Body tips:
- Say what this document is in the first paragraph. The 200-character summary returned by
whichand MCP is derived from the first non-heading, non-empty paragraph. A good one lets the AI correctly decide whether to open it - Do not write history — link to the ADR. A spec carries only “what is true now”
Step 5 — reconcile with the code, then verify
Read the spec you wrote against the actual code. This is the critical step: docs-kit guarantees only “when this was last done.” The work itself is done by a human or an AI.
Once you have confirmed it matches:
docs-kit verify SPEC-editing-model
SPEC-editing-model: verified_at → 8f3a1c2 (promoted to current)
verified_at now holds HEAD’s SHA and status has moved from draft to current.
---
id: SPEC-editing-model
title: Editing model
status: current
scope:
- src/core/editing/**
depends_on: [ADR-0001]
verified_at: 8f3a1c2e0b7d4a91c3f6e2d8b5a704c1e9f3d602
---
verify rewrites only the relevant front matter lines. It does not re-serialize the YAML, so comments, x- fields, and the body survive byte for byte.
If there are uncommitted changes inside scope, you get a warning.
verifyworks against HEAD, so those changes are not covered. Commit first.
Step 6 — generate the index and commit
docs-kit lint
lint: OK (2 documents)
docs-kit index
generated: docs/INDEX.md (2 documents)
docs-kit stale
ADR-0001 skipped (no scope)
SPEC-editing-model ok (verified at 8f3a1c2)
ok 1 / stale 0 / broken 0 / skipped 1
With all three green, commit.
git add docs docs.config.yaml CLAUDE.md .github .claude && git commit -m "docs: introduce docs-kit with the first ADR and spec"
docs/INDEX.mdis generated output. Never hand-edit it. If it drifts, regenerate withdocs-kit index. CI’sindex --checkkeeps it in sync (the gofmt approach).
Step 7 — turn on CI
init already generated .github/workflows/docs-kit.yml. Push and it runs.
One thing to check inside it:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # ← without this, every document comes back broken
fetch-depth: 0 is required. stale walks git history, so a shallow clone cannot resolve the verified_at SHAs and everything reports unknown-sha / broken.
DOCS_KIT_VERSION is stamped with the version of the binary that ran init. For a local build that will be something like dev, so replace it with a real tag.
env:
DOCS_KIT_VERSION: v1.0.0
Step 8 — connect Claude Code
claude mcp add docs-kit -- docs-kit mcp
The AI now has four tools (docs_which / docs_stale / docs_verify / docs_new), and the managed block that init put in CLAUDE.md pins its working procedure.
The two slash commands written into .claude/commands/ also become available.
| Command | Purpose |
|---|---|
/docs-verify | The closing routine (reflect spec changes → verify → check lint/stale) |
/docs-catchup | Resolve stale / broken documents against the implementation |
Step 9 — run the loop once to check
Confirm the setup actually works by going around the loop once.
1. Does reverse lookup work?
docs-kit which src/core/editing/model.ts
primary:
SPEC-editing-model current 8f3a1c2 docs/spec/editing-model.md
related:
ADR-0001 current - docs/adr/0001-use-postgres.md
Correct means the spec appears under primary, and the ADR linked via depends_on appears under related.
2. Does the freshness check work?
Change any file inside scope, commit it, then run stale.
docs-kit stale
ADR-0001 skipped (no scope)
SPEC-editing-model stale code-changed: 1 files changed since 8f3a1c2
- src/core/editing/model.ts
ok 0 / stale 1 / broken 0 / skipped 1
Getting stale means it works. Update the document to match the implementation and run docs-kit verify SPEC-editing-model to return it to ok.
That completes the setup. From here it is just the loop in Workflow.
Common stumbles
| Symptom | Cause | Fix |
|---|---|---|
E008 | You set status: current but there is no verified_at | Do not set current by hand — go through docs-kit verify <id> |
E004 | id and filename do not correspond | Align them: SPEC-foo ↔ spec/foo.md, ADR-0003 ↔ adr/0003-*.md |
| Everything is always stale | scope is too broad, or tests are not in ignore | Narrow the scope. Add test globs to ignore |
| W002 warning | The scope matches no existing file (reported only once the document is no longer draft) | Fix the path typo. Check codeRoots too |
| W003 warning | The scope points outside codeRoots | Correct codeRoots to match reality |
verify errors out | No scope / plan layer / superseded | Add a scope. The plan layer cannot be verified by design |
Only CI reports everything broken | fetch-depth: 0 is missing | Add it to the checkout step |
index --check fails | You forgot to commit INDEX.md | Run docs-kit index and commit the diff |
Next
- Documentation workflow — the full picture of daily operation
- Concept and design principles — why it is shaped this way