Skill v1.0.1
currentAutomated scan100/100+3 new
name: bootstrap description: Onboard a brownfield project into OpenSpec — analyze the existing codebase with parallel explorer agents, write an initial architecture document, then generate one spec per discovered feature describing what is already built. Use when adopting spec-driven development on a repo that has code but no specs. license: MIT compatibility: Requires the openspec CLI. Node.js >= 18 for the bundled interlock-graph CLI. argument-hint: "[--quick] [--scope <path>]" allowed-tools: Bash(openspec ) Bash(interlock-graph ) Bash(git check-ignore *) Agent Read Write Glob Grep metadata: version: "1.0.1" type: discovery outputs:
- openspec/initial-architecture.md
- openspec/specs/<feature>/spec.md
Document what a codebase already is, in OpenSpec form, so that every later change has something to change from.
This is archaeology, not design. Every spec you write describes behavior that exists in the code today. If you cannot point at the code that implements a requirement, it does not belong in the spec.
| Flag | Effect | |
|---|---|---|
--quick | Sequential, single-context. For small repos (< ~50 source files). | |
--scope <path> | Restrict the whole run to a subdirectory. | |
| (none) | Full parallel run. |
0. Pre-flight
openspec --version
If it does not print a version, stop here, before openspec init, before interlock-graph build, before spawning any explorer or writing any file — even when openspec/config.yaml already exists, because the specs this skill writes are only done once openspec validate passes.
command not found(or the shell's equivalent): the CLI is missing. Say so in one line and give the install commandnpm install -g @fission-ai/openspec@latest, or the project's own package manager if it pins openspec.- Any other error: the CLI is installed but does not run. Do not call it missing and do not offer the install line. Quote its error; the usual cause is a Node.js older than OpenSpec's floor, 20.19.0.
Either way, point at interlock doctor for the full preflight. Do not install or upgrade anything yourself, and do not hand-write openspec/config.yaml or openspec/specs/ — the CLI owns those formats.
# Initialize OpenSpec if this repo has never been initialized.# `openspec init` creates openspec/config.yaml, so its absence is the real signal.test -f openspec/config.yaml || openspec init --tools claude# Build the structural graph once — it seeds every explorer below.# Never swallow the outcome: stderr stays visible, and every way the graph can# come back less than whole (build errored / nothing readable / nothing# indexable / some paths unreadable) gets a banner.graph_log=$(interlock-graph build .)graph_status=$?printf '%s\n' "$graph_log"if [ "$graph_status" -ne 0 ]; thenecho "GRAPH UNAVAILABLE: build errored (exit $graph_status) — explorers fall back to grep and will be slower"elif printf '%s' "$graph_log" | grep -q 'nothing indexable under the root could be read'; thenecho "GRAPH UNAVAILABLE: nothing under the root could be read — explorers fall back to grep and will be slower"elif printf '%s' "$graph_log" | grep -q 'nodes=0'; thenecho "GRAPH UNAVAILABLE: nothing indexable found — explorers fall back to grep and will be slower"elif printf '%s' "$graph_log" | grep -q 'skipped='; thenecho "GRAPH PARTIAL: some paths were unreadable and are not in the graph (each is named above as GRAPH SKIPPED UNREADABLE) — a missing edge for one of them is not evidence of absence"fi
A graph failure is not a stop. Bootstrap works without it — docs and OpenSpec indexing still happen — so print the banner, carry it to the final report, and continue with degraded expectations: the explorers grep instead of following edges, and they are slower for it.
A partial graph is not a failure, and not a whole graph either. interlock-graph build exits 0 when it meets a path it cannot read: it skips the path, names it on stderr as GRAPH SKIPPED UNREADABLE: <path> (<code>), and ends its summary line in skipped=<n>. The exit code alone no longer tells you the graph is complete. The explorers still follow edges, but nothing about a skipped path is in the graph, so tell them which paths those were and have them Read or grep there instead of trusting an empty result.
nodes=0 is the ordinary case for a repo the graph doesn't structurally index (Go, Rust, Java, Ruby — structural indexing covers JavaScript/TypeScript, Python, shell, C# (.cs, .csproj) and Bicep (.bicep)). Say which reason applied — an unsupported language, or a tree that could not be read — rather than presenting either as a defect.
When the build did produce a graph, read `.claude/graph/GRAPH_REPORT.md` before spawning anything. Its hub nodes and module boundaries tell the explorers where to look, so they don't rediscover the tree from scratch.
Then check whether openspec/specs/ already has content. If it does:
"This project already has N specs. Bootstrap will ADD specs for undocumented features but will never overwrite an existing one. Continue?"
1. DISCOVER — parallel exploration
Spawn five explorer subagents via the Agent tool (subagent_type: "Explore"), or run them in sequence under --quick. Each returns structured JSON.
Before writing the prompts, establish the stack. Read the dependency manifest (package.json, pyproject.toml, go.mod, Cargo.toml, pom.xml, Gemfile, …) and the top-level layout. Then phrase each prompt in that stack's vocabulary. The categories below are universal; examples in your head are illustrative only — replace them with the frameworks this repo actually uses. Asking a Go service about React hooks wastes a whole agent.
Shared skeleton — fill {role}, {task}, {schema} from the table:
You are the {role} explorer for this repo. Phrase findings in this stack's vocabulary.{task}Prefer GRAPH_REPORT.md / interlock-graph when a graph exists; otherwise grep.Return JSON only: {schema}
| Role | Task | JSON schema | |
|---|---|---|---|
| Structure Mapper | Map layout. Prefer GRAPH_REPORT modules/hubs, then verify on disk. Report organization (feature/layer/hybrid), entry points and routes, naming conventions, key config files, monorepo yes/no. | { organization, entryPoints[], conventions[], configFiles[], monorepo } | |
| Dependency & Data Model Analyzer | Prefer interlock-graph query / path. Report third-party deps by role, internal layer imports, persistence schema/models, entity relationships, outbound integrations. | { dependencies[], internalLayers[], dataModels[], entityRelationships[], integrations[] } | |
| Pattern Extractor | Recurring composition (components/handlers/services), state/lifecycle, errors, auth, data fetch/mutate, testing, code style. | { compositionPatterns[], stateManagement, errorHandling, auth, dataFlow, testing, codeStyle } | |
| Interface Scanner | External surface: endpoints and protocol, mutations, schemas/validators, public exports/SDK, realtime/streaming, CLI. | { endpoints[], mutations[], schemas[], publicExports[], realtime[], cli[] } | |
| Infrastructure & Config Reader | Build/task-runner entry points, env vars referenced in code, CI/CD, deploy target, lint/format, typecheck/static analysis. | { buildSystem, tasks[], envVars[], cicd, deployment, linting, staticAnalysis } |
Cap: 8 agents in this phase.
2. SYNTHESIZE — the architecture document
One synthesis pass reads all five outputs and writes openspec/initial-architecture.md:
# Initial Architecture> Auto-generated by /interlock:bootstrap on {date}.> Point-in-time snapshot, not a living document.## Stack & Infrastructure## Project Organization## Architecture Overview <- ASCII diagram, subsystem boundaries, data flow## Data Model## Subsystems & Features <- table: subsystem | location | responsibility | patterns## Established Patterns <- composition, data flow, state, errors, auth, testing## Interface Surface## Design Decisions (Inferred)-[EXTRACTED] stated outright in comments or docs-[INFERRED] deduced from a consistent pattern-[AMBIGUOUS] unclear — several patterns coexist## Gaps & Risks <- incomplete work, conflicting patterns, hazards
Tag every design decision with its confidence. An [INFERRED] decision that turns out wrong is recoverable; one presented as fact is not.
3. IDENTIFY FEATURES — boundary detection
From the synthesis, pick the units that each deserve a spec:
- Entry-point based — each major route group, command, or consumer
- Domain based — each data model with a full lifecycle
- Capability based — cross-cutting concerns (auth, observability, error handling)
- Module based — feature directories and barrel exports
Present the list for confirmation before generating:
Discovered N features to spec:1. authentication — login, register, session management2. task-crud — create, read, update, delete tasks3. app-shell — layout, navigation, route protectionGenerate specs for all, or select specific ones?
4. SPEC GENERATION — one agent per feature
For each confirmed feature, spawn a subagent (cap: 12).
You are writing an OpenSpec spec for an existing feature. Document what IS built, not what SHOULD be.Feature:{name}. Architecture context:{relevant section}. Source files:{paths}.Read the implementation. Write requirements in the repo's OpenSpec spec format, each one traceable to code. Where behavior is genuinely unclear, mark it rather than guessing.
Write to openspec/specs/<feature>/spec.md. Never overwrite an existing spec file. If one exists, skip it and report the skip.
Then validate:
openspec validate --specs
5. UPDATE PROJECT CONTEXT
Fill in the project context in openspec/config.yaml with what was discovered — stack, architecture summary, conventions, domain — so later openspec instructions calls carry it. Merge into the existing file; never clobber unrelated keys a user has set.
6. REPORT
- Any
GRAPH UNAVAILABLE: <reason> — explorers fall back to grep and will be slowerbanner from pre-flight, repeated verbatim. AGRAPH PARTIALbanner is repeated verbatim too, followed by the paths the build named asGRAPH SKIPPED UNREADABLE. If the graph built whole, say so in one line instead — a report that mentions none of these reads like a graph-backed run that never happened. - Architecture doc path and what it covers
- Specs created, and specs skipped because they already existed
- Features deliberately not specced, with the reason
openspec validate --specsresult- Corpus-persistence posture (below)
- Suggested next step:
/interlock:docs-digestto build the agent prose primer
Corpus-persistence posture
This is the one moment the repository is looked at before any run has written to it, so raise the question now: will its run corpora be kept?
Observe the current state with git's own matcher — do not read or parse .gitignore. Its semantics include negation, directory-only trailing slashes, nested files, .git/info/exclude and the user's global excludes file, and a hand-rolled match gets some of those wrong:
git check-ignore -q .claude/ship/ ; echo "ship=$?"git check-ignore -q .claude/learning/ ; echo "learning=$?"git check-ignore -q .claude/metrics/ ; echo "metrics=$?"
Exit 0 means the path is excluded, 1 means it is tracked territory.
Report each of the three separately — never collapse them into one verdict. A split state, where one is excluded and the others are not, is the case most likely to be unintentional, and collapsing hides exactly that.
State the recommendation as conditional, never as a verdict about this repository. Nothing you can observe tells you whether the repo will run /interlock:ship against its own product, so say which posture fits which intent and let the reader choose:
.claude/ship/and.claude/metrics/are excluded,.claude/learning/isnot. If this repository will run/interlock:shipon its own product, thoseexclusions drop the audit trail — remove them from.gitignore. If it is aharness whose ship runs are development exhaust, add.claude/learning/soall three match. Rationale and both.gitignoreblocks: docs/11, "Whether tokeep them".
Never write to `.gitignore`. Not to create it, not to append, not to "fix" a split state. Name the entries a person could add and let them add them — this skill was invoked to produce specs, and an edit to an unrelated file is a surprise in someone's diff, not a service.
If any git check-ignore exits non-zero for a reason other than "not ignored" — an unreadable .gitignore, or a directory that is not a git repository at all — report the posture as undetermined and say why, then carry on. Bootstrap completes and writes every artifact it would otherwise have written; a failed check never fails the onboarding.
Guardrails
- Never overwrite an existing spec. Add only.
- Never modify source code. This skill is read-only outside
openspec/and.claude/graph/. - Respect
.gitignore; skip vendored and generated trees. - Max 8 explorer agents in phase 1, 12 spec agents in phase 4.
- Idempotent. Re-running adds newly-discovered features and leaves everything else alone.
- If a feature's behavior can't be determined from the code, say so in the spec rather than inventing a requirement.
Follow ${CLAUDE_PLUGIN_ROOT}/shared/TOOL-ECONOMY.md.