@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.
- package/.claude-plugin/marketplace.json +19 -0
- package/.claude-plugin/plugin.json +58 -0
- package/CHANGELOG.md +380 -0
- package/LICENSE +21 -0
- package/README.md +358 -0
- package/agents/ping.md +8 -0
- package/agents/worker.md +8 -0
- package/bin/interlock +2655 -0
- package/bin/interlock-graph +6 -0
- package/bin/interlock-run +508 -0
- package/bin/interlock-ship-acp +39 -0
- package/hooks/_shared.mjs +162 -0
- package/hooks/guard-commit.mjs +77 -0
- package/hooks/guard-tasks.mjs +107 -0
- package/hooks/guard-tests.mjs +63 -0
- package/hooks/preflight.mjs +107 -0
- package/lib/artifacts.mjs +316 -0
- package/lib/autonomy.mjs +149 -0
- package/lib/conformance.mjs +132 -0
- package/lib/doctor.mjs +1176 -0
- package/lib/drift.mjs +409 -0
- package/lib/eval-history.mjs +304 -0
- package/lib/evals-calibrate.mjs +317 -0
- package/lib/evals-capture.mjs +437 -0
- package/lib/evals-promote.mjs +257 -0
- package/lib/evals-triage.mjs +216 -0
- package/lib/findings.mjs +340 -0
- package/lib/graph/cli.mjs +266 -0
- package/lib/graph/extract/config-json.mjs +74 -0
- package/lib/graph/extract/create-extractor.mjs +35 -0
- package/lib/graph/extract/memory.mjs +99 -0
- package/lib/graph/extract/openspec.mjs +80 -0
- package/lib/graph/extract/python.mjs +137 -0
- package/lib/graph/extract/regex-fallback.mjs +187 -0
- package/lib/graph/extract/shell.mjs +71 -0
- package/lib/graph/extract/typescript.mjs +217 -0
- package/lib/graph/extract/walk.mjs +272 -0
- package/lib/graph/format.mjs +51 -0
- package/lib/graph/graph/build.mjs +374 -0
- package/lib/graph/graph/query.mjs +249 -0
- package/lib/graph/graph/report.mjs +122 -0
- package/lib/graph/graph/update.mjs +47 -0
- package/lib/graph/paths.mjs +66 -0
- package/lib/graph/retrieve/context.mjs +86 -0
- package/lib/graph/retrieve/docs-index.mjs +116 -0
- package/lib/graph/retrieve/markdown.mjs +246 -0
- package/lib/host/acp.mjs +551 -0
- package/lib/host/claude-cli.mjs +285 -0
- package/lib/host/codex.mjs +231 -0
- package/lib/host/model-map.mjs +129 -0
- package/lib/host/qwen.mjs +176 -0
- package/lib/host/registry.mjs +213 -0
- package/lib/host.mjs +393 -0
- package/lib/ledger.mjs +444 -0
- package/lib/limits.mjs +525 -0
- package/lib/locators.mjs +99 -0
- package/lib/merge-lanes.mjs +148 -0
- package/lib/metrics.mjs +99 -0
- package/lib/notify.mjs +229 -0
- package/lib/outcomes.mjs +560 -0
- package/lib/plan-fingerprint.mjs +570 -0
- package/lib/project-slug.mjs +18 -0
- package/lib/prompts/commit.mjs +26 -0
- package/lib/prompts/dimensions.mjs +70 -0
- package/lib/prompts/handoff.mjs +86 -0
- package/lib/prompts/implementer.mjs +150 -0
- package/lib/prompts/index.mjs +250 -0
- package/lib/prompts/planner.mjs +70 -0
- package/lib/prompts/remediate.mjs +137 -0
- package/lib/prompts/replan.mjs +23 -0
- package/lib/prompts/review.mjs +107 -0
- package/lib/prompts/schemas.mjs +206 -0
- package/lib/prompts/stage.mjs +48 -0
- package/lib/prompts/verify.mjs +88 -0
- package/lib/ready.mjs +812 -0
- package/lib/receipt.mjs +434 -0
- package/lib/remediate.mjs +275 -0
- package/lib/report-html.mjs +623 -0
- package/lib/report.mjs +1124 -0
- package/lib/review-core.mjs +713 -0
- package/lib/risk.mjs +370 -0
- package/lib/run-log.mjs +1004 -0
- package/lib/run-paths.mjs +239 -0
- package/lib/run.mjs +2921 -0
- package/lib/ship-stage.mjs +223 -0
- package/lib/spill.mjs +150 -0
- package/lib/surface.mjs +195 -0
- package/lib/verify.mjs +1074 -0
- package/lib/waves.mjs +2661 -0
- package/package.json +40 -0
- package/shared/CONTEXT-HYGIENE.md +84 -0
- package/shared/DECISION-LEDGER.md +141 -0
- package/shared/EXPLORE-BRIEF.md +96 -0
- package/shared/INVARIANT-SWEEP.md +57 -0
- package/shared/SESSION-SHAPES.md +261 -0
- package/shared/SKILL-CANDIDATE-BRIEF.md +106 -0
- package/shared/TEST-PROFILE.md +204 -0
- package/shared/TOOL-ECONOMY.md +155 -0
- package/skills/bootstrap/SKILL.md +226 -0
- package/skills/commit/SKILL.md +99 -0
- package/skills/dispatch/SKILL.md +78 -0
- package/skills/docs-digest/SKILL.md +109 -0
- package/skills/evals/SKILL.md +160 -0
- package/skills/explain-code/SKILL.md +42 -0
- package/skills/explain-code/commit-teachin.md +136 -0
- package/skills/explain-code/module-walkthrough.md +358 -0
- package/skills/explore/SKILL.md +159 -0
- package/skills/fix-tests/SKILL.md +120 -0
- package/skills/graph/SKILL.md +136 -0
- package/skills/manual-test-plan/SKILL.md +121 -0
- package/skills/mr/SKILL.md +170 -0
- package/skills/report/SKILL.md +121 -0
- package/skills/review-artifacts/SKILL.md +127 -0
- package/skills/review-code/REVIEW.template.md +49 -0
- package/skills/review-code/SKILL.md +156 -0
- package/skills/review-code/dimensions/architecture.md +10 -0
- package/skills/review-code/dimensions/devops.md +12 -0
- package/skills/review-code/dimensions/language.md +12 -0
- package/skills/review-code/dimensions/qa.md +12 -0
- package/skills/review-code/dimensions/security.md +12 -0
- package/skills/review-code/dimensions/technical-lead.md +19 -0
- package/skills/ship/SKILL.md +67 -0
- package/skills/spec/SKILL.md +264 -0
- package/skills/spec/continuity.md +66 -0
- 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.
|