@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,136 @@
1
+ ---
2
+ name: graph
3
+ description: Build and query a local, deterministic codebase knowledge graph (imports, symbols, OpenSpec specs, memory) so agents navigate with token-budgeted subgraphs instead of re-grepping. Use when indexing a repo, finding who consumes a symbol, tracing how one module reaches another, or before a deep explore or review sweep.
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 Grep Glob
7
+ metadata:
8
+ type: discovery
9
+ outputs:
10
+ - .claude/graph/graph.json
11
+ - .claude/graph/GRAPH_REPORT.md
12
+ - .claude/graph/docs-index.json
13
+ ---
14
+
15
+ Build and query a **local, deterministic** codebase knowledge graph. No vector store, no external service, no network.
16
+
17
+ **What gets indexed:** JS/TS, Python (`.py`/`.pyi`), shell (`.sh`/`.bash`), and curated small config JSON (`package.json`, `tsconfig*.json` — not lockfiles). Markdown stays on the bounded-retrieval path (`DOCS_DIGEST.md` / `context` / `docs`) plus the OpenSpec overlay — never dump every `*.md` into `graph.json`.
18
+
19
+ Languages outside that set (Go, Rust, Java, Ruby) are **not** structurally indexed: you still get the docs and OpenSpec overlays, but no import/symbol edges. Say so plainly rather than implying the graph is complete.
20
+
21
+ **Artifacts, all project-local under `.claude/graph/`:**
22
+
23
+ | Path | Role |
24
+ |------|------|
25
+ | `graph.json` | Node-link graph — the query source of truth |
26
+ | `GRAPH_REPORT.md` | Cheap bootstrap: hubs, modules, inferred edges |
27
+ | `docs-index.json` | Deterministic docs TOC + content hashes |
28
+ | `DOCS_DIGEST.md` | Agent-only prose bootstrap (written by `/interlock:docs-digest`) |
29
+ | `manifest.json` | File hashes powering incremental `update` |
30
+
31
+ Add `.claude/graph/` to `.gitignore` — these are generated per-checkout and should never be committed.
32
+
33
+ ---
34
+
35
+ ## The CLI
36
+
37
+ `interlock-graph` is on PATH whenever this plugin is enabled. Do not probe for it, do not build a fallback path, do not shell out to `node` with an absolute path.
38
+
39
+ ```bash
40
+ interlock-graph --help # full subcommand list
41
+ interlock-graph which # print the resolved CLI path, if you need to report it
42
+ ```
43
+
44
+ ---
45
+
46
+ ## Modes
47
+
48
+ ### Build — when the graph is missing, or the user asks to index
49
+
50
+ ```bash
51
+ interlock-graph build .
52
+ ```
53
+
54
+ Then read `.claude/graph/GRAPH_REPORT.md` and summarize the top hub nodes and module boundaries. **Do not rebuild on every query.** If `graph.json` already exists, query it or update it.
55
+
56
+ ### Update — after merges, or when paths look stale
57
+
58
+ ```bash
59
+ interlock-graph update .
60
+ ```
61
+
62
+ No-ops when file hashes still match `manifest.json`.
63
+
64
+ ### Query — the default mode
65
+
66
+ For "where is X", "what connects to Y", "how does A reach B", "who uses Z":
67
+
68
+ 1. Expand the user's wording into graph vocabulary (use labels from a prior `query` or from `GRAPH_REPORT.md`).
69
+ 2. Run the matching subcommand:
70
+
71
+ ```bash
72
+ interlock-graph query "<tokens>" --budget 1500
73
+ interlock-graph consumers <symbol-or-file>
74
+ interlock-graph path <A> <B>
75
+ interlock-graph explain <node>
76
+ ```
77
+
78
+ 3. Answer from the returned subgraph. Cite `source_file:source_location`.
79
+ 4. Only then reach for Grep/Read to cover what the graph structurally cannot see: dynamic imports, string-keyed lookups, generated code, reflection.
80
+
81
+ ### Docs and context — bounded prose retrieval
82
+
83
+ Never read all of `docs/*.md` upfront.
84
+
85
+ 1. **Prose bootstrap** — if `.claude/graph/DOCS_DIGEST.md` exists, read it once. Rebuild with `/interlock:docs-digest` when it is missing or stale.
86
+ 2. **Rebuild the deterministic TOC** the digest is derived from:
87
+
88
+ ```bash
89
+ interlock-graph docs-index . --write
90
+ ```
91
+
92
+ 3. **Task-specific excerpts:**
93
+
94
+ ```bash
95
+ interlock-graph context "<task tokens>" --budget 2000 --changed path/a.ts,path/b.ts
96
+ interlock-graph docs "<domain terms>" --budget 800 --dirs docs,openspec/specs
97
+ ```
98
+
99
+ `context` combines a structural subgraph (~45% of budget) with ranked Markdown sections (~55%). Sections cite `file — heading (Lstart-Lend)`.
100
+
101
+ ---
102
+
103
+ ## Arguments
104
+
105
+ This skill takes free text, treated as a query. Two forms are recognized ahead of that:
106
+
107
+ | Input | Action |
108
+ |-------|--------|
109
+ | "index the repo" / "build the graph" / "rebuild" | `build .`, summarize the report, stop |
110
+ | "update the graph" | `update .`, report what changed, stop |
111
+ | anything else | Query mode |
112
+
113
+ Note these map to CLI **subcommands**, not flags. There is no `--rebuild` or `--consumers` flag; `interlock-graph --rebuild` is an error.
114
+
115
+ ---
116
+
117
+ ## When other skills call this
118
+
119
+ | Situation | Command |
120
+ |-----------|---------|
121
+ | Session explore / architecture mapping | Read `GRAPH_REPORT.md`, then `query` per subsystem |
122
+ | Domain prose bootstrap | Read `DOCS_DIGEST.md`; rebuild via `/interlock:docs-digest` if stale |
123
+ | Invariant sweep, structural layer | `consumers <field-or-symbol>` first, then grep for string-keyed readers |
124
+ | explain-code "called by" / "calls into" | `explain` / `consumers` / `path` |
125
+ | Brownfield onboarding | `build`, then seed the architecture doc from report hubs and modules |
126
+
127
+ Follow `${CLAUDE_PLUGIN_ROOT}/shared/TOOL-ECONOMY.md` and `${CLAUDE_PLUGIN_ROOT}/shared/INVARIANT-SWEEP.md`.
128
+
129
+ ---
130
+
131
+ ## Stance
132
+
133
+ - **Graph first, files second.** The graph is an index, not a substitute for reading real code when you implement or verify semantics.
134
+ - **Confidence matters.** Prefer `EXTRACTED` edges. Treat `INFERRED` edges (OpenSpec, memory overlays) as hints to verify, not facts.
135
+ - **Never invent nodes.** If the CLI reports no match, say so and fall back to grep. Do not synthesize a plausible-looking path.
136
+ - **Read-only by default.** `build` and `update` write only under `.claude/graph/`.
@@ -0,0 +1,121 @@
1
+ ---
2
+ name: manual-test-plan
3
+ description: Produce an expert-QA manual UI test plan from an OpenSpec change plus a git commit range — every touched file accounted for, spec scenarios and tasks mapped to numbered test cases, and non-UI-testable changes flagged rather than dropped. Use when asked for a manual test plan, QA checklist, or UI verification plan after implementing and before opening an MR.
4
+ license: MIT
5
+ compatibility: Requires git. Optional openspec CLI for change scoping. Node.js >= 18 for the bundled interlock CLI.
6
+ argument-hint: "[change-name] [commit-range]"
7
+ allowed-tools: Bash(git *) Bash(interlock *) Bash(interlock-graph *) Bash(openspec *) Read Write Grep Glob
8
+ metadata:
9
+ type: generation
10
+ outputs:
11
+ - openspec/changes/<change>/manual-test-plan.md
12
+ ---
13
+
14
+ Produce a precise, executable **manual UI test plan** from an OpenSpec change and a git commit range.
15
+
16
+ Write for a junior tester who will not read the code: exact navigation paths, exact expected results, explicit pass/fail criteria. If a step needs someone to already know how the feature works, rewrite it.
17
+
18
+ ---
19
+
20
+ ## 1. Resolve scope
21
+
22
+ ```bash
23
+ interlock changes # active changes
24
+ git log --oneline "$FROM".."$TO"
25
+ git diff --stat "$FROM" "$TO"
26
+ git diff --name-status "$FROM" "$TO"
27
+ git diff "$FROM" "$TO" -- <source files> # cap ~40; skip lockfiles and binaries
28
+ ```
29
+
30
+ Default range: the merge base against the default branch. Read `proposal.md`, `design.md`, `tasks.md`, and `specs/**/*.md` for the change when one resolves.
31
+
32
+ Optional context — do **not** preload all of `docs/`:
33
+ - `.claude/graph/DOCS_DIGEST.md` if present
34
+ - `interlock-graph context "<change> routes components forms" --budget 2000`
35
+
36
+ ---
37
+
38
+ ## 2. Classify the surface — deterministically
39
+
40
+ ```bash
41
+ interlock surface --changed <files> --json
42
+ ```
43
+
44
+ This returns the tri-state per file, and it is authoritative. **Do not re-derive the classification in prose**; the whole reason it is a CLI is that this question used to get two different answers on the same diff.
45
+
46
+ | Class | Meaning |
47
+ |---|---|
48
+ | `UI-TESTABLE` | Directly user-visible — pages, components, forms, navigation, copy, layout, client validation, loading and error states |
49
+ | `UI-INDIRECT` | Reachable only through UI side effects — API routes the UI calls, server actions, data shaping that changes what is displayed |
50
+ | `NOT-UI-TESTABLE` | Backend, infra, docs, tests, config, tooling with no user-visible effect |
51
+
52
+ If `needsManualTestPlan` is false, say so and stop. A backend-only change does not get a UI test plan, and writing one anyway teaches the reader to ignore these documents.
53
+
54
+ ---
55
+
56
+ ## 3. Analyze
57
+
58
+ 1. **Inventory** every changed file from `--name-status`.
59
+ 2. **Map specs and tasks to cases.** Spec scenarios and unchecked `tasks.md` items are the backbone; findings visible only in the diff become additional cases.
60
+ 3. **Cover the whole surface.** Every `UI-TESTABLE` and `UI-INDIRECT` file appears in at least one test case (or is explicitly deferred to a shared flow). Every `NOT-UI-TESTABLE` file appears in the Non-UI table. **Never silently drop a changed file** — an unmentioned file reads as "tested" when it wasn't.
61
+
62
+ ---
63
+
64
+ ## 4. Write the plan
65
+
66
+ Write to `openspec/changes/<change-name>/manual-test-plan.md`. If nothing scopes it, write `./manual-test-plan.md` and say so.
67
+
68
+ Output **exactly** this structure — no preamble, no sign-off inside the file:
69
+
70
+ ```markdown
71
+ # Manual UI Test Plan — <change-name or "unscoped">
72
+
73
+ **Scope:** <N commits / merge-base vs base> · HEAD `<short-hash>` · <branch>
74
+ **Intent:** <1–2 sentences from proposal.md, or inferred from commit subjects>
75
+ **Persona:** Expert QA — assume a clean session unless a case says otherwise
76
+ **Environment prep:** <accounts, feature flags, seed data, base URL — or "none">
77
+
78
+ ---
79
+
80
+ ## Coverage Matrix
81
+
82
+ | File | Status | Class | Covered by |
83
+ |------|--------|-------|------------|
84
+ | path/to/file | M/A/D/R | UI-TESTABLE / UI-INDIRECT / NOT-UI-TESTABLE | TC-01, TC-03 / — see Non-UI |
85
+
86
+ Every changed source file in the range MUST appear here.
87
+
88
+ ---
89
+
90
+ ## Test Cases
91
+
92
+ ### TC-01 — <short title>
93
+ - **Priority:** P0 (blocks ship) / P1 (core flow) / P2 (edge / polish)
94
+ - **Traces to:** <spec scenario / task id / diff observation>
95
+ - **Covers files:** `path/a`, `path/b`
96
+ - **Preconditions:** …
97
+ - **Steps:**
98
+ 1. …
99
+ 2. …
100
+ - **Expected:** …
101
+ - **Pass / Fail:** Pass if … · Fail if …
102
+
103
+ ### Happy path
104
+ TC ids proving the primary journey works end to end.
105
+
106
+ ### Edge / negative
107
+ TC ids for validation errors, empty states, permissions, failure UX.
108
+
109
+ ### Regression watch
110
+ TC ids for adjacent UI that tends to break when this area changes.
111
+
112
+ ---
113
+
114
+ ## Non-UI Changes (not manually testable in the UI)
115
+
116
+ | File | Why not UI-testable | Suggested verification |
117
+ |------|---------------------|------------------------|
118
+ | path/to/x | pure utility / infra / docs-only | unit test / CI job / manual CLI invoke / N/A |
119
+ ```
120
+
121
+ Group assertions into one case when they share a navigation path; split them when the outcomes differ.
@@ -0,0 +1,170 @@
1
+ ---
2
+ name: mr
3
+ description: Generate a merge-request or pull-request summary from the change artifacts and diff, then create or update it on whichever platform the remote points at — GitLab via glab, GitHub via gh. Optional comment-only add-ons post the manual test plan or a teaching walkthrough. Use when opening an MR/PR, refreshing its description, or attaching a test plan.
4
+ license: MIT
5
+ compatibility: Requires git, plus glab (GitLab) or gh (GitHub) authenticated. Optional openspec CLI for change context.
6
+ argument-hint: "[change-name] [--create] [--test-plan] [--explain]"
7
+ disable-model-invocation: true
8
+ allowed-tools: Bash(git *) Bash(glab *) Bash(gh *) Bash(openspec *) Bash(interlock *) Bash(interlock-graph *) Read Write Grep Glob
9
+ metadata:
10
+ type: generation
11
+ outputs:
12
+ - openspec/changes/<change>/mr-summary.md
13
+ ---
14
+
15
+ Turn a finished change into a merge request a reviewer can actually review.
16
+
17
+ Almost all of this skill is platform-agnostic — the summary, the risk assessment, the test plan. Only the last step differs between GitLab and GitHub, so **detect the platform, do not ask about it.**
18
+
19
+ | Flag | Effect |
20
+ |------|--------|
21
+ | `--create` | Create the MR/PR instead of updating an existing one |
22
+ | `--test-plan` | Also post the full manual test plan **as a comment** |
23
+ | `--explain` | Also post a per-file teaching walkthrough **as a comment** |
24
+
25
+ ---
26
+
27
+ ## 1. Detect the platform
28
+
29
+ ```bash
30
+ git remote get-url origin
31
+ ```
32
+
33
+ | Remote host contains | Platform | CLI | Check |
34
+ |---|---|---|---|
35
+ | `gitlab` | GitLab | `glab` | `glab auth status` |
36
+ | `github` | GitHub | `gh` | `gh auth status` |
37
+
38
+ Self-hosted instances often carry neither string — fall back to whichever CLI is installed and authenticated, and say which one you chose. If both or neither resolve, ask once; this is a genuinely ambiguous fact about the user's setup, not a default you can pick.
39
+
40
+ ---
41
+
42
+ ## 2. Resolve the MR/PR and the change
43
+
44
+ ```bash
45
+ # GitLab
46
+ glab mr view 2>/dev/null
47
+ # GitHub
48
+ gh pr view 2>/dev/null
49
+ ```
50
+
51
+ No open MR/PR and no `--create` → stop: *"No open merge request for this branch. Push the branch and re-run with `--create`."*
52
+
53
+ Resolve the OpenSpec change with `interlock changes` and the branch name. If several are plausible, ask which one — a wrong change here produces a confidently wrong description.
54
+
55
+ ---
56
+
57
+ ## 3. Gather
58
+
59
+ ```bash
60
+ git diff --stat <base>...HEAD
61
+ git diff --name-status <base>...HEAD
62
+ git log --oneline <base>...HEAD
63
+ ```
64
+
65
+ Read `proposal.md` (the why), `design.md` (the how), `tasks.md` (what was done), and `specs/**/*.md`. Use `interlock-graph consumers <symbol>` to check blast radius when the graph exists.
66
+
67
+ ---
68
+
69
+ ## 4. Compose the description
70
+
71
+ ```markdown
72
+ ## Summary
73
+ 2–4 bullets: what changed and why. Lead with the why.
74
+
75
+ ## Changes
76
+ Grouped by capability, using spec names as headers where possible:
77
+ - **<capability>**: <one-line user-visible change>
78
+ - <files or modules touched>
79
+
80
+ ## High Risk Changes
81
+ Production risks, each with a mitigation or rollback note. Write `None` if there are none.
82
+
83
+ ## Prerequisites to Production
84
+ Env vars, migrations, feature flags, config. Write `None` if there are none.
85
+
86
+ ## Testing
87
+ - Unit: <N added / N updated> — `<command>` passing
88
+ - Verified locally: <one line on what was exercised>
89
+ - Not covered: <gap, or "none">
90
+ ```
91
+
92
+ **`## Testing` answers "was this verified?", not "how do I test it by hand?"** Three to five bullets, readable without scrolling.
93
+
94
+ **Never embed the manual test plan in the description.** It goes in a comment, via `--test-plan`. When you post one in this run, add a single closing line to `## Testing`:
95
+
96
+ ```
97
+ Manual UI test plan: see MR comment.
98
+ ```
99
+
100
+ Omit that line when `--test-plan` was not passed.
101
+
102
+ Be honest in the risk assessment. An inflated confidence level is worse than no assessment, because it is the section a reviewer uses to decide how hard to look.
103
+
104
+ ---
105
+
106
+ ## 5. Write the local artifact
107
+
108
+ Write `openspec/changes/<change-name>/mr-summary.md` with the branch name, commit message, MR title (≤70 chars), and the full description. This survives the session; the platform copy can be regenerated from it.
109
+
110
+ ---
111
+
112
+ ## 6. Publish
113
+
114
+ ```bash
115
+ # GitLab — update
116
+ glab mr update <id> --description "$(cat <<'EOF'
117
+ <description>
118
+ EOF
119
+ )"
120
+ # GitLab — create
121
+ glab mr create --title "<title>" --description "..."
122
+
123
+ # GitHub — update
124
+ gh pr edit <id> --body "$(cat <<'EOF'
125
+ <description>
126
+ EOF
127
+ )"
128
+ # GitHub — create
129
+ gh pr create --title "<title>" --body "..."
130
+ ```
131
+
132
+ Updating **replaces the description entirely.** If the existing description contains anything hand-written by a human, say so and confirm before overwriting it.
133
+
134
+ ---
135
+
136
+ ## 7. Optional comments
137
+
138
+ **`--test-plan`** — reuse `openspec/changes/<change>/manual-test-plan.md` when it is fresh relative to HEAD; otherwise run `/interlock:manual-test-plan` first. Post it as a comment (`glab mr note -m` / `gh pr comment -b`). Split across comments if it exceeds the platform's size limit.
139
+
140
+ **`--explain`** — run `/interlock:explain-code` in commit teach-in mode and post the walkthrough as a comment.
141
+
142
+ Both are **comment-only, always.** Neither ever enters the description — a reviewer opening the MR should see the summary, not a 200-line test script.
143
+
144
+ ---
145
+
146
+ ## 8. Close the loop: say what still needs archiving
147
+
148
+ This is the last point the change is in your hands, so it is where the spec lifecycle gets a mention.
149
+
150
+ Pass the changed-file list you already resolved in §2:
151
+
152
+ ```bash
153
+ interlock drift --changed <files> --json
154
+ ```
155
+
156
+ **Never blocks.** It exits 0 whatever it finds — do not branch on the exit code here, and do not refuse to publish because of it.
157
+
158
+ Four findings, in descending order of how much they deserve the reader's attention. Report only what is non-empty:
159
+
160
+ - **`unarchived`** — earlier changes finished their tasks but were never archived, so their delta specs never reached `openspec/specs/` and the living specs no longer describe what shipped. Name them and give the command:
161
+ ```bash
162
+ openspec archive <change-name>
163
+ ```
164
+ - **`stale.broken`** — a spec cites a file that no longer exists. This is **evidence**, not inference: the file was there when the graph was built. Report it as a real finding. If `stale.graphBuiltAt` is old, say so too — a rename against a stale graph looks identical to a deletion.
165
+ - **`orphans.orphans`** — changed source files no spec describes. **Always report `orphans.coverage` alongside the count.** "2 files have no spec" is alarming; "2 of 6, in a repo where 34% of source files have one" is informative, and on a repo still being specced the second is the honest framing.
166
+ - **`stale.aging`** — living specs older than files they cite. Weakest of the four: it compares dates on **inferred** edges, and a file can be refactored without the spec becoming wrong. Mention it only if it names a spec relevant to this change.
167
+
168
+ Then close with the one line that matters for *this* change: once the MR merges, run `openspec archive <this-change>`. Interlock does not archive for you, and nothing downstream will notice if nobody does.
169
+
170
+ If `drift` reports nothing, say nothing. A clean lifecycle does not need a paragraph.
@@ -0,0 +1,121 @@
1
+ ---
2
+ name: report
3
+ description: Read the indicator report over Interlock's three recorded corpora — outcome records, run trajectories, review metrics — and say what it does and does not license. Use when someone asks how the loop is actually performing, whether continuity is working, how often review findings survive, which gate blocks most, or whether there is yet enough evidence to change a cap or a gate. The figures come from `interlock report`, never from your own counting; the judgement you add is about denominators, not about thresholds.
4
+ license: MIT
5
+ compatibility: Node.js >= 18 and the bundled interlock CLI. Offline — no model, no network, and nothing is written.
6
+ allowed-tools: Bash(interlock *) Read Grep Glob
7
+ metadata:
8
+ type: reading
9
+ outputs:
10
+ - a written reading of the census, with what each figure licenses
11
+ ---
12
+
13
+ `interlock report` computes the numbers. You do not. Your job is the sentence
14
+ that stops a number being misused — which is almost always a sentence about its
15
+ denominator.
16
+
17
+ ## 1. Get the report
18
+
19
+ ```
20
+ interlock report --json
21
+ ```
22
+
23
+ Add `--since <iso>` for a window and `--change <name>` for one change. The
24
+ command always exits 0: it gates nothing, and per §4.15(a) nothing in the loop
25
+ reads its output. If you find yourself wanting an exit code, you have started
26
+ designing a gate — that is decision §4.16 / slice F6.6 and it has not been made.
27
+
28
+ Never recompute a figure by hand. If a number you need is absent, the corpus did
29
+ not record it; say that instead of deriving a substitute from something nearby.
30
+
31
+ When someone wants to *look* at the corpora rather than have them read to them:
32
+
33
+ ```
34
+ interlock report --html > report.html
35
+ ```
36
+
37
+ This renders the same report object as one self-contained HTML document — no
38
+ stylesheet, script or font is fetched, so it opens offline and can be attached
39
+ to an issue. It is **a generated file, not a service**: there is no server, no
40
+ port and no watch mode to start, and nothing in the loop reads it. It is also
41
+ **not a verdict**, and gains none by being visual: it draws no threshold, target
42
+ or trend, no colour on it encodes health, and an unobserved indicator renders as
43
+ `UNOBSERVED` with its reason at the same weight as a figure. Bare it writes to
44
+ stdout; given a path it writes only there. It cannot be combined with `--json`.
45
+
46
+ Handing someone the document does not discharge your job. The reading below is
47
+ still yours to write — the page reports, and reporting is not interpretation.
48
+
49
+ ## 2. Read coverage first, and let it govern everything after it
50
+
51
+ The `coverage` group is not preamble. It decides whether any indicator below it
52
+ means anything:
53
+
54
+ - `trajectories.withReceipt` is the denominator behind **every**
55
+ receipt-derived indicator — first-pass ship, remediation rounds, plan-reuse
56
+ status, the receipt finding series. If it is 0, all of those are unobserved,
57
+ and the finding to report is *that the receipt path has not fired*, not that
58
+ the runs were clean.
59
+ - `outcomes.exists: false` means no ship run has recorded an outcome at all. The
60
+ continuity question — should the human checkpoint have been skipped — is then
61
+ unanswerable, and no amount of trajectory data substitutes, because the
62
+ trajectories hold no human judgement.
63
+ - `metrics.unrecognized` names files that reached no indicator. That is normal:
64
+ `skills/review-artifacts` and `skills/fix-tests` both write into
65
+ `.claude/metrics/` in their own shapes. Report the count; do not treat an
66
+ unrecognized file as a missing one.
67
+ - `trajectories.truncated` means the scan hit its cap. Narrow with `--since`
68
+ before reading any rate, or say the rate covers only the most recent files.
69
+
70
+ ## 3. State what each figure licenses
71
+
72
+ For every indicator you present, give the value, the denominator, and one
73
+ sentence on what it supports. The pattern:
74
+
75
+ > **first-pass ship** — unobserved, 0 receipts. Licenses nothing. Would become
76
+ > readable once ship runs write receipts; the trajectories on disk predate that
77
+ > event type.
78
+
79
+ > **gate exits non-zero** — 12.6% of 1 785 recorded exits, and 150 of the 225
80
+ > are `wave-state record-batch`. Licenses a claim about which gate stops a run
81
+ > most often. Licenses no claim about whether stopping was correct.
82
+
83
+ Three rules hold without exception:
84
+
85
+ 1. **A denominator under ~20 licenses no trend.** Say "too few to read", name
86
+ what would have to accumulate, and stop. Do not soften it into a provisional
87
+ reading.
88
+ 2. **Never sum across provenance.** `fromMetrics` and `fromReceipts` are
89
+ different writers over different runs; the report keeps them apart and so do
90
+ you. The same rule separates a value a run *observed* from one an agent
91
+ *reported* — check each indicator's `source`.
92
+ 3. **`diffMatchesPlan` is computed one way only, and the other two plan figures
93
+ are not it.** It is the share of paths a run TOUCHED that its plan predicted
94
+ — read `direction` and quote it. Do not answer "does the merged diff match
95
+ the plan" with `planStatus` or with the mid-run revision rate: both are about
96
+ the plan, neither observes the diff. Read `excluded` before reading the
97
+ figure: a share over three qualifying runs while forty were excluded for a
98
+ recording gap is a fact about the recording, and saying so is the reading.
99
+
100
+ ## 4. Do not recommend a gate
101
+
102
+ You may report a figure, name its denominator, and name what would have to be
103
+ recorded for it to mean more. You may not recommend that a cap be raised, a
104
+ threshold be set, a band be adjusted, or a gate be relaxed or tightened on the
105
+ evidence of a figure — not even when the figure looks decisive, and not even
106
+ when asked directly. The corpora exist to inform §4.16 later; converting them
107
+ into a recommendation now is the decision that has not been made.
108
+
109
+ If asked for a recommendation, answer with the evidence that is missing.
110
+
111
+ ## 5. Report the caps rather than restating them
112
+
113
+ The scan bound comes from `interlock limits`, not from a number typed here. Cite
114
+ the command.
115
+
116
+ ## Output
117
+
118
+ A short written reading: the coverage headline, then each indicator with its
119
+ value, denominator, and what it licenses. Close with the single most valuable
120
+ missing measurement — the one whose absence blocks the most indicators. That
121
+ sentence is usually the most useful thing in the report.
@@ -0,0 +1,127 @@
1
+ ---
2
+ name: review-artifacts
3
+ description: Review OpenSpec change artifacts before anyone writes code — architecture quality and design completeness, then spec completeness and testability. Catches a wrong idea at the cheapest possible moment. Use after generating proposal, design, tasks and delta specs, and before implementing.
4
+ license: MIT
5
+ compatibility: Requires the openspec CLI. Node.js >= 18 for the bundled interlock CLI.
6
+ argument-hint: "[change-name]"
7
+ allowed-tools: Bash(interlock *) Bash(openspec *) Read Glob Grep
8
+ metadata:
9
+ type: review
10
+ outputs:
11
+ - .claude/metrics/review-artifacts-<change>-<timestamp>.json
12
+ ---
13
+
14
+ Review the change artifacts before implementation. This is the highest-leverage gate in the whole flow: a blocker caught here costs a paragraph, the same blocker caught after ship costs a day.
15
+
16
+ Resolve the change:
17
+
18
+ ```bash
19
+ interlock validate [change-name] --json
20
+ ```
21
+
22
+ If it reports ambiguity, pick the change the user named. If they named none and several are active, review the most recently modified and **say which one you picked** — this skill runs inside automated chains where a question would stall the flow.
23
+
24
+ Read all of `proposal.md`, `specs/**/*.md`, `design.md`, `tasks.md`.
25
+
26
+ Detect the stack from the artifacts and the dependency manifest, and skip framework-specific checks that do not apply. Do not ask a Go service about accessibility requirements.
27
+
28
+ ---
29
+
30
+ ## Reviewer 1 — Architecture
31
+
32
+ **Architecture quality**
33
+ - Are layer boundaries placed correctly in `design.md` for this stack?
34
+ - Does the structure follow the patterns already in this project?
35
+ - Are state and ownership decisions appropriate?
36
+ - Is the data flow clear — reads, writes, mutations, side effects?
37
+
38
+ **Design completeness**
39
+ - Does `design.md` specify an error-handling strategy?
40
+ - When UI is in scope, are accessibility requirements present?
41
+ - Is the approach consistent with the project's own architecture docs (`CLAUDE.md`, `AGENTS.md`, `docs/`)?
42
+
43
+ **Pattern fitness**
44
+ - Does the proposed structure introduce complexity it does not need?
45
+ - Is there a simpler alternative that reaches the same outcome?
46
+
47
+ ---
48
+
49
+ ## Reviewer 2 — QA
50
+
51
+ **Spec completeness**
52
+ - Does every requirement in `specs/` have concrete Given/When/Then scenarios?
53
+ - Are edge cases covered — empty, error, loading, boundary values, permission denied?
54
+ - Which scenarios would a real user hit that the spec does not mention?
55
+
56
+ **Testability**
57
+ - Can every scenario become an automated test?
58
+ - Are acceptance criteria specific enough to verify? "Should work properly" is not a criterion.
59
+
60
+ **Task quality**
61
+ - Does `tasks.md` pair a test task with every feature task?
62
+ - Is the ordering right — do dependencies precede dependents?
63
+ - Is each task small enough for one focused session?
64
+ - **Same-file staircase.** Do consecutive checkboxes name the same production path — one per TDD beat, comment move or trap? That is one checkbox, not five. Report it as a warning naming the repeated path. It is not a ship halt: `planWaves` folds the resulting 1-task waves back together, so the cost is implementer count and lost parallelism, not checkpoints.
65
+ - Does each numbered section hold work that could share a wave? Independent files belong in one section; a new section is only an output-exists boundary.
66
+
67
+ **Delta spec quality**
68
+ - Are ADDED / MODIFIED / REMOVED sections used correctly?
69
+ - Do the deltas describe **behavior**, not implementation?
70
+
71
+ ---
72
+
73
+ ## Invariant check
74
+
75
+ If the change transforms a shared or derived value — normalizing an identity, deriving a cache or dedup key, canonicalizing a field — then `tasks.md` must contain a task for **every consumer** of that value, not just the one the change is about.
76
+
77
+ A change that updates one call site and leaves its siblings reading the raw form is a **blocker at this gate**, because it is far cheaper to add the tasks now than to discover the surviving bug after review. See `${CLAUDE_PLUGIN_ROOT}/shared/INVARIANT-SWEEP.md`.
78
+
79
+ ---
80
+
81
+ ## Output
82
+
83
+ ```
84
+ ── ARCHITECTURE ────────────────────────────────
85
+ ✓ [passed — brief]
86
+ ⚠ WARNING: [specific issue, with file reference]
87
+ ✗ BLOCKER: [critical issue]
88
+
89
+ ── QA ──────────────────────────────────────────
90
+ ✓ [passed — brief]
91
+ ⚠ WARNING: [specific issue, with file reference]
92
+ ✗ BLOCKER: [critical issue]
93
+
94
+ SUMMARY: N blockers, N warnings, N suggestions
95
+ Recommendation: [fix blockers | address warnings | proceed to /interlock:ship]
96
+ ```
97
+
98
+ Be specific — name files and sections. Do not pad with generic praise. If the artifacts are genuinely good, say so briefly and recommend proceeding.
99
+
100
+ ---
101
+
102
+ ## Emit structured findings
103
+
104
+ Write `.claude/metrics/review-artifacts-<change>-<YYYYMMDD-HHMMSS>.json` in this shape:
105
+
106
+ ```json
107
+ { "dimension": "artifacts",
108
+ "findings": [
109
+ { "severity": "blocker|warning|suggestion",
110
+ "file": "path/to/artifact.md", "line": 42,
111
+ "title": "short claim",
112
+ "description": "what is wrong and why",
113
+ "suggestion": "how to fix it" }
114
+ ] }
115
+ ```
116
+
117
+ `severity`, `file`, `title` and `description` are required; `line` and `suggestion` are optional. Map BLOCKER→`blocker`, WARNING→`warning`, SUGGESTION→`suggestion`. Create `.claude/metrics/` if it does not exist. This is the complete emit contract — do not read `lib/findings.mjs` to rediscover it.
118
+
119
+ Then let the deterministic gate decide, rather than deciding in prose:
120
+
121
+ ```bash
122
+ interlock gate --findings .claude/metrics/review-artifacts-<change>-<ts>.json --metrics <change>
123
+ ```
124
+
125
+ The gate blocks if and only if at least one finding is a `blocker`. Its exit status is the verdict: non-zero means blocked. Do not re-derive the verdict in prose.
126
+
127
+ `--metrics <change>` is not optional decoration. It writes the four counts this gate produced — raised, dismissed, dropped by the quality band, surviving — to `.claude/metrics/` in the schema `interlock report` recognizes. Drop it and this review path becomes invisible to the report forever: the review-finding indicators read `unobserved` no matter how many gates ran. It is bookkeeping only and cannot change the verdict or the exit status.