@renzrollon/interlock 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/.claude-plugin/marketplace.json +19 -0
  2. package/.claude-plugin/plugin.json +58 -0
  3. package/CHANGELOG.md +380 -0
  4. package/LICENSE +21 -0
  5. package/README.md +358 -0
  6. package/agents/ping.md +8 -0
  7. package/agents/worker.md +8 -0
  8. package/bin/interlock +2655 -0
  9. package/bin/interlock-graph +6 -0
  10. package/bin/interlock-run +508 -0
  11. package/bin/interlock-ship-acp +39 -0
  12. package/hooks/_shared.mjs +162 -0
  13. package/hooks/guard-commit.mjs +77 -0
  14. package/hooks/guard-tasks.mjs +107 -0
  15. package/hooks/guard-tests.mjs +63 -0
  16. package/hooks/preflight.mjs +107 -0
  17. package/lib/artifacts.mjs +316 -0
  18. package/lib/autonomy.mjs +149 -0
  19. package/lib/conformance.mjs +132 -0
  20. package/lib/doctor.mjs +1176 -0
  21. package/lib/drift.mjs +409 -0
  22. package/lib/eval-history.mjs +304 -0
  23. package/lib/evals-calibrate.mjs +317 -0
  24. package/lib/evals-capture.mjs +437 -0
  25. package/lib/evals-promote.mjs +257 -0
  26. package/lib/evals-triage.mjs +216 -0
  27. package/lib/findings.mjs +340 -0
  28. package/lib/graph/cli.mjs +266 -0
  29. package/lib/graph/extract/config-json.mjs +74 -0
  30. package/lib/graph/extract/create-extractor.mjs +35 -0
  31. package/lib/graph/extract/memory.mjs +99 -0
  32. package/lib/graph/extract/openspec.mjs +80 -0
  33. package/lib/graph/extract/python.mjs +137 -0
  34. package/lib/graph/extract/regex-fallback.mjs +187 -0
  35. package/lib/graph/extract/shell.mjs +71 -0
  36. package/lib/graph/extract/typescript.mjs +217 -0
  37. package/lib/graph/extract/walk.mjs +272 -0
  38. package/lib/graph/format.mjs +51 -0
  39. package/lib/graph/graph/build.mjs +374 -0
  40. package/lib/graph/graph/query.mjs +249 -0
  41. package/lib/graph/graph/report.mjs +122 -0
  42. package/lib/graph/graph/update.mjs +47 -0
  43. package/lib/graph/paths.mjs +66 -0
  44. package/lib/graph/retrieve/context.mjs +86 -0
  45. package/lib/graph/retrieve/docs-index.mjs +116 -0
  46. package/lib/graph/retrieve/markdown.mjs +246 -0
  47. package/lib/host/acp.mjs +551 -0
  48. package/lib/host/claude-cli.mjs +285 -0
  49. package/lib/host/codex.mjs +231 -0
  50. package/lib/host/model-map.mjs +129 -0
  51. package/lib/host/qwen.mjs +176 -0
  52. package/lib/host/registry.mjs +213 -0
  53. package/lib/host.mjs +393 -0
  54. package/lib/ledger.mjs +444 -0
  55. package/lib/limits.mjs +525 -0
  56. package/lib/locators.mjs +99 -0
  57. package/lib/merge-lanes.mjs +148 -0
  58. package/lib/metrics.mjs +99 -0
  59. package/lib/notify.mjs +229 -0
  60. package/lib/outcomes.mjs +560 -0
  61. package/lib/plan-fingerprint.mjs +570 -0
  62. package/lib/project-slug.mjs +18 -0
  63. package/lib/prompts/commit.mjs +26 -0
  64. package/lib/prompts/dimensions.mjs +70 -0
  65. package/lib/prompts/handoff.mjs +86 -0
  66. package/lib/prompts/implementer.mjs +150 -0
  67. package/lib/prompts/index.mjs +250 -0
  68. package/lib/prompts/planner.mjs +70 -0
  69. package/lib/prompts/remediate.mjs +137 -0
  70. package/lib/prompts/replan.mjs +23 -0
  71. package/lib/prompts/review.mjs +107 -0
  72. package/lib/prompts/schemas.mjs +206 -0
  73. package/lib/prompts/stage.mjs +48 -0
  74. package/lib/prompts/verify.mjs +88 -0
  75. package/lib/ready.mjs +812 -0
  76. package/lib/receipt.mjs +434 -0
  77. package/lib/remediate.mjs +275 -0
  78. package/lib/report-html.mjs +623 -0
  79. package/lib/report.mjs +1124 -0
  80. package/lib/review-core.mjs +713 -0
  81. package/lib/risk.mjs +370 -0
  82. package/lib/run-log.mjs +1004 -0
  83. package/lib/run-paths.mjs +239 -0
  84. package/lib/run.mjs +2921 -0
  85. package/lib/ship-stage.mjs +223 -0
  86. package/lib/spill.mjs +150 -0
  87. package/lib/surface.mjs +195 -0
  88. package/lib/verify.mjs +1074 -0
  89. package/lib/waves.mjs +2661 -0
  90. package/package.json +40 -0
  91. package/shared/CONTEXT-HYGIENE.md +84 -0
  92. package/shared/DECISION-LEDGER.md +141 -0
  93. package/shared/EXPLORE-BRIEF.md +96 -0
  94. package/shared/INVARIANT-SWEEP.md +57 -0
  95. package/shared/SESSION-SHAPES.md +261 -0
  96. package/shared/SKILL-CANDIDATE-BRIEF.md +106 -0
  97. package/shared/TEST-PROFILE.md +204 -0
  98. package/shared/TOOL-ECONOMY.md +155 -0
  99. package/skills/bootstrap/SKILL.md +226 -0
  100. package/skills/commit/SKILL.md +99 -0
  101. package/skills/dispatch/SKILL.md +78 -0
  102. package/skills/docs-digest/SKILL.md +109 -0
  103. package/skills/evals/SKILL.md +160 -0
  104. package/skills/explain-code/SKILL.md +42 -0
  105. package/skills/explain-code/commit-teachin.md +136 -0
  106. package/skills/explain-code/module-walkthrough.md +358 -0
  107. package/skills/explore/SKILL.md +159 -0
  108. package/skills/fix-tests/SKILL.md +120 -0
  109. package/skills/graph/SKILL.md +136 -0
  110. package/skills/manual-test-plan/SKILL.md +121 -0
  111. package/skills/mr/SKILL.md +170 -0
  112. package/skills/report/SKILL.md +121 -0
  113. package/skills/review-artifacts/SKILL.md +127 -0
  114. package/skills/review-code/REVIEW.template.md +49 -0
  115. package/skills/review-code/SKILL.md +156 -0
  116. package/skills/review-code/dimensions/architecture.md +10 -0
  117. package/skills/review-code/dimensions/devops.md +12 -0
  118. package/skills/review-code/dimensions/language.md +12 -0
  119. package/skills/review-code/dimensions/qa.md +12 -0
  120. package/skills/review-code/dimensions/security.md +12 -0
  121. package/skills/review-code/dimensions/technical-lead.md +19 -0
  122. package/skills/ship/SKILL.md +67 -0
  123. package/skills/spec/SKILL.md +264 -0
  124. package/skills/spec/continuity.md +66 -0
  125. package/workflows/ship.js +565 -0
@@ -0,0 +1,155 @@
1
+ # Tool Economy — Locate Before You Read, Batch Your Bash, Read State Don't Re-Derive It
2
+
3
+ Shared discipline for all interlock skills that investigate a codebase or
4
+ gather project state. Companion to `CONTEXT-HYGIENE.md` and `INVARIANT-SWEEP.md`.
5
+
6
+ ## Rule 0 — Query the graph before you Grep (when it exists)
7
+
8
+ If `.claude/graph/graph.json` exists, treat it as the first locator for
9
+ structural questions — “where is X”, “who uses Y”, “how does A reach B”:
10
+
11
+ ```bash
12
+ interlock-graph query "<tokens>" --budget 1500
13
+ interlock-graph consumers <symbol-or-file>
14
+ interlock-graph path <A> <B>
15
+ interlock-graph explain <node>
16
+ ```
17
+
18
+ (`interlock-graph` is on PATH whenever this plugin is enabled — never probe for it.)
19
+
20
+ Read `.claude/graph/GRAPH_REPORT.md` once for hubs/modules when scoping a
21
+ fan-out. Then Grep/Read only for detail the graph lacks (dynamic imports,
22
+ stringly keys, generated code, languages outside the graph walk).
23
+
24
+ If the graph is missing and the session will do heavy exploration, prefer
25
+ `interlock-graph build .` (or `/interlock:graph rebuild`) once over repeated blind greps.
26
+ Do **not** rebuild on every query when the graph already exists — use
27
+ `interlock-graph update` after large merges.
28
+
29
+ ## Rule 0.5 — Budgeted documentation retrieval (don't preload `docs/`)
30
+
31
+ Do **not** read all `docs/*.md` or concatenate OpenSpec markdown into context
32
+ upfront. Prefer the agent-only digest, then retrieve cited excerpts on demand.
33
+
34
+ **Digest-first ladder**
35
+
36
+ 1. If `.claude/graph/DOCS_DIGEST.md` exists → Read it **once** as the default docs
37
+ primer (bounded agent-only file; prose twin of `GRAPH_REPORT.md`). Do not also
38
+ preload raw `docs/`.
39
+ 2. Task-specific drill-down → budgeted retrieve:
40
+
41
+ ```bash
42
+ interlock-graph context "<task tokens>" --budget 2000 --changed path/a.ts,path/b.ts
43
+ interlock-graph docs "<domain terms>" --budget 800
44
+ ```
45
+
46
+ 3. If digest missing/stale and the session needs domain prose → run `/interlock:docs-digest`
47
+ (rebuilds via `interlock-graph docs-index` + compress), else proceed with `context` only.
48
+ 4. Fallback when retrieve is empty (at most one query rewrite, then stop sprawl):
49
+ - broaden terms once (`auth` → `authentication session`)
50
+ - `grep` headings in `docs/` / `openspec/`, then Read spans
51
+ - if graph missing: `docs` only, or read `docs/architecture.md` headings via grep
52
+
53
+ > **Widening is a deliberate position, not an oversight.** [The Spec Growth
54
+ > Engine](https://arxiv.org/abs/2606.27045) §5.3 argues the opposite: *"the
55
+ > default repair for an insufficient context is to fix the spec graph, not to
56
+ > widen the context."* That is right when every module is required to carry a
57
+ > machine-readable contract — an empty retrieval then means the architecture has
58
+ > a hole, and widening hides it. Interlock targets brownfield repos where the
59
+ > spec graph is incomplete by construction, and "fix the spec graph" is not an
60
+ > action available mid-task. So we widen once, bounded, and stop. Take the
61
+ > paper's repair instead whenever the spec genuinely should have covered it.
62
+
63
+ **Default budgets**
64
+
65
+ | Command | Budget | Role |
66
+ |---------|--------|------|
67
+ | `DOCS_DIGEST.md` | ~2500 soft / ~3200 hard | Agent-only prose bootstrap (via `/interlock:docs-digest`) |
68
+ | `context` | 2000 tokens | Combined structural + documentation bundle |
69
+ | `docs` | 800 tokens | Prose/domain context only |
70
+ | `query` / `consumers` | 1500 tokens | Structural navigation |
71
+ | `docs-index` | (index JSON) | Deterministic TOC/hashes for digest rebuild |
72
+
73
+ **Provenance** — every excerpt must cite `file:heading (Lstart-Lend)`.
74
+ Treat graph `INFERRED` edges as hints; verify with Read spans before claims.
75
+
76
+ **Authoritative artifact exception** — when implementing against an active OpenSpec
77
+ change, still read **all** of `proposal.md`, `design.md`, `tasks.md`, and delta
78
+ specs for that change first. Rule 0.5 replaces exploratory preload, not the
79
+ implementation contract leash (`/interlock:ship`).
80
+
81
+ **Batch gathers** — a non-interactive run that needs project prose should pull a
82
+ single bounded bundle via `interlock-graph context`, never `find docs -exec cat`.
83
+ If `context` fails, prefer `.claude/graph/DOCS_DIGEST.md` over reading the head
84
+ of an architecture doc.
85
+
86
+ ## Rule 1 — Locate before you Read
87
+
88
+ Use `grep` / the Grep tool to find the file and line first, then Read with
89
+ `offset`/`limit` around the hit. Do **not** Read whole files sequentially to
90
+ discover where something lives. Read a file in full only once you know it's the
91
+ one you need. When Rule 0 applies, graph query/consumers counts as “locate.”
92
+
93
+ ## Rule 2 — Batch and background your Bash
94
+
95
+ - **Independent read-only commands** go in one message (multiple tool calls) or
96
+ joined with `;` — not `&&`. `&&` short-circuits: if an early command exits
97
+ non-zero, the later ones never run and their output is silently lost. For a
98
+ status sweep (`git branch`, `git status`, `git log`, `openspec status`) you
99
+ want them all regardless, so batch them.
100
+ - **Reserve `&&` for dependency-ordered chains** where a later step is meaningless
101
+ if an earlier one failed — e.g. `npm run typecheck && npm run lint && npm test
102
+ && npm run build`.
103
+ - **Quote shell globs** in filter arguments (`--include='*.ts'`,
104
+ `--glob '*.ts'`) so zsh does not expand them before the command sees them.
105
+ Prefer the Grep tool for `--include`-style file filtering when available.
106
+ - **Background the long-running.** Builds, full test suites, and installs run with
107
+ `run_in_background` so you can continue other work while they execute.
108
+
109
+ ## Rule 3 — Read state, don't re-derive it
110
+
111
+ At session start, current-task context comes from a few cheap, authoritative
112
+ sources — `git log --oneline`, `git status --short`, `openspec status --json`,
113
+ an open-task grep over `openspec/changes/*/tasks.md`, and the latest handoff.
114
+ Front-load these in one pass. Read them,
115
+ then act. Do not re-traverse the tree to reconstruct what they already tell you.
116
+
117
+ **Explore brief as Rule-3 state:** when `/interlock:spec` runs after explore
118
+ (especially after `/clear` or prompt-cache expiry), read the latest matching
119
+ `.claude/handoff/explore-*.md` first (see `EXPLORE-BRIEF.md`).
120
+ Prefer the brief’s recommendations over re-deriving discovery from the codebase
121
+ or chat. Reopen code only for gaps or contradictions. Writing that brief in
122
+ autonomous explore is how explore pays forward — spec should not re-pay.
123
+
124
+ ## Rule 4 — Spilled verify output: locator then Read spans, never the whole file
125
+
126
+ `interlock verify spill` writes a command's full stdout/stderr to
127
+ `.claude/ship/spill/<runId>/<seq>-<kind>.log` once it passes
128
+ `LIMITS.verifySpillBytes`, and hands back `{ locator, preview, bytes, sha256 }`.
129
+ The `preview` is already a head-and-tail slice bounded by
130
+ `LIMITS.verifyPreviewChars` — read it from the result, do not re-open the
131
+ locator to get what the preview already gives you.
132
+
133
+ Only reopen the locator when the preview genuinely does not answer the
134
+ question (a failure whose stack trace was in the omitted middle). Then Read it
135
+ the same way Rule 1 says: with `offset`/`limit` around the span you need, not
136
+ the whole file. A spilled suite log can be hundreds of KB — reading it in full
137
+ defeats the reason it was spilled instead of pasted into the result.
138
+
139
+ Never paste spilled bytes back into a verify result field (`detail`,
140
+ `cliStdout`, `reason`, `stdout`, `stderr`, `output`, or a `failures[]` entry).
141
+ `interlock verify judge` / `verify unit` check every one of those fields
142
+ against the preview budget and reject the result outright — "oversized result
143
+ field(s)" — if any exceeds it. The locator is not a courtesy; the anti-swallow
144
+ check enforces it.
145
+
146
+ ## When NOT to apply
147
+
148
+ This targets *wasted* exploration, not legitimate reconnaissance:
149
+
150
+ - Genuine cross-subsystem investigation still warrants a fan-out
151
+ (`/interlock:explore`). Rule 1 applies *within* each investigator; the
152
+ decision to fan out is unchanged.
153
+ - The artifact leash still holds: when you're about to implement against a
154
+ proposal/design/tasks set, read all of it first (see `/interlock:ship`).
155
+ That is not the sprawl this file is about.
@@ -0,0 +1,226 @@
1
+ ---
2
+ name: bootstrap
3
+ 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.
4
+ license: MIT
5
+ compatibility: Requires the openspec CLI. Node.js >= 18 for the bundled interlock-graph CLI.
6
+ argument-hint: "[--quick] [--scope <path>]"
7
+ allowed-tools: Bash(openspec *) Bash(interlock-graph *) Bash(git check-ignore *) Agent Read Write Glob Grep
8
+ metadata:
9
+ type: discovery
10
+ outputs:
11
+ - openspec/initial-architecture.md
12
+ - openspec/specs/<feature>/spec.md
13
+ ---
14
+
15
+ Document what a codebase **already is**, in OpenSpec form, so that every later change has something to change *from*.
16
+
17
+ 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.
18
+
19
+ | Flag | Effect |
20
+ |------|--------|
21
+ | `--quick` | Sequential, single-context. For small repos (< ~50 source files). |
22
+ | `--scope <path>` | Restrict the whole run to a subdirectory. |
23
+ | *(none)* | Full parallel run. |
24
+
25
+ ---
26
+
27
+ ## 0. Pre-flight
28
+
29
+ ```bash
30
+ # Initialize OpenSpec if this repo has never been initialized.
31
+ # `openspec init` creates openspec/config.yaml, so its absence is the real signal.
32
+ test -f openspec/config.yaml || openspec init --tools claude
33
+
34
+ # Build the structural graph once — it seeds every explorer below.
35
+ # Never swallow the outcome: stderr stays visible, and both failure modes
36
+ # (build errored / nothing indexable) get a banner.
37
+ graph_log=$(interlock-graph build .)
38
+ graph_status=$?
39
+ printf '%s\n' "$graph_log"
40
+ if [ "$graph_status" -ne 0 ]; then
41
+ echo "GRAPH UNAVAILABLE: build errored (exit $graph_status) — explorers fall back to grep and will be slower"
42
+ elif printf '%s' "$graph_log" | grep -q 'nodes=0'; then
43
+ echo "GRAPH UNAVAILABLE: nothing indexable found — explorers fall back to grep and will be slower"
44
+ fi
45
+ ```
46
+
47
+ **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.
48
+
49
+ `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 and shell). Say which of the two reasons applied rather than presenting either as a defect.
50
+
51
+ 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.
52
+
53
+ Then check whether `openspec/specs/` already has content. If it does:
54
+
55
+ > "This project already has N specs. Bootstrap will ADD specs for undocumented features but will never overwrite an existing one. Continue?"
56
+
57
+ ---
58
+
59
+ ## 1. DISCOVER — parallel exploration
60
+
61
+ Spawn five explorer subagents via the Agent tool (`subagent_type: "Explore"`), or run them in sequence under `--quick`. Each returns structured JSON.
62
+
63
+ **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.
64
+
65
+ Shared skeleton — fill `{role}`, `{task}`, `{schema}` from the table:
66
+
67
+ ```
68
+ You are the {role} explorer for this repo. Phrase findings in this stack's vocabulary.
69
+ {task}
70
+ Prefer GRAPH_REPORT.md / interlock-graph when a graph exists; otherwise grep.
71
+ Return JSON only: {schema}
72
+ ```
73
+
74
+ | Role | Task | JSON schema |
75
+ |------|------|-------------|
76
+ | 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 }` |
77
+ | 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[] }` |
78
+ | Pattern Extractor | Recurring composition (components/handlers/services), state/lifecycle, errors, auth, data fetch/mutate, testing, code style. | `{ compositionPatterns[], stateManagement, errorHandling, auth, dataFlow, testing, codeStyle }` |
79
+ | Interface Scanner | External surface: endpoints and protocol, mutations, schemas/validators, public exports/SDK, realtime/streaming, CLI. | `{ endpoints[], mutations[], schemas[], publicExports[], realtime[], cli[] }` |
80
+ | 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 }` |
81
+
82
+ Cap: 8 agents in this phase.
83
+
84
+ ---
85
+
86
+ ## 2. SYNTHESIZE — the architecture document
87
+
88
+ One synthesis pass reads all five outputs and writes `openspec/initial-architecture.md`:
89
+
90
+ ```markdown
91
+ # Initial Architecture
92
+
93
+ > Auto-generated by /interlock:bootstrap on {date}.
94
+ > Point-in-time snapshot, not a living document.
95
+
96
+ ## Stack & Infrastructure
97
+ ## Project Organization
98
+ ## Architecture Overview <- ASCII diagram, subsystem boundaries, data flow
99
+ ## Data Model
100
+ ## Subsystems & Features <- table: subsystem | location | responsibility | patterns
101
+ ## Established Patterns <- composition, data flow, state, errors, auth, testing
102
+ ## Interface Surface
103
+ ## Design Decisions (Inferred)
104
+ - [EXTRACTED] stated outright in comments or docs
105
+ - [INFERRED] deduced from a consistent pattern
106
+ - [AMBIGUOUS] unclear — several patterns coexist
107
+ ## Gaps & Risks <- incomplete work, conflicting patterns, hazards
108
+ ```
109
+
110
+ Tag every design decision with its confidence. An `[INFERRED]` decision that turns out wrong is recoverable; one presented as fact is not.
111
+
112
+ ---
113
+
114
+ ## 3. IDENTIFY FEATURES — boundary detection
115
+
116
+ From the synthesis, pick the units that each deserve a spec:
117
+
118
+ - **Entry-point based** — each major route group, command, or consumer
119
+ - **Domain based** — each data model with a full lifecycle
120
+ - **Capability based** — cross-cutting concerns (auth, observability, error handling)
121
+ - **Module based** — feature directories and barrel exports
122
+
123
+ Present the list for confirmation before generating:
124
+
125
+ ```
126
+ Discovered N features to spec:
127
+ 1. authentication — login, register, session management
128
+ 2. task-crud — create, read, update, delete tasks
129
+ 3. app-shell — layout, navigation, route protection
130
+
131
+ Generate specs for all, or select specific ones?
132
+ ```
133
+
134
+ ---
135
+
136
+ ## 4. SPEC GENERATION — one agent per feature
137
+
138
+ For each confirmed feature, spawn a subagent (cap: 12).
139
+
140
+ > You are writing an OpenSpec spec for an **existing** feature. Document what IS built, not what SHOULD be.
141
+ > Feature: `{name}`. Architecture context: `{relevant section}`. Source files: `{paths}`.
142
+ > 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.
143
+
144
+ Write to `openspec/specs/<feature>/spec.md`. **Never overwrite an existing spec file.** If one exists, skip it and report the skip.
145
+
146
+ Then validate:
147
+
148
+ ```bash
149
+ openspec validate
150
+ ```
151
+
152
+ ---
153
+
154
+ ## 5. UPDATE PROJECT CONTEXT
155
+
156
+ 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.
157
+
158
+ ---
159
+
160
+ ## 6. REPORT
161
+
162
+ - Any `GRAPH UNAVAILABLE: <reason> — explorers fall back to grep and will be slower` banner from pre-flight, repeated verbatim. If the graph built, say so in one line instead — a report that mentions neither reads like a graph-backed run that never happened.
163
+ - Architecture doc path and what it covers
164
+ - Specs created, and specs skipped because they already existed
165
+ - Features deliberately not specced, with the reason
166
+ - `openspec validate` result
167
+ - Corpus-persistence posture (below)
168
+ - Suggested next step: `/interlock:docs-digest` to build the agent prose primer
169
+
170
+ ### Corpus-persistence posture
171
+
172
+ This is the one moment the repository is looked at before any run has written to
173
+ it, so raise the question now: will its run corpora be kept?
174
+
175
+ Observe the current state with git's own matcher — do **not** read or parse
176
+ `.gitignore`. Its semantics include negation, directory-only trailing slashes,
177
+ nested files, `.git/info/exclude` and the user's global excludes file, and a
178
+ hand-rolled match gets some of those wrong:
179
+
180
+ ```bash
181
+ git check-ignore -q .claude/ship/ ; echo "ship=$?"
182
+ git check-ignore -q .claude/learning/ ; echo "learning=$?"
183
+ git check-ignore -q .claude/metrics/ ; echo "metrics=$?"
184
+ ```
185
+
186
+ Exit 0 means the path is excluded, 1 means it is tracked territory.
187
+
188
+ Report **each of the three separately** — never collapse them into one verdict.
189
+ A split state, where one is excluded and the others are not, is the case most
190
+ likely to be unintentional, and collapsing hides exactly that.
191
+
192
+ State the recommendation as **conditional**, never as a verdict about this
193
+ repository. Nothing you can observe tells you whether the repo will run
194
+ `/interlock:ship` against its own product, so say which posture fits which
195
+ intent and let the reader choose:
196
+
197
+ > `.claude/ship/` and `.claude/metrics/` are excluded, `.claude/learning/` is
198
+ > not. If this repository will run `/interlock:ship` on its own product, those
199
+ > exclusions drop the audit trail — remove them from `.gitignore`. If it is a
200
+ > harness whose ship runs are development exhaust, add `.claude/learning/` so
201
+ > all three match. Rationale and both `.gitignore` blocks: docs/11, "Whether to
202
+ > keep them".
203
+
204
+ **Never write to `.gitignore`.** Not to create it, not to append, not to "fix"
205
+ a split state. Name the entries a person could add and let them add them — this
206
+ skill was invoked to produce specs, and an edit to an unrelated file is a
207
+ surprise in someone's diff, not a service.
208
+
209
+ If any `git check-ignore` exits non-zero for a reason other than "not ignored"
210
+ — an unreadable `.gitignore`, or a directory that is not a git repository at all
211
+ — report the posture as **undetermined and say why**, then carry on. Bootstrap
212
+ completes and writes every artifact it would otherwise have written; a failed
213
+ check never fails the onboarding.
214
+
215
+ ---
216
+
217
+ ## Guardrails
218
+
219
+ - **Never overwrite an existing spec.** Add only.
220
+ - **Never modify source code.** This skill is read-only outside `openspec/` and `.claude/graph/`.
221
+ - Respect `.gitignore`; skip vendored and generated trees.
222
+ - Max 8 explorer agents in phase 1, 12 spec agents in phase 4.
223
+ - **Idempotent.** Re-running adds newly-discovered features and leaves everything else alone.
224
+ - If a feature's behavior can't be determined from the code, say so in the spec rather than inventing a requirement.
225
+
226
+ Follow `${CLAUDE_PLUGIN_ROOT}/shared/TOOL-ECONOMY.md`.
@@ -0,0 +1,99 @@
1
+ ---
2
+ name: commit
3
+ description: Create a single feature-level commit after a change is implemented. Reads the OpenSpec change artifacts to write a verb-phrase conventional-commit message with an outcome summary and optional issue reference. Commit only — never runs tests, typecheck, lint or build, and never pushes.
4
+ license: MIT
5
+ compatibility: Requires git. Optional openspec CLI for change context.
6
+ argument-hint: "[ISSUE-REF] [--yes]"
7
+ disable-model-invocation: true
8
+ allowed-tools: Bash(git *) Bash(openspec *) Bash(interlock *) Read Glob
9
+ metadata:
10
+ type: execution
11
+ ---
12
+
13
+ Write one good commit for a completed change.
14
+
15
+ **Commit only.** This skill never runs tests, typecheck, lint, or a build — not in any mode, `--yes` included. Something else already verified; if nothing did, that is a gap in the caller, not work for this skill to absorb.
16
+
17
+ `--yes` runs non-interactively: no confirmation prompt. It is **not** permission to push or amend.
18
+
19
+ ---
20
+
21
+ ## 1. Resolve the change and the issue reference
22
+
23
+ Resolve the change by trying, in order:
24
+
25
+ 1. An explicit change name argument
26
+ 2. The current branch name (`feat/<name>`, `fix/<name>`)
27
+ 3. The most recently modified `openspec/changes/*/` directory
28
+
29
+ Ask only when the session is interactive **and** all three fail. Under `--yes`, never ask — report which rungs were tried and stop with a one-line message.
30
+
31
+ Extract an issue reference from the branch when one was not passed:
32
+ `feat/RD-65-task-page` → `RD-65` · `fix/PROJ-123-login-bug` → `PROJ-123`
33
+
34
+ If the branch looks like a feature branch but no reference was found:
35
+ > ⚠️ No issue reference found. Branch `<name>` looks like a feature branch — pass one as `/interlock:commit <REF>` to avoid rewriting the message later.
36
+
37
+ Interactive: wait for the user to confirm or supply one. Under `--yes`: emit that single line, **proceed without a ref**, and surface it again in the final report. On `main`, `develop`, or any branch without a `feat/`/`fix/` prefix, proceed silently — the reference is genuinely optional there.
38
+
39
+ ---
40
+
41
+ ## 2. Gather context
42
+
43
+ ```bash
44
+ openspec status --change "<name>" --json
45
+ git status --short
46
+ git diff --stat
47
+ ```
48
+
49
+ Read `proposal.md` for the one-line intent, `design.md` for scope, and the checked items in `tasks.md` for what was actually done. Inspect the diff to confirm the message matches reality rather than matching the plan.
50
+
51
+ ---
52
+
53
+ ## 3. Compose the message
54
+
55
+ ```
56
+ <type>(<scope>): <verb phrase, imperative, lowercase>
57
+
58
+ Summary of changes:
59
+ - <outcome, not task completion>
60
+ - <outcome>
61
+
62
+ Refs: <ISSUE-REF>
63
+ ```
64
+
65
+ - **Scope** is the issue reference when one exists (`feat(RD-65): …`), otherwise the primary module or feature area (`feat(tasks): …`).
66
+ - **Summary bullets describe outcomes**, not "completed task 2.3".
67
+ - **`Refs:` line** appears only when a reference exists. Omit the line entirely otherwise — never write `Refs: none`.
68
+
69
+ | | |
70
+ |---|---|
71
+ | Good | `feat(RD-65): add task list page with filtering and status badges` |
72
+ | Good | `fix(PROJ-123): resolve token refresh race condition` |
73
+ | Good | `feat(tasks): add task list page with filtering` |
74
+ | Bad | `feat(tasks): Task List Page` — not a verb phrase |
75
+ | Bad | `feat(tasks): added the new task list page` — past tense, wordy |
76
+
77
+ ---
78
+
79
+ ## 4. Preview and commit
80
+
81
+ Show the full message. Interactive: ask "Commit with this message?" and allow edits. Under `--yes`: print it and commit.
82
+
83
+ Stage by naming **every path explicitly**, then commit.
84
+
85
+ ---
86
+
87
+ ## Guardrails
88
+
89
+ - **Never `git add -A` or `git add .`.** Name every path. Leave unrelated dirty files unstaged.
90
+ - **Never push.** `--yes` is not that permission.
91
+ - **Never amend** unless the user explicitly asks and the amend conditions hold. `--yes` is not that permission.
92
+ - **Never run tests, typecheck, lint, or build** — in any mode.
93
+ - **Never re-implement or quick-fix failing code here.** If the tree looks broken: interactive, warn and ask whether to commit anyway; under `--yes`, commit and report the observation. Never start a verify/fix loop either way.
94
+
95
+ ---
96
+
97
+ ## 5. Report
98
+
99
+ Commit hash and subject, files committed, anything left unstaged, and the warning from step 1 if it fired. Suggest the next step: `/interlock:mr` to open or update the merge request.
@@ -0,0 +1,78 @@
1
+ ---
2
+ name: dispatch
3
+ description: Route a request to the right interlock skill, after one batched pre-flight that loads git state, OpenSpec status, open tasks and graph presence in a single pass. Use when you are not sure which skill applies, or at the start of a session to orient before doing anything else.
4
+ license: MIT
5
+ compatibility: Optional openspec CLI and git. Node.js >= 18 for the bundled interlock CLI.
6
+ allowed-tools: Bash(git *) Bash(openspec *) Bash(interlock *) Bash(interlock-graph *) Read Glob
7
+ metadata:
8
+ type: routing
9
+ ---
10
+
11
+ Figure out where the user actually is in the flow, then route. One cheap pre-flight beats an exploratory warm-up.
12
+
13
+ ---
14
+
15
+ ## Step 0 — Pre-flight, batched
16
+
17
+ Run these **in one turn**, joined with `;` not `&&` — you want every answer regardless of which ones fail:
18
+
19
+ ```bash
20
+ git branch --show-current; git status --short; git log --oneline -5
21
+ openspec list --json 2>/dev/null
22
+ interlock changes 2>/dev/null
23
+ ls -t .claude/handoff/explore-*.md 2>/dev/null | head -3
24
+ ls .claude/graph/GRAPH_REPORT.md .claude/graph/DOCS_DIGEST.md 2>/dev/null
25
+ ```
26
+
27
+ That is Rules 2 and 3 of `${CLAUDE_PLUGIN_ROOT}/shared/TOOL-ECONOMY.md`: read state, do not re-derive it. Do not traverse the tree to reconstruct what these already tell you.
28
+
29
+ ---
30
+
31
+ ## The flow
32
+
33
+ ```
34
+ graph → docs-digest → bootstrap (once per repo)
35
+ /interlock:spec explore → artifacts → review ⟵ STOPS. Human reviews.
36
+ /interlock:ship waves → verify → commit (workflow, zero-touch; --strict adds review)
37
+ /interlock:mr summary → merge request
38
+ ```
39
+
40
+ The gap between `spec` and `ship` is deliberate. Never bridge it automatically — if a spec is ready, say so and let the user start ship. The user can bridge it themselves with `/interlock:spec --continue`, but that is theirs to ask for; routing never offers it.
41
+
42
+ `ship` is a dynamic workflow. `/interlock:ship` is a skill trampoline that launches it via the Workflow tool (`scriptPath` → `workflows/ship.js`). Invoke the **skill** `interlock:ship` (or call Workflow with that scriptPath yourself). Do not treat a missing Workflow-namespaced command as "ship does not exist" — the skill is the entry point. If the trampoline halts because the Workflow tool is unavailable, that is the runtime gap (v2.1.154+, Dynamic workflows on in `/config`); do not implement the loop inline.
43
+
44
+ ---
45
+
46
+ ## Routing table
47
+
48
+ | Situation | Route to |
49
+ |---|---|
50
+ | No `openspec/` directory, or no specs on an existing codebase | `/interlock:bootstrap` |
51
+ | No `.claude/graph/`, and the session will explore or review heavily | `/interlock:graph` (build) |
52
+ | Docs context too large, or the digest is stale | `/interlock:docs-digest` |
53
+ | "How does X work?", "what breaks if I change Y?", tracing, comparing options | `/interlock:explore` |
54
+ | "I want to build / fix X" — no change exists yet | `/interlock:spec` |
55
+ | A change exists with reviewed artifacts and unchecked tasks | `/interlock:ship` — only if the user asked to ship, and this conversation has not already run ship |
56
+ | Leftover unchecked boxes after a ship workflow just returned | Report them. Do not route to ship. |
57
+ | Implementation done, diff needs review | `/interlock:review-code` |
58
+ | Artifacts written but not yet reviewed | `/interlock:review-artifacts` |
59
+ | Suite is red, or a CI log was pasted | `/interlock:fix-tests` |
60
+ | "Explain this code" / "what changed in that commit?" | `/interlock:explain-code` |
61
+ | Need a QA checklist for a change | `/interlock:manual-test-plan` |
62
+ | Work is done and needs committing | `/interlock:commit` |
63
+ | MR or PR needs creating or updating | `/interlock:mr` |
64
+ | "I want to skip the checkpoint" / "don't stop and make me read it" | `/interlock:spec --continue` — **advanced**. Opt-in, fail-closed, and it stops anyway on anything a machine cannot settle. It cannot catch a wrong idea; only the read can. |
65
+
66
+ **Prerequisite checks before routing:**
67
+
68
+ - Routing to `ship` — run `interlock validate <change>` first. Not ready means route to `spec`, not ship. If a ship workflow already returned in this conversation, leftover boxes are a report, not a retry.
69
+ - Routing to `spec` with no explore brief and a question that spans subsystems — run `explore` first.
70
+ - Routing to anything that reads the graph when `.claude/graph/` is absent — offer to build it once.
71
+
72
+ ---
73
+
74
+ ## Ambiguity
75
+
76
+ When two routes fit, prefer the **earlier phase**. Exploring when you should have specced costs one turn; shipping when you should have specced costs a bad change and a review cycle.
77
+
78
+ When the request genuinely does not fit any skill, say so and handle it directly. Not everything is a workflow, and forcing an ordinary question through a skill is worse than just answering it.
@@ -0,0 +1,109 @@
1
+ ---
2
+ name: docs-digest
3
+ description: Compress a repo's docs and OpenSpec specs into one token-dense, agent-only primer at .claude/graph/DOCS_DIGEST.md — the prose twin of GRAPH_REPORT.md. Use when docs context is too large to load, after substantial docs or spec edits, or when asked to rebuild or refresh the docs digest. Not for producing human-readable documentation.
4
+ license: MIT
5
+ compatibility: Requires Node.js >= 18. The interlock-graph CLI ships with this plugin and is on PATH automatically.
6
+ allowed-tools: Bash(interlock-graph *) Read Write
7
+ metadata:
8
+ type: discovery
9
+ outputs:
10
+ - .claude/graph/docs-index.json
11
+ - .claude/graph/DOCS_DIGEST.md
12
+ ---
13
+
14
+ Produce **one agent-only primer** that lets a fresh agent understand this repo's documented domain without reading `docs/` end to end.
15
+
16
+ `GRAPH_REPORT.md` answers *what connects to what*. This answers *what this project means*. They are siblings, both under `.claude/graph/`.
17
+
18
+ **Inputs:** `docs/**` and `openspec/specs/**`, via the deterministic index.
19
+ **Output:** `.claude/graph/DOCS_DIGEST.md`, overwritten in place.
20
+ **Never indexed:** `openspec/changes/**`. Active change artifacts are read directly, not digested.
21
+
22
+ Run this when: the digest is missing or stale, docs or living specs changed substantially, or the user asks to rebuild it. **Do not run it per query** — interactive skills should read an existing fresh digest, not regenerate one.
23
+
24
+ ---
25
+
26
+ ## Execution
27
+
28
+ ### 1. Index (deterministic — no model judgement)
29
+
30
+ ```bash
31
+ interlock-graph docs-index . --write
32
+ ```
33
+
34
+ Writes `.claude/graph/docs-index.json` covering `docs/` and `openspec/specs/`, with a `sha256` and heading spans per file.
35
+
36
+ If the index is empty, stop and report that: a repo with no `docs/` and no `openspec/specs/` has nothing to digest, and inventing one is worse than having none.
37
+
38
+ ### 2. Staleness check
39
+
40
+ If `.claude/graph/DOCS_DIGEST.md` exists, compare its frontmatter `source_hashes` against each `files[].sha256` in the index. All match, and the user didn't force a refresh? Report fresh and stop.
41
+
42
+ Rebuild when: hashes mismatch, the digest is missing, or the user says rebuild / refresh / force.
43
+
44
+ ### 3. Selective read — never concatenate the corpus
45
+
46
+ Read `.claude/graph/docs-index.json` in full. It is an inventory, not the corpus.
47
+
48
+ Then read **section spans only** (`offset`/`limit` from each entry's `startLine`/`endLine`), highest signal first. Rank the index entries yourself using these signals, in order:
49
+
50
+ 1. **Entry points.** A file whose path or title matches `readme`, `architecture`, `overview`, `getting-started`, `contributing`, or `adr` — these state intent and structure.
51
+ 2. **Inbound references.** Files linked to most often from other indexed files. The index records outbound links; invert them. A doc everything points at is load-bearing.
52
+ 3. **Spec coverage.** Every `openspec/specs/*/spec.md` capability gets at least its title and requirement blurbs — the capability map is the single most useful thing in the digest.
53
+ 4. **Breadth over depth.** Prefer one section from each of ten documents over ten sections from one. You are building a map, not a summary.
54
+
55
+ Drop first under token pressure: long analysis or research write-ups, changelogs, meeting notes, anything whose blurb already conveys the point, and peripheral spec capabilities. For those, use the index blurb instead of reading a span.
56
+
57
+ Rank what the repo actually contains. Do not assume any particular filename exists.
58
+
59
+ **Forbidden:** `find docs -exec cat`, reading every `docs/**/*.md` or `openspec/specs/**/*.md` end to end, reading anything under `openspec/changes/`, and writing output anywhere under `docs/` or `openspec/`.
60
+
61
+ ### 4. Write the digest
62
+
63
+ Overwrite `.claude/graph/DOCS_DIGEST.md` with exactly this contract:
64
+
65
+ ```markdown
66
+ ---
67
+ audience: agent-only
68
+ target_tokens: 2500
69
+ generated_at: <ISO-8601>
70
+ source_hashes:
71
+ <path>: <sha256>
72
+ # one entry per indexed file
73
+ ---
74
+ # docs-digest
75
+ ## map
76
+ - <short-key> → <path>
77
+ ## facts
78
+ - <dense telegraphic facts; ;-joined clauses fine>
79
+ ## workflows
80
+ - <agent-relevant workflows and constraints>
81
+ ## specs
82
+ - <capability> → key requirements / constraints (telegraphic, pointer-heavy)
83
+ ## pointers
84
+ - <path>#<heading> Lstart-Lend
85
+ ```
86
+
87
+ Omit `## specs` entirely when the index contains no `openspec/specs/` files.
88
+
89
+ **Style — this is read by agents, not people.** Telegraphic. Abbreviations fine. Lists and `→`. No tutorial tone, no narrative padding, no worked examples. Prefer a pointer over a paraphrase whenever the reader could drill down with `interlock-graph context` or a Read span.
90
+
91
+ **Budgets:** soft cap ~2500 tokens (`ceil(chars/4)`), hard stop ~3200. Drop lowest-priority content first. Never state a fact that isn't in the index or a span you actually read.
92
+
93
+ ### 5. Report (≤10 lines)
94
+
95
+ Output path, token estimate, file count split by `docs/` vs `openspec/specs/`, fresh-vs-rebuilt, and a reminder that humans should keep reading `docs/` — this artifact is not for them.
96
+
97
+ ---
98
+
99
+ ## Relation to the graph skill
100
+
101
+ | Need | Use |
102
+ |------|-----|
103
+ | Prose bootstrap / domain primer | This digest (`DOCS_DIGEST.md`) |
104
+ | Structural bootstrap | `GRAPH_REPORT.md`, `interlock-graph query` |
105
+ | Task-specific cited excerpts | `interlock-graph context` / `docs` |
106
+ | Active change artifacts | Read `openspec/changes/<name>/` directly — **never** this digest |
107
+ | TOC and hashes for this skill | `interlock-graph docs-index` |
108
+
109
+ Follow `${CLAUDE_PLUGIN_ROOT}/shared/TOOL-ECONOMY.md`.