@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,261 @@
1
+ # Session Shapes
2
+
3
+ Patterns that produce expert-level sessions. Use these as mental models when
4
+ starting a session — or let `/interlock:dispatch` orient you first.
5
+
6
+ The key insight: expert sessions are not longer or harder — they are better
7
+ structured from the start. The right shape chosen upfront eliminates
8
+ backtracking, scope drift, and wasted exploration. Score a live session against
9
+ these shapes and against `${CLAUDE_PLUGIN_ROOT}/shared/TOOL-ECONOMY.md`.
10
+
11
+ ---
12
+
13
+ ## Shape 1: Plan-then-Execute
14
+
15
+ **Duration:** 15-25 min | **Agents:** 4-6 | **Pattern:** think → structure → do
16
+
17
+ **Entry signal:** "I want to build X" — no active change exists, scope spans multiple files
18
+
19
+ **Flow:**
20
+ 1. `/interlock:explore` (optional — skip if the domain is well-understood)
21
+ 2. `/interlock:spec` — generates proposal, design, tasks, delta specs, then gates them through an artifact review and **stops at the human checkpoint**
22
+ 3. Human reads the spec
23
+ 4. `/interlock:ship` — dependency-ordered waves of parallel implementers → verify → commit
24
+
25
+ **Expert version:** State the full scope upfront. Let `spec` generate all artifacts
26
+ and pass Gate 1 in one pass. Read the spec once. `ship` handles execution in
27
+ parallel and produces one feature-level commit. No mid-session design changes.
28
+
29
+ **Common failure mode (advanced, not expert):** Starting to code before speccing.
30
+ Scope drifts mid-implementation. Multiple commits for what should be one logical
31
+ change. 30+ minutes because exploration and execution are interleaved.
32
+
33
+ **Preconditions for expert execution:**
34
+ - Clear one-sentence scope statement before starting
35
+ - No ambiguity about boundaries (what's in / out)
36
+ - Domain knowledge sufficient to skip the explore phase
37
+
38
+ ---
39
+
40
+ ## Shape 2: Structured Execute
41
+
42
+ **Duration:** 7-15 min | **Agents:** 3-13 (depends on task count) | **Pattern:** load → classify → do
43
+
44
+ **Entry signal:** "Ship the change" or "execute the tasks" — an active change with reviewed artifacts already exists
45
+
46
+ **Flow:**
47
+ 1. `/interlock:ship` — the workflow loads all artifacts, classifies tasks, and runs them in dependency-ordered waves
48
+ 2. Unit verification, then a single commit
49
+
50
+ **Expert version:** Artifacts already exist and passed the checkpoint in a prior
51
+ session. This session is pure execution — zero exploration, zero design decisions.
52
+ Fastest path to done. The workflow reads everything before writing anything.
53
+
54
+ **Common failure mode:** Re-implementing the change inline in the conversation
55
+ instead of launching the `ship` workflow. Making implementation decisions that
56
+ contradict the design doc. Speccing again when the spec is already reviewed.
57
+
58
+ **Preconditions for expert execution:**
59
+ - All artifacts exist and were reviewed (Gate 1 passed)
60
+ - Tasks are well-defined with clear acceptance criteria
61
+ - The Workflow tool is available (Claude Code v2.1.154+, dynamic workflows on)
62
+
63
+ ---
64
+
65
+ ## Shape 3: Investigate-then-Propose
66
+
67
+ **Duration:** 20-35 min | **Agents:** 2-5 | **Pattern:** understand → capture → structure
68
+
69
+ **Entry signal:** "How does X work? I might need to change it." — needs understanding before committing to a design
70
+
71
+ **Flow:**
72
+ 1. `/interlock:explore` — fan out read-only investigators across subsystems, write a durable brief
73
+ 2. Synthesize findings into a scope definition
74
+ 3. `/interlock:spec` — convert understanding into durable artifacts (auto-loads the latest explore brief)
75
+ 4. Stop at the checkpoint (or continue to ship in the same session if scope is small)
76
+
77
+ **Expert version:** Run `/interlock:explore` and let it fan out across threads
78
+ without asking which to follow; it writes `.claude/handoff/explore-<slug>-<ts>.md`.
79
+ Then `/interlock:spec` (possibly after `/clear` or cache expiry) auto-loads the
80
+ latest matching brief and generates proposal artifacts — no re-discovery from chat.
81
+
82
+ **Common failure mode:** Exploring in one long single-threaded chat. Findings live
83
+ only in chat history and are lost after `/clear` or prompt-cache expiry. Never
84
+ converting understanding into a spec or explore brief — the next session
85
+ re-discovers everything.
86
+
87
+ **Preconditions for expert execution:**
88
+ - Multiple subsystems or modules are involved (justifies fan-out)
89
+ - The question is specific enough to decompose into parallel investigations
90
+ - Memory / prior briefs are checked first (avoid re-investigating known territory)
91
+
92
+ ---
93
+
94
+ ## Shape 4: Targeted Fix
95
+
96
+ **Duration:** 5-12 min | **Agents:** 0-1 | **Pattern:** locate → fix → verify
97
+
98
+ **Entry signal:** "X is broken" or "fix the error in Y" — specific bug, narrow scope
99
+
100
+ **Flow:**
101
+ 1. Check memory / known constraints about this area
102
+ 2. Locate the issue (often a single file or function) — graph query or grep, not a full-file read sweep
103
+ 3. Fix it
104
+ 4. `/interlock:fix-tests` or run the suite to verify
105
+ 5. `/interlock:commit` (or report if more investigation is needed)
106
+
107
+ **Expert version:** The relevant constraint is already known. No exploration needed
108
+ — go directly to the fix. Verify immediately. Done in under 10 minutes.
109
+
110
+ **Common failure mode:** Guessing at solutions without reading constraints. Trying
111
+ three wrong approaches before finding the right one. Exploring broadly when the fix
112
+ is narrow. Not running tests after the fix.
113
+
114
+ **Preconditions for expert execution:**
115
+ - The error is specific (not "something is slow" — that's Investigate-then-Propose)
116
+ - Scope is genuinely narrow (one file, one concept)
117
+ - If the fix touches a shared or derived value, its consumers are swept
118
+ (`${CLAUDE_PLUGIN_ROOT}/shared/INVARIANT-SWEEP.md`)
119
+
120
+ ---
121
+
122
+ ## Shape 5: Workflow-Orchestrated
123
+
124
+ **Duration:** 10-40 min | **Agents:** 10-50+ | **Pattern:** script controls flow, agents do work
125
+
126
+ **Entry signal:** Complex work that benefits from deterministic orchestration — a reviewed change ready to ship, or a diff ready to review.
127
+
128
+ **Flow (ship):**
129
+ 1. `/interlock:ship` launches `workflows/ship.js` on the Workflow runtime
130
+ 2. Load artifacts → classify tasks (schema-validated) → execute in parallel waves
131
+ 3. Inter-wave verification gates catch interface mismatches immediately
132
+ 4. Unit verify, then commit. `--strict` adds adversarial review, remediation, handoff, conformance
133
+ 5. Resumable — a stopped run picks up where it left off
134
+
135
+ **Flow (review):**
136
+ 1. `/interlock:review-code` fans out up to six dimension reviewers (language, architecture, QA, delivery; devops and security when the diff earns them)
137
+ 2. Two skeptics try to refute every blocker and warning; a dismissal must cite a `file:line` span
138
+ 3. Only surviving findings reach you; the report states how many were dismissed
139
+
140
+ **Expert version:** The script holds the plan, not the model's context window.
141
+ Intermediate results stay in script variables, not chat history. Deterministic
142
+ retry logic instead of prompt-based "hopefully it retries."
143
+
144
+ **Common failure mode:** Re-implementing a workflow's job inline in the
145
+ conversation. Using a workflow for work that is genuinely sequential or needs a
146
+ human decision between steps.
147
+
148
+ **Preconditions for expert execution:**
149
+ - Tasks are independent (can parallelize without file conflicts)
150
+ - Verification commands are detectable (typecheck, test, lint)
151
+ - For `ship`: artifacts exist and passed Gate 1
152
+ - For `review-code`: implementation is complete (files to review exist)
153
+
154
+ ---
155
+
156
+ ## Shape 6: Agent-Team Investigation
157
+
158
+ **Duration:** 15-45 min | **Agents:** 3-6 persistent teammates | **Pattern:** debate → converge → decide
159
+
160
+ **Entry signal:** "The root cause is unclear" or "we need to investigate from multiple angles simultaneously" — when competing hypotheses need adversarial testing, not just parallel exploration.
161
+
162
+ **Flow:**
163
+ 1. Spawn 3-5 teammates, each assigned a different hypothesis or angle
164
+ 2. Teammates investigate independently AND challenge each other's findings
165
+ 3. The debate structure surfaces the theory that survives scrutiny
166
+ 4. The lead synthesizes consensus into a decision or a spec
167
+
168
+ **Expert version:** Unlike a workflow fan-out (which collects results silently),
169
+ agent teams *communicate*. Teammate A finds evidence that contradicts Teammate B's
170
+ theory and messages them directly. The adversarial communication is the value — it
171
+ prevents anchoring on the first plausible explanation.
172
+
173
+ **Best use cases:**
174
+ - Debugging with an unclear root cause (an investigator per hypothesis)
175
+ - Architecture decisions with genuine tradeoffs (an advocate per approach)
176
+ - Cross-layer coordination where subsystem owners negotiate interfaces
177
+
178
+ **Common failure mode:** Using teams for work that's purely mechanical parallelism
179
+ (use a workflow instead). Teams add coordination overhead — only worth it when
180
+ agents need to *talk to each other*.
181
+
182
+ **Preconditions:**
183
+ - Requires `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1` in settings
184
+ - The problem has multiple plausible approaches / causes (not a single clear path)
185
+ - Value comes from inter-agent communication, not just parallel execution
186
+
187
+ ---
188
+
189
+ ## Shape Selection Heuristic
190
+
191
+ When in doubt, start with the simplest shape that fits:
192
+
193
+ ```
194
+ Specific error + narrow scope → Targeted Fix
195
+ "Ship the tasks" + reviewed artifacts → Structured Execute
196
+ "Build X" + clear scope → Plan-then-Execute
197
+ "How / why / what if" + broad scope → Investigate-then-Propose
198
+ Reviewed change ready, or a diff to vet → Workflow-Orchestrated (ship / review-code)
199
+ Unclear root cause + competing theories → Agent-Team Investigation
200
+ ```
201
+
202
+ If you pick Targeted Fix and discover the scope is broader than expected, escalate
203
+ to Investigate-then-Propose. If you pick Plan-then-Execute and the change is already
204
+ reviewed, downgrade to Structured Execute. Shape classification is a starting point,
205
+ not a commitment.
206
+
207
+ **Choosing between Workflow and Agent Team:**
208
+ - Tasks are independent and the orchestration is known → Workflow (deterministic, cheaper)
209
+ - Agents need to communicate, challenge, or negotiate → Agent Team (higher cost, richer coordination)
210
+
211
+ ---
212
+
213
+ ## Anti-Patterns (What NOT to Do)
214
+
215
+ | Anti-Pattern | Why It Fails | Instead |
216
+ |-------------|-------------|---------|
217
+ | "Explore everything first" | Burns 20 min before any output | Pick a shape, start executing |
218
+ | "Let me read the whole codebase" | Context window fills with irrelevant code | Query the graph / read artifacts, then act |
219
+ | "I'll figure out the approach as I go" | Leads to backtracking and scope drift | Classify shape, announce scope, then execute |
220
+ | "Just one more thing…" | Scope creep extends sessions past 30 min | Handoff + new session for new scope |
221
+ | Single-threading work that could parallelize | 3x wall-clock for independent tasks | `ship` or an explore fan-out |
222
+ | Re-implementing `ship` inline | Context fills, intermediate results lost | Launch the `ship` workflow — results stay in script variables |
223
+ | Bridging `spec` → `ship` automatically | Skips the one human checkpoint | Stop at the checkpoint; let the human read |
224
+
225
+ ---
226
+
227
+ ## Expertise Level
228
+
229
+ **expertise_level**: Rate the USER's demonstrated expertise in the domain/task of THIS
230
+ session — command of its terminology, structures, and conventions — ONCE for the WHOLE
231
+ conversation, from the user's turns only. Rate domain familiarity with the work AT HAND,
232
+ NOT general intelligence, NOT the agent's performance, NOT task difficulty (a senior
233
+ engineer can be a beginner at Rust or differential privacy). Weigh three CO-EQUAL signals
234
+ together:
235
+ - Setup specificity: does the framing use named entities/constraints that require domain
236
+ knowledge to even reach for? Naming files/paths visible on screen is NOT domain
237
+ knowledge — anyone using the agent does that.
238
+ - Verification type: generic asks ("please double-check", "are you sure?") are epistemic
239
+ humility, not expertise; targeted asks ("did you actually call commit()?", "what's the
240
+ cardinality of that join?") require knowing WHAT to check.
241
+ - Direction of correction: the agent correcting the user's terminology/mental model pulls
242
+ DOWN (1-2); the user catching the agent's domain mistakes pulls UP (4-5); neither
243
+ correcting the other is neutral.
244
+ Emit exactly ONE value (default `2_beginner` only when there's nothing to go on, e.g. a
245
+ warmup session):
246
+ - `1_novice` — generic/imprecise framing, no domain names; verification absent or fully
247
+ generic; doesn't notice wrong output; agent supplies basic domain concepts.
248
+ - `2_beginner` — some correct terms used loosely; mostly generic verification; pushes back
249
+ only on obvious errors; agent reframes the user at least once. A fluent technologist
250
+ working OUTSIDE their domain lands here.
251
+ - `3_intermediate` — precise at a directive level (names files, outputs, libraries) but
252
+ doesn't engage methodology/tradeoffs; mix of generic and targeted checks; catches
253
+ meaningful mistakes. A domain expert delegating a routine task can also land here.
254
+ - `4_advanced` — structural domain knowledge NOT readable off the screen (a specific edge
255
+ case, non-obvious constraint/invariant, version-specific behavior, known failure mode);
256
+ moderately specific verification; catches at least one of the agent's domain mistakes;
257
+ the agent doesn't have to correct the user's domain model.
258
+ - `5_expert` — insider-only jargon/conventions, unprompted tradeoff discussion, surgical
259
+ verification, authoritative corrections invoking specific technical reasoning,
260
+ preemptive edge-case handling. Direction of correction is user->agent. "Insider talking
261
+ to an equal," even in a short session.
@@ -0,0 +1,106 @@
1
+ # Skill Candidate Brief — Author Contract
2
+
3
+ A durable handoff from a retro/review skill into a skill-author agent. It
4
+ captures evidence that a repeatable session procedure should become a new
5
+ `SKILL.md`, without writing the skill itself.
6
+
7
+ **Path:** `.claude/handoff/skill-candidate-<slug>-<YYYYMMDD-HHMMSS>.md`
8
+ Slug = the proposed skill name in kebab-case. Same handoff directory as explore
9
+ briefs (`${CLAUDE_PLUGIN_ROOT}/shared/EXPLORE-BRIEF.md`).
10
+
11
+ **When to write**
12
+
13
+ - The retro finds a repeatable procedure not covered by an existing skill.
14
+ - Cap at the top 3 candidates per review. Skip one-offs, pure chat, and duplicates
15
+ of existing skills (suggest extending those instead).
16
+
17
+ **Bound the brief** (~1–3k tokens): cite the concrete moments and the tool pattern;
18
+ treat the procedure steps as hypotheses the author must refine.
19
+
20
+ ---
21
+
22
+ ## Template (stable — skill-author agents depend on these headings)
23
+
24
+ ```markdown
25
+ # Skill Candidate Brief: <slug>
26
+
27
+ ## Meta
28
+ - source: interactive | loop
29
+ - slug: <kebab-case proposed skill name>
30
+ - confidence: high | med | low
31
+ - created: <ISO-8601 timestamp>
32
+ - session_refs: <e.g. this session, 2026-08-20>
33
+ - evidence_basis: current-session (live context, no transcript re-read)
34
+
35
+ ## Proposed Skill
36
+ - name: <kebab-case>
37
+ - description: <pushy trigger description — what it does AND when to use it>
38
+ - when_to_use: <bullet list of user phrases / contexts>
39
+ - when_not_to_use: <bullet list — overlaps, one-offs, better existing skills>
40
+
41
+ ## Evidence
42
+ <!-- The concrete moments this session, the tool pattern, the assigned shape + score.
43
+ State clearly which steps below are inferred rather than directly observed. -->
44
+
45
+ ## Procedure to Encode
46
+ <!-- Ordered steps a SKILL.md should teach. Hypotheses from what the session did — author must refine. -->
47
+ 1. ...
48
+ 2. ...
49
+
50
+ ## Inputs / Outputs / Exit Criteria
51
+ - inputs: <args, flags, natural-language triggers>
52
+ - outputs: <files written, chat summary shape>
53
+ - exit_criteria:
54
+ - [ ] ...
55
+
56
+ ## Related Existing Skills
57
+ <!-- From the enumerated skill list. Name overlaps and why this is NOT a duplicate
58
+ (or: "extend <skill> instead" and stop — do not invent a parallel skill). -->
59
+
60
+ ## Open Questions
61
+ <!-- Gaps the author must resolve before writing SKILL.md (scope, tools, output path, etc.). -->
62
+
63
+ ## Author Checklist
64
+ - [ ] Name + description are distinct from Related Existing Skills
65
+ - [ ] Trigger phrases are concrete enough to avoid under-triggering
66
+ - [ ] Procedure steps refined against at least one real session or user confirmation
67
+ - [ ] Inputs / outputs / exit criteria are testable
68
+ - [ ] Open questions resolved or explicitly deferred in the skill body
69
+ ```
70
+
71
+ ---
72
+
73
+ ## Section rules
74
+
75
+ | Section | Rule |
76
+ |---------|------|
77
+ | **Meta.confidence** | `high` = a repeated procedural shape with durable domain knowledge; `med` = a single strong session; `low` = weak signal / thin evidence |
78
+ | **Evidence** | Cite the concrete moments. Never invent tool calls, quotes, or token counts. |
79
+ | **Procedure to Encode** | Hypotheses labeled as such. Prefer a locate → act → verify shape. |
80
+ | **Related Existing Skills** | Must check the enumerated skill list. Prefer "extend X" over a new skill when overlap is high. |
81
+ | **Author Checklist** | If unchecked blockers remain, say so when pointing the user at the brief. |
82
+
83
+ ## Candidacy signals (what a retro looks for)
84
+
85
+ - The same multi-step procedure performed by hand that no skill covers
86
+ - A long, high-tool-count stretch reinventing a workflow absent from existing skills
87
+ - A mid/low shape score where a skill would have prevented the named failure mode
88
+ - A clear procedural shape (locate → transform → verify) with durable domain knowledge
89
+
90
+ **Skip:** one-off Targeted Fixes, chat-only stretches, and work already covered by
91
+ an existing skill (suggest extending that skill instead).
92
+
93
+ ## Chat reply after write
94
+
95
+ Do **not** re-dump full briefs into chat. Reply with:
96
+
97
+ 1. Paths to each `.claude/handoff/skill-candidate-*.md` written
98
+ 2. A one-line summary per candidate (slug + confidence + why)
99
+ 3. "Hand these to a skill-author agent to write the SKILL.md"
100
+
101
+ ## Author consumption
102
+
103
+ A skill-author agent reads the brief, resolves the Open Questions with the user,
104
+ then writes `skills/<name>/SKILL.md` (and docs / manifest wiring as needed). Prefer
105
+ the brief's recommendations over re-deriving the procedure; reopen the session only
106
+ for gaps or contradictions.
@@ -0,0 +1,204 @@
1
+ # Test Profile — Discovery & Persistence Contract
2
+
3
+ Durable, machine-readable record of how a project's tests run. Owned by
4
+ `/interlock:fix-tests` and reused by `/interlock:ship`, so neither has to
5
+ re-sniff `package.json` on every run.
6
+
7
+ **Path:** `.claude/testing/profile.json`
8
+ **Human mirror:** idempotent `## Testing` block in `CLAUDE.md` (or `AGENTS.md`
9
+ on Cursor-only projects) between HTML comment markers (see below).
10
+
11
+ Learned gotchas discovered mid-fix go to
12
+ `.claude/memory/failure-modes/<slug>.md` (max 3 per session) — **not** into
13
+ the profile.
14
+
15
+ ---
16
+
17
+ ## Schema (`profile.json`)
18
+
19
+ ```json
20
+ {
21
+ "version": 1,
22
+ "updated": "<ISO-8601>",
23
+ "discovered_from": ["package.json#scripts.test", "CI", "interview"],
24
+ "unit": {
25
+ "command": "npm test",
26
+ "cwd": ".",
27
+ "filter_syntax": "node --test --test-name-pattern <pattern> <file>",
28
+ "single_file": "node --test path/to/file.test.mjs",
29
+ "env": {},
30
+ "timeout_ms": 120000,
31
+ "prerequisites": []
32
+ },
33
+ "e2e": {
34
+ "enabled": false,
35
+ "command": null,
36
+ "cwd": ".",
37
+ "filter_syntax": null,
38
+ "single_file": null,
39
+ "env": {},
40
+ "timeout_ms": 600000,
41
+ "prerequisites": []
42
+ },
43
+ "coverage": {
44
+ "enabled": false,
45
+ "command": null,
46
+ "report_path": "coverage/lcov.info",
47
+ "format": "lcov"
48
+ },
49
+ "typecheck": {
50
+ "command": null,
51
+ "cwd": "."
52
+ },
53
+ "lint": {
54
+ "command": null,
55
+ "cwd": "."
56
+ },
57
+ "known_flaky": [],
58
+ "notes": []
59
+ }
60
+ ```
61
+
62
+ | Field | Meaning |
63
+ |-------|---------|
64
+ | `unit.command` / `e2e.command` | Full shell command that runs the suite (as in CI / `package.json`) |
65
+ | `filter_syntax` | How to run a subset by name/pattern (string with `<pattern>` placeholder) |
66
+ | `single_file` | How to run one file (string with path implied, or template) |
67
+ | `env` | Extra env vars required for a green run (not secrets — names/placeholders only) |
68
+ | `prerequisites` | Human-readable setup steps (dev server, seeded DB, `npx playwright install`, …) |
69
+ | `timeout_ms` | Soft budget for one full suite run; do not raise to mask races |
70
+ | `coverage.enabled` | Whether a coverage command is known. `false` = not configured (never an error) |
71
+ | `coverage.command` | Full shell command that produces a coverage report (e.g. `npm run coverage`) |
72
+ | `coverage.report_path` | Where the report lands, relative to `unit.cwd` (`coverage/lcov.info`, `coverage/coverage-summary.json`, …) |
73
+ | `coverage.format` | Report format the path holds: `lcov`, `json-summary`, `cobertura`, … |
74
+ | `typecheck.command` | Full shell command that type-checks the project (e.g. `npm run typecheck`). `null` = none found; the step is skipped with a reason, never guessed |
75
+ | `lint.command` | Full shell command that lints the project (e.g. `npm run lint`). `null` = none found; the step is skipped with a reason, never guessed |
76
+ | `known_flaky` | Paths or test-name patterns that flip on re-run (recorded, not "fixed") |
77
+ | `notes` | Short freeform caveats (≤5 lines total) |
78
+
79
+ `e2e.enabled` defaults to `false`. The skill never runs e2e unless the user
80
+ passes `--e2e` **and** `e2e.command` is set.
81
+
82
+ `coverage.enabled` also defaults to `false`, and coverage is always **advisory**
83
+ — a missing coverage tool, a failed coverage run, or 0% covered changed lines
84
+ must never fail a gate or turn a green suite red.
85
+
86
+ ---
87
+
88
+ ## Discovery precedence
89
+
90
+ First run (or `--reconfigure`): derive in order, stop when confident enough to
91
+ write a profile. Ask at most **4 questions** only when still ambiguous.
92
+
93
+ 1. **`package.json` / language manifests**
94
+ - `scripts.test`, `scripts.test:unit`, `scripts.test:e2e`, `scripts.test:ci`
95
+ - Python: `pytest` / `tox` / `pyproject.toml` `[tool.pytest]`
96
+ - Go: `go test ./…`
97
+ - Rust: `cargo test`
98
+ 2. **Runner config files** (infer filter + single-file syntax)
99
+ - `vitest.config.*`, `jest.config.*`, `playwright.config.*`
100
+ - `pytest.ini`, `pyproject.toml`, `.mocharc.*`
101
+ - Node built-in: `node --test` (filter: `--test-name-pattern`)
102
+ 3. **CI workflows** (`.github/workflows/*`, `.gitlab-ci.yml`, etc.)
103
+ - Prefer the job that runs unit tests on PRs
104
+ - Capture env and prerequisites from the job definition
105
+ 4. **Ask (≤4 questions, once)** — only for gaps that block a correct run:
106
+ - Unit command (if none found)
107
+ - Single-file / filter syntax (if not inferable)
108
+ - Required env or prerequisites
109
+ - E2e command (only if user asked about e2e / passed `--e2e`)
110
+
111
+ Every later run is **zero-question** unless `--reconfigure`.
112
+
113
+ ### Coverage discovery
114
+
115
+ `coverage` is resolved independently of `unit`/`e2e` and **never blocks**. Derive
116
+ in this order, stop at the first hit, and persist what you find:
117
+
118
+ 1. `coverage.command` already in the profile → use it as-is
119
+ 2. `package.json` `scripts.coverage`
120
+ 3. `package.json` `scripts["test:coverage"]`
121
+ 4. `vitest --coverage` / `jest --coverage` when vitest or jest is the runner
122
+ 5. `c8` / `nyc` present in `devDependencies` → wrap `unit.command`
123
+ 6. `node --test --experimental-test-coverage` when the runner is node:test
124
+
125
+ Never ask for a coverage command and never install tooling to obtain one —
126
+ nothing found → `enabled:false`, report and continue; never block.
127
+
128
+ ### Typecheck and lint discovery
129
+
130
+ `typecheck` and `lint` are resolved the same way `coverage` is — independently,
131
+ never by asking, never by inventing a command. Without them a ship run's
132
+ inter-wave checkpoint has no typecheck to run, and `typecheck` is a **halting**
133
+ kind there: a type error must stop the next wave from building on top of it.
134
+
135
+ Derive in this order, stop at the first hit, and persist what you find:
136
+
137
+ 1. the command already in the profile → use it as-is
138
+ 2. `package.json` `scripts.typecheck`, then `scripts["type-check"]`; for lint,
139
+ `scripts.lint`
140
+ 3. the step in the CI job that runs it (`.github/workflows/*`, `.gitlab-ci.yml`)
141
+
142
+ Nothing found → `command: null`. **Never ask, and never invent one** — a guessed
143
+ `tsc --noEmit` against a project that does not typecheck that way is a red step
144
+ nobody asked for. A `null` command is skipped with
145
+ `reason=no-detectable-command`, which is a stated absence rather than a silent
146
+ one.
147
+
148
+ **Stale profile:** rewrite when `unit.command` no longer exists in
149
+ `package.json` / manifests, or the command exits 127 / "not found". Otherwise
150
+ reuse as-is.
151
+
152
+ ---
153
+
154
+ ## Human mirror (`## Testing` block)
155
+
156
+ Write (or replace) content **between** these markers inside `CLAUDE.md`
157
+ (prefer) or `AGENTS.md` (if that is the project's agent doc and `CLAUDE.md`
158
+ is absent):
159
+
160
+ ```markdown
161
+ ## Testing
162
+
163
+ <!-- BEGIN interlock:testing -->
164
+ **Unit:** `<unit.command>` (cwd: `<unit.cwd>`)
165
+ **Filter:** `<unit.filter_syntax>`
166
+ **Single file:** `<unit.single_file>`
167
+ **E2E:** `<e2e.command or "opt-in via /fix-tests --e2e (not configured)">`
168
+ **Prerequisites:** <list or "none">
169
+ **Profile:** `.claude/testing/profile.json` (managed by `/interlock:fix-tests`)
170
+ <!-- END interlock:testing -->
171
+ ```
172
+
173
+ **Idempotency rules**
174
+
175
+ - If both markers exist: replace only the interior; leave surrounding prose alone.
176
+ - If `## Testing` exists without markers: insert markers + block immediately
177
+ under the heading (preserve any pre-existing bullet notes above the markers
178
+ only if they are clearly human-authored and outside a prior managed block).
179
+ - If no `## Testing` section: append the full section (heading + markers) at
180
+ end of file (or before `## Git` / `## Hooks` if those headings exist).
181
+ - Never duplicate the managed block. A second `/interlock:fix-tests` run must leave a
182
+ single pair of markers.
183
+
184
+ ---
185
+
186
+ ## Consumers
187
+
188
+ Anything that needs to run this project's tests reads
189
+ `.claude/testing/profile.json` first instead of sniffing `package.json`.
190
+
191
+ - `/interlock:fix-tests` — owns discovery, and is the only skill allowed to
192
+ interview the user to fill a field.
193
+ - `/interlock:ship` — reads the profile during its test wave and final
194
+ verification. Where the discovery ladder would ask a question, ship leaves the
195
+ field `null` and notes it. **Ship never interviews.**
196
+
197
+ **Two writers, no fighting.** `/interlock:fix-tests` and `/interlock:ship` both write this file.
198
+ Each writes only the fields it actually resolved and preserves every other key
199
+ verbatim — `/interlock:ship` never clears `known_flaky`, human-authored `notes`, or
200
+ `e2e` settings it did not discover, and `/interlock:fix-tests` leaves `coverage` alone
201
+ unless it discovered coverage tooling itself. Neither writer interviews the user
202
+ on behalf of the other: `/interlock:ship` is always headless and records
203
+ unresolvable fields as `null` plus a line in `notes`, where the ladder above
204
+ would have asked.