hstack 0.2.0 → 0.3.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/CHANGELOG.md CHANGED
@@ -6,6 +6,26 @@ All notable changes to hstack are documented here. Format follows [Keep a Change
6
6
 
7
7
  _Nothing yet._
8
8
 
9
+ ## [0.3.0] - 2026-05-22
10
+
11
+ ### Added
12
+ - **Kernel-fit closed-loop system.** Five-layer post-hoc detector for kernel-vs-practice drift, with a hard human gate on every promotion to a kernel change.
13
+ - L1 Detection — new Python insight module `template/scripts/telemetry/insights/kernel_fit.py` with three starter patterns: **KF-P1** `category-a-claim-spans-production-paths` (post-PR-#5 misclassification detector — catches `internal-tooling: true` claims whose in-scope reveals Category B), **KF-P2** `halt-reason-cluster-uncovered-by-enum` (Jaccard-clustered halt sentinels with `reason=other` surface missing enum cases), **KF-P3** `skill-precondition-violated-and-recoverable` (the ADR-0002 pattern — adversarial spec-compliance findings whose resolving commits mention a missed upstream gate). Pure read; derivative of git + frontmatter; preserves the no-parallel-tracker rule.
14
+ - L2 Synthesis — new `kernel-fit-analyst` subagent (model `opus`) loads kernel + shipped artifacts + prior findings; explicitly NOT implementer transcripts (same session-isolation rule as `adversarial-reviewer`). Produces one finding file per fired pattern with a mandatory two-bullet counter-explanation; auto-downgrades confidence to `low` if absent.
15
+ - L3 Artifact — new template `templates/kernel-fit-finding.md` with `KF-NNNN` ids, status lifecycle (`open → acknowledged → promoted | dismissed | superseded | archived`), validator rules KF-01..KF-05.
16
+ - L4 Skills — `/hstack:kernel-fit-scan` (detection + synthesis + Slack), `/hstack:kernel-fit-triage` (mechanical status flip per ADR-0001 with proposed-diff preview), `/hstack:kernel-fit-promote` (routes to `/hstack:adr-new --from-kernel-fit <id>`; `spec-author`'s Nygard interview is the human gate).
17
+ - L5 Notification — Slack via `mcp__claude_ai_Slack__slack_send_message`, threshold-gated (medium/high only), 14-day dedup, graceful degradation when MCP unreachable (deliberate carve-out from the kernel's general MCP-unreachable stop condition).
18
+ - **Kernel `## How hstack improves itself` section** in `template/CLAUDE.md` documenting the five-layer loop and the non-negotiable human-gate-on-promotion contract.
19
+ - **`promoted-from-kernel-fit: []` field** on `templates/adr.md` (adr schema-version 1 → 2) making the reciprocal back-reference (KF-04) mechanically checkable.
20
+ - **`--from-kernel-fit` flag** on `/hstack:adr-new`, mirroring the existing `--from-research` pattern.
21
+ - **`kernel-fit/` entry** added to `src/manifest.ts USER_CONTENT_PATHS` so `npx hstack update` never overwrites consumer triage state.
22
+
23
+ See [ADR-0004](adr/ADR-0004-kernel-fit-closed-loop.md) for the design rationale and the complementary relationship with PR #5's Category-B `enables` schema split.
24
+
25
+ ### Notes
26
+ - v1 honesty: detector patterns are hand-written Python rules; `validate-spec.ts` is still a `{{TODO-SCRIPT}}` placeholder so KF-01..KF-05 are bypassable via direct frontmatter edit until it ships; promote uses a two-commit pattern documented as a recoverable carve-out analogous to `/hstack:finalize` in-progress.
27
+ - KF-P1 was retargeted between authoring and release to detect the post-PR-#5 misclassification pattern rather than the pre-PR-#5 conflation (which PR #5 closed structurally).
28
+
9
29
  ## [0.2.0] - 2026-05-22
10
30
 
11
31
  ### Added
package/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.0
1
+ 0.3.0
package/dist/manifest.js CHANGED
@@ -33,6 +33,7 @@ export const USER_CONTENT_PATHS = [
33
33
  "tech-debt/",
34
34
  "research/",
35
35
  "telemetry/reports/",
36
+ "kernel-fit/",
36
37
  ];
37
38
  /**
38
39
  * Consumer-side wiring under `<consumer>/.claude/` — symlinks pointing into
@@ -1 +1 @@
1
- {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH;;;GAGG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,WAAW;IACX,YAAY;IACZ,iBAAiB;IACjB,iBAAiB;IACjB,oBAAoB;CACZ,CAAC;AAEX;;;;GAIG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,aAAa;IACb,UAAU;IACV,QAAQ;IACR,MAAM;IACN,YAAY;IACZ,WAAW;IACX,oBAAoB;CACZ,CAAC;AAEX;;;;GAIG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,4EAA4E;IAC5E,UAAU,EAAE;QACV,IAAI,EAAE,gBAAgB;QACtB,EAAE,EAAE,uBAAuB;KAC5B;IACD,+FAA+F;IAC/F,UAAU,EAAE,UAAU;IACtB,eAAe,EAAE,uBAAuB;IACxC,eAAe,EAAE,gBAAgB;CACzB,CAAC"}
1
+ {"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH;;;GAGG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,WAAW;IACX,YAAY;IACZ,iBAAiB;IACjB,iBAAiB;IACjB,oBAAoB;CACZ,CAAC;AAEX;;;;GAIG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,aAAa;IACb,UAAU;IACV,QAAQ;IACR,MAAM;IACN,YAAY;IACZ,WAAW;IACX,oBAAoB;IACpB,aAAa;CACL,CAAC;AAEX;;;;GAIG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,4EAA4E;IAC5E,UAAU,EAAE;QACV,IAAI,EAAE,gBAAgB;QACtB,EAAE,EAAE,uBAAuB;KAC5B;IACD,+FAA+F;IAC/F,UAAU,EAAE,UAAU;IACtB,eAAe,EAAE,uBAAuB;IACxC,eAAe,EAAE,gBAAgB;CACzB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hstack",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "A spec-driven engineering workflow that ships as Claude Code Skills and subagents.",
5
5
  "keywords": [
6
6
  "claude-code",
@@ -0,0 +1,137 @@
1
+ ---
2
+ name: kernel-fit-analyst
3
+ model: opus
4
+ description: |
5
+ Use this agent when `/hstack:kernel-fit-scan` has run the detection layer (`kernel_fit.py`) and the engineer wants evidence rows synthesized into one finding file per pattern, with confidence and a mandatory counter-explanation. The analyst loads the kernel, every shipped change-spec, every ADR, every tech-debt item, every module-spec, every prior finding, and the latest telemetry report — explicitly NOT implementer transcripts (same session-isolation rule as `adversarial-reviewer`). The analyst writes ONLY under `hstack/kernel-fit/findings/`. It never writes ADRs, change-specs, or edits existing findings except to mark them `superseded` when restating cleanly. Promotion to a kernel change is engineer-gated via `/hstack:kernel-fit-promote`. Examples:
6
+
7
+ <example>
8
+ Context: `/hstack:kernel-fit-scan` ran and KF-P1 fired with 2 candidate Category B rows. The analyst is being invoked with the JSON evidence blob.
9
+ user: "Synthesize findings from kernel_fit.compute() output."
10
+ assistant: "I'll write one finding per fired pattern. For KF-P1 I'll cite each evidence row's change-id with a specific signal (production-path count, missing user-story, downstream consumer), point to `template/CLAUDE.md § Frontmatter contract — the internal-tooling field` as the kernel surface implicated, propose a direction (split flag vs. enables array), and produce two counter-explanations or auto-downgrade confidence to `low`."
11
+ <commentary>
12
+ One file per pattern is atomic and non-bundled. The counter-explanation is mandatory; without two honest ones, the finding lands at `confidence: low` and skips Slack notification. This is the same false-positive discipline that `adversarial-reviewer`'s findings-floor enforces in reverse — there it forces minimum critique; here it forces minimum self-skepticism.
13
+ </commentary>
14
+ </example>
15
+
16
+ <example>
17
+ Context: The detector's `existing_open_findings_by_pattern` shows KF-0001 (KF-P1) is already at `status: open` from a prior scan. New evidence has been added.
18
+ user: "KF-P1 has an open finding from two weeks ago and new evidence today. Restate or supersede?"
19
+ assistant: "If the new finding's pattern body is materially clearer or the evidence is materially stronger, I'll write KF-0002 and set KF-0001's `status: superseded`, `superseded-by: KF-0002`. If the new evidence just adds rows without changing the framing, I'll skip writing a new finding and surface that the existing finding's evidence-rows should grow — but I don't edit the existing finding myself; that's the engineer's call via `/hstack:kernel-fit-triage`."
20
+ <commentary>
21
+ The supersession carve-out is narrow and deliberate. The analyst's default is append-only. Editing existing findings (other than the supersession status flip) would let the analyst rewrite the historical record, which violates the kernel's "no parallel tracker" rule and the audit-trail discipline.
22
+ </commentary>
23
+ </example>
24
+
25
+ <example>
26
+ Context: KF-P2 fired but the cluster representative-context is sparse — only short halt strings, no rich surrounding prose.
27
+ user: "KF-P2 fired with 3 cluster members but the context is thin."
28
+ assistant: "I'll write the finding at `confidence: low` and not bother fabricating a richer story than the evidence supports. Low-confidence findings land silently on disk; the engineer sees them on next `/hstack:kernel-fit-scan` rollup but no Slack fires. If the same cluster grows on a future run, the analyst can supersede with `confidence: medium`."
29
+ <commentary>
30
+ Confidence honesty is load-bearing. The temptation to inflate confidence so the Slack nudge fires is exactly the failure mode the threshold gate exists to mitigate. Same v1-vs-v2 honesty framing as `security-reviewer` (LLM-judgment, not measured truth).
31
+ </commentary>
32
+ </example>
33
+
34
+ tools:
35
+ - Read
36
+ - Grep
37
+ - Glob
38
+ - Write
39
+ - Bash
40
+ - "{{TODO-SKILL: /hstack:kernel-fit-scan — invokes kernel-fit-analyst with the detector's JSON evidence blob}}"
41
+ - "{{TODO-SCRIPT: hstack/scripts/validate-spec.ts — validates kernel-fit-finding frontmatter and KF-01 through KF-05}}"
42
+ - "{{TODO-OTHER: fresh-session-attestation — in v1, the agent self-attests the session is fresh and no implementer transcripts are loaded; v2 substrate will capture and compare Claude Code session-ids automatically, mirroring the adversarial-reviewer pattern}}"
43
+ ---
44
+
45
+ ## Role
46
+
47
+ The kernel-fit-analyst is hstack's meta-judgment agent. Its job is to take detector evidence about the kernel itself — fired patterns from `scripts/telemetry/insights/kernel_fit.py` — and produce one finding file per pattern, with explicit confidence and a mandatory counter-explanation. It is the only subagent whose primary subject is the kernel rather than a change. Its distinct perspective is post-hoc and structural: it reads what shipped, what halted, what surfaced as adversarial findings, and asks whether the kernel's contracts (status lifecycles, template fields, Skill preconditions, halt-enum coverage) match the practice that produced those artifacts.
48
+
49
+ The analyst never writes ADRs. The kernel's "AI writes, humans confirm" contract applies most forcefully at the kernel-modification layer; promotion to an ADR is engineer-gated via `/hstack:kernel-fit-promote`, which routes through `/hstack:adr-new` and exercises `spec-author`'s Nygard interview. The analyst's job ends at the finding artifact.
50
+
51
+ The analyst runs in a session separate from any implementer session — same honor-system rule as `adversarial-reviewer` in v1, same v2 substrate (session-id verification) when it ships. The reason is the same: meta-judgment about the kernel is contaminated when the same model that authored an in-flight change also reasons about whether the kernel that scopes it is correct.
52
+
53
+ ## Session start protocol
54
+
55
+ At session start, kernel-fit-analyst loads:
56
+
57
+ - `hstack/CLAUDE.md` (kernel) — the artifact under analysis; always loaded.
58
+ - The detector's output as a JSON blob (passed by `/hstack:kernel-fit-scan` orchestration). Contains: `existing_open_findings_by_pattern`, per-pattern `evidence_rows`, `fired` flags, and `note` fields.
59
+ - The latest `hstack/telemetry/reports/<date>.md` for cross-pattern context (token economics, workflow shape, etc. may corroborate a fired pattern).
60
+ - Every prior finding at `hstack/kernel-fit/findings/KF-*.md` — full bodies, not just frontmatter. Required for dedup decisions, supersession decisions, and `related-findings` population.
61
+ - Every change-spec at `status: shipped` — full bodies. Required because the analyst must cite specific changes in the Evidence section.
62
+ - Every ADR at `hstack/adr/ADR-*.md` — full bodies. The analyst must check whether a fired pattern is already addressed by a recent ADR (in which case the finding is a no-op or supersedes a stale earlier finding).
63
+ - Every tech-debt item at `hstack/tech-debt/TD-*.md` — full bodies. Same reason as ADRs.
64
+ - Every module-spec at `hstack/specs/<module>/spec.md` — for module-wide context.
65
+
66
+ Explicitly NOT loaded:
67
+
68
+ - Any conversation transcript or scratchpad from any in-flight implementer session.
69
+ - Any in-flight (non-`shipped`) change-spec body or its sub-artifacts. The analyst reasons about shipped practice only.
70
+ - The analyst's own prior session transcripts. Each scan is fresh against on-disk findings only (same rule as `adversarial-reviewer`).
71
+
72
+ The agent self-attests this exclusion in the `detected-by` provenance and in the Methodology of the first finding written this session. If implementer transcripts are visible, halt.
73
+
74
+ ## Templates this subagent writes
75
+
76
+ - `hstack/kernel-fit/findings/KF-<NNNN>-<slug>.md` — one file per pattern that fires. Atomic; never bundled. Frontmatter and section schema defined in `template/templates/kernel-fit-finding.md`.
77
+
78
+ ## Templates this subagent reads
79
+
80
+ - `hstack/templates/kernel-fit-finding.md` — the canonical template being filled.
81
+ - Every artifact named in the session start protocol.
82
+
83
+ ## Behavior rules
84
+
85
+ - **One file per fired pattern.** If the detector returns three fired patterns, the analyst produces three separate Write calls. Bundling multiple patterns into one file is forbidden — atomic findings are triageable; bundled reports rot.
86
+ - **Mandatory counter-explanation.** Every finding's `## Counter-explanations` section has exactly two non-empty bullets naming reasons the finding might NOT warrant a kernel change. If the analyst cannot produce two honest counter-explanations, it sets `confidence: low` automatically per KF-03. Saying "the pattern is small" alone is insufficient; each counter-explanation names a specific category (engineer error, transient practice, kernel-rule-not-actually-violated, etc.) and explains why.
87
+ - **Confidence honesty.** `high` confidence requires `evidence-row-count >= 3` AND ≥2 distinct change-specs cited (KF-02). `medium` is the conservative default. `low` is appropriate when evidence is thin, when the challenge prompts substantially weaken the finding, or when the analyst cannot produce two honest counter-explanations. Inflating confidence to fire a Slack nudge is the failure mode the threshold gate exists to mitigate.
88
+ - **Cite specific artifacts.** The `## Evidence` section's prose must include ≥1 inline citation per evidence row (change-id, ADR-id, TD-id, commit-sha, kernel section). KF-01 enforces `len(evidence-rows) == evidence-row-count`; the prose must cover each row. No prose without a citation.
89
+ - **Identify the kernel surface implicated.** The `## Kernel surface implicated` section is a single-sentence pointer (e.g., "`template/CLAUDE.md § Frontmatter contract — the internal-tooling field`"; "`template/templates/change-spec.md` frontmatter — `surfaces` enum"; "`/hstack:adversarial-review` precondition check at SKILL.md line 61"). Vague pointers ("the kernel needs work somewhere") are unacceptable — the analyst halts and re-reasons rather than ship a finding without a specific surface.
90
+ - **Propose direction, not specification.** The `## Proposed direction` section is one paragraph naming a possible kernel revision (split a flag, add an enum case, add a Skill precondition). It is NOT a full ADR; that work is done by `spec-author` if and when the engineer invokes `/hstack:kernel-fit-promote`. Over-specifying the direction in the finding pre-empts the human-gated decision.
91
+ - **Dedup against existing findings.** Before writing a new finding for pattern P, the analyst checks `existing_open_findings_by_pattern[P]` (from the detector output) and the prior-findings load. If an open or acknowledged finding for P already exists AND the new evidence is materially equivalent, the analyst skips writing and surfaces this to the calling Skill (which then suppresses the Slack notification per the 14-day dedup window). If the new evidence is materially stronger or restates the pattern more cleanly, the analyst writes the new finding AND sets the prior finding's `status: superseded` + `superseded-by: KF-<NNNN>`. This is the only carve-out from the never-edit-existing-findings rule.
92
+ - **Never write outside `hstack/kernel-fit/findings/`.** No ADRs, no tech-debt, no change-specs, no kernel edits. Hard refusal at every Write call to a path outside that directory.
93
+ - **Sequential IDs.** Read the highest existing `KF-NNNN-*.md` and increment. IDs are immutable once written per the frontmatter contract.
94
+ - **Provenance attestation.** Every finding's `detected-by: kernel-fit-analyst` and `detected-at: <ISO-8601>` are written by the analyst. The session-isolation attestation lives in the first finding written this session, in the Methodology-equivalent prose at the head of the `## Pattern fired` section.
95
+
96
+ ## Stop conditions
97
+
98
+ Stop and ask the human when:
99
+
100
+ - The session is not fresh (implementer transcripts visible, or in-flight change-spec scratchpads loaded). Halt and ask the engineer to open a new Claude Code session. Emit `HSTACK-HALT: reason=other` with the prose explanation.
101
+ - The detector output is malformed or missing required keys (no `existing_open_findings_by_pattern`, no per-pattern blocks, missing `fired` flags). Emit `HSTACK-HALT: reason=missing-context`.
102
+ - A fired pattern's `evidence_rows` is empty (the detector should not fire in this case; if it does, the detector itself is buggy and the analyst surfaces this instead of writing a finding without evidence).
103
+ - The analyst would need to write a kernel-surface pointer that is genuinely vague (no specific section / template / SKILL.md line to cite). Halt with `HSTACK-HALT: reason=ambiguous-spec`.
104
+ - The analyst would need to cite an artifact that does not exist (e.g., a change-id from `evidence_rows` whose change-spec file is not on disk). Halt and re-prompt the engineer.
105
+ - A high-confidence finding cannot honestly satisfy KF-02 (would require fabricating evidence rows or citations). Downgrade to `medium` or `low`; if the analyst would still need to fabricate at `low`, halt.
106
+
107
+ Halting is not failure. It is the correct response when preconditions for honest synthesis are not met.
108
+
109
+ ## Output expectations
110
+
111
+ A finding at terminal-write state has:
112
+
113
+ - All universal frontmatter plus `pattern`, `confidence`, `detected-by`, `detected-at`, `evidence-row-count`, `evidence-rows`, `related-findings`, `promoted-to: null`, `dismissed-reason: null`, `superseded-by: null`, `schema-version: 1`.
114
+ - Six required body sections per `template/templates/kernel-fit-finding.md`: Title, Pattern fired, Evidence (≥1 citation per row), Kernel surface implicated (single-sentence pointer), Proposed direction (one paragraph), Counter-explanations (exactly two non-empty bullets per KF-03), Confidence rationale (one paragraph), Triage Log (empty at terminal-write).
115
+ - Passes KF-01 (`len(evidence-rows) == evidence-row-count >= 1`), KF-02 (`high` confidence well-justified), KF-03 (two counter-explanations or auto-downgrade), KF-04 (`promoted-to: null` at terminal-write — promotion is downstream), KF-05 (`dismissed-reason: null` — dismissal is downstream).
116
+ - For a supersession write, the supersession edit on the prior finding (status flip + `superseded-by` set) lands in the same `Write` sequence as the new finding so the audit trail is atomic.
117
+
118
+ ## Anti-patterns
119
+
120
+ - Never bundle multiple patterns into one finding. One pattern, one file.
121
+ - Never write a finding without two counter-explanations. Auto-downgrade `confidence` instead.
122
+ - Never inflate `confidence` to trigger the Slack notification. The threshold gate's job is to suppress noise; gaming it is the failure mode.
123
+ - Never write outside `hstack/kernel-fit/findings/`. No ADRs, no change-specs, no kernel edits.
124
+ - Never edit existing findings except for the supersession carve-out (status flip + `superseded-by` set in the same atomic write).
125
+ - Never advocate a specific kernel change in `## Proposed direction` beyond a one-paragraph sketch. Over-specifying pre-empts the human-gated promotion.
126
+ - Never cite an artifact that does not exist or invent a commit-sha / change-id. Halt instead.
127
+ - Never load implementer transcripts or in-flight authoring scratchpads. If visible, halt.
128
+ - Never run in the same Claude Code session as an implementer. Honor system in v1; CI-verified in v2.
129
+ - Never claim the analyst's output is measured truth. Frame every finding as LLM-strategized judgment per the kernel's v1 / v2 split rule — same framing discipline that `test-strategist` and `security-reviewer` carry.
130
+
131
+ ## Confirmation discipline
132
+
133
+ The kernel-fit-analyst is structurally similar to `adversarial-reviewer`: it surfaces candidates for the human to confirm-or-rule-out, not findings for the human to merely accept. The challenge-prompt directive applies inverted: the analyst probes for what the kernel's *current contracts did not anticipate*, not what they explicitly cover. Silence from the engineer on a finding is not promotion; promotion is an explicit `/hstack:kernel-fit-promote` invocation. Silence on a finding is not dismissal either; dismissal is an explicit `/hstack:kernel-fit-triage --action dismiss --reason <text>` invocation. The analyst's findings sit at `status: open` indefinitely until the engineer acts.
134
+
135
+ The counter-explanation discipline is the analyst's primary internal check: every finding must defend itself against two honest reasons not to warrant a kernel change. If the analyst cannot produce two, the finding is downgraded to `confidence: low` and does not nudge Slack — the system is honest about the boundary between signal and noise.
136
+
137
+ The fresh-session honor system is part of the confirmation discipline: at session open, attest the session is fresh; if it is not, halt. The v2 substrate's session-id verification will close this loophole automatically.
@@ -44,6 +44,7 @@ Invoke when the engineer wants to capture a new architectural decision. Common t
44
44
  - `<slug>` (required, positional): kebab-case slug for the ADR. Examples: `pgvector-over-pinecone`, `trigger-dev-v4-only`, `per-tenant-encryption-keys`.
45
45
  - `--supersedes <ADR-NNNN>` (optional): id of the ADR being superseded. The Skill enforces reciprocity per AD-02.
46
46
  - `--from-research <session-id>` (optional): set when invoked via `hstack-research --promote`. The named research session is seeded into the Context section.
47
+ - `--from-kernel-fit <finding-id>` (optional): set when invoked via `hstack-kernel-fit-promote`. The named kernel-fit finding's Evidence + Kernel surface + Proposed direction are seeded into the Context section, and the new ADR's `promoted-from-kernel-fit` frontmatter array is populated with the finding id (reciprocal with `kernel-fit-finding.promoted-to`; KF-04). The finding's Counter-explanations section is NOT seeded — the engineer's Decision must engage fresh with the kernel-change question.
47
48
 
48
49
  ## Preconditions
49
50
 
@@ -54,12 +55,13 @@ Before any work:
54
55
  - Verify the `<slug>` matches `^[a-z][a-z0-9-]*$` and is not already used in any existing ADR id.
55
56
  - When `--supersedes` is provided: verify the referenced ADR exists at `status: accepted`.
56
57
  - When `--from-research` is provided: verify the research session file exists at `hstack/research/sessions/<session-id>.md`.
58
+ - When `--from-kernel-fit` is provided: verify the finding file exists at `hstack/kernel-fit/findings/<finding-id>*.md` and is at `status: open` or `acknowledged`.
57
59
 
58
60
  ## Orchestration steps
59
61
 
60
62
  1. **Compute the next id.** `ADR-NNNN-<slug>` where `NNNN` is the next sequential number, zero-padded to four digits.
61
63
 
62
- 2. **Invoke `spec-author`.** Use the Task tool with `subagent_type: spec-author` and context = [kernel, `hstack/templates/adr.md`, glossary, tech-stack, the superseded ADR when `--supersedes`, the research session when `--from-research`]. The subagent walks the six Nygard sections.
64
+ 2. **Invoke `spec-author`.** Use the Task tool with `subagent_type: spec-author` and context = [kernel, `hstack/templates/adr.md`, glossary, tech-stack, the superseded ADR when `--supersedes`, the research session when `--from-research`, the kernel-fit finding when `--from-kernel-fit` (Evidence + Kernel surface + Proposed direction extracted as Context seed; Counter-explanations excluded)]. The subagent walks the six Nygard sections.
63
65
 
64
66
  3. **Interview discipline.** Per the `spec-author` contract:
65
67
  - Title — short noun phrase. One field, one confirmation.
@@ -71,6 +73,8 @@ Before any work:
71
73
 
72
74
  4. **Supersession reciprocity.** When `--supersedes` is set, `spec-author` writes `superseded-by: <new-adr-id>` on the prior ADR and `supersedes: <prior-adr-id>` on the new one. AD-02 enforces reciprocity.
73
75
 
76
+ **Kernel-fit reciprocity.** When `--from-kernel-fit` is set, `spec-author` writes `promoted-from-kernel-fit: [<finding-id>]` on the new ADR. The reciprocal write on the finding (`promoted-to: adr:<new-adr-id>` plus the status flip to `promoted`) is performed by `/hstack:kernel-fit-promote` after this Skill returns, in a separate commit (the recoverable two-commit carve-out documented in that Skill's Failure modes — analogous to the `/hstack:finalize` in-progress carve-out).
77
+
74
78
  5. **Validate.** Run `{{TODO-SCRIPT: hstack/scripts/validate-spec.ts}}` — AD-01 (sequential id), AD-02 (reciprocal supersession), AD-03 (fixed section structure), AD-04 (`superseded` requires `superseded-by`).
75
79
 
76
80
  6. **Status transition.** When every section is confirmed and the validator passes, `spec-author` advances `status` from `proposed` to `accepted`. The engineer can override to keep `proposed` if the decision is still under discussion.
@@ -100,6 +104,7 @@ Beyond the kernel's general stop conditions:
100
104
  - The `<slug>` collides with an existing ADR.
101
105
  - A `--supersedes` target does not exist at `status: accepted`.
102
106
  - A `--from-research` session does not exist on disk.
107
+ - A `--from-kernel-fit` finding does not exist on disk or is at a status other than `open` / `acknowledged`.
103
108
  - The Consequences challenge prompt cannot produce two consequences that look bad; the engineer either thinks harder or accepts that this might not be ADR-worthy after all.
104
109
 
105
110
  ## Failure modes
@@ -0,0 +1,164 @@
1
+ ---
2
+ name: hstack-kernel-fit-promote
3
+ description: |
4
+ Use this skill when the engineer has decided that an `acknowledged` (or `open`) kernel-fit finding warrants a kernel change captured as an ADR. The Skill seeds the finding's Evidence + Kernel surface + Proposed direction into an ADR Context section and routes through `/hstack:adr-new --from-kernel-fit <id> --slug <slug>`, where `spec-author` runs the normal Nygard interview — the human gate the kernel's "AI writes, humans confirm" contract requires at the kernel-modification layer. After the ADR lands, the Skill writes the reciprocal `promoted-to` back-reference on the finding and flips it to `status: promoted`. Two commits in v1 — the ADR commit (from `adr-new`) and the back-reference commit (from this Skill); see Failure modes for the recoverable-two-commit carve-out (analogous to the `/hstack:finalize` in-progress carve-out). Examples:
5
+
6
+ <example>
7
+ Context: The engineer reviewed KF-0001 (KF-P1, Category-A claim spans production paths), acknowledged it via triage last week, and now wants to land an ADR tightening the SP-13 guidance.
8
+ user: "/hstack:kernel-fit-promote KF-0001 --slug sp-13-prose-guidance-tightening"
9
+ assistant: "I'll read KF-0001, verify status (open or acknowledged), construct the ADR Context seed from Evidence + Kernel surface + Proposed direction, and invoke `/hstack:adr-new --from-kernel-fit KF-0001 --slug sp-13-prose-guidance-tightening`. spec-author runs the Nygard interview — Title, Status, Context (seeded), Decision, Consequences (with the challenge prompt), Alternatives Considered. After the ADR lands at terminal status, I'll write `promoted-to: adr:ADR-NNNN-...` on KF-0001, flip status to `promoted`, append a Triage Log entry, and commit. The ADR commit and the back-reference commit are separate — see Failure modes for the recoverable carve-out."
10
+ <commentary>
11
+ The promote Skill is the only path that elevates a kernel-fit finding to an ADR. The kernel's "AI writes, humans confirm" contract is preserved because `/hstack:adr-new`'s spec-author interview is the human gate; the engineer reviews and confirms every Nygard section before the ADR lands. The promote Skill itself performs no field-level interview — it is a mechanical wrapper plus a reciprocal back-reference write per ADR-0001.
12
+ </commentary>
13
+ </example>
14
+
15
+ <example>
16
+ Context: The engineer wants to route a finding to tech-debt instead of an ADR — the kernel surface is not yet ready for revision but the team wants to track the gap.
17
+ user: "/hstack:kernel-fit-promote KF-0004 --target tech-debt --slug halt-enum-missing-figma-mcp-case"
18
+ assistant: "I'll route through `/hstack:tech-debt-new --from-kernel-fit KF-0004 --slug halt-enum-missing-figma-mcp-case`. spec-author runs the six-section TD interview (Title, Why we took the shortcut, What it costs us, Fix sketch, Pre-conditions, Acceptance). After the TD lands, I'll write `promoted-to: tech-debt:TD-NNNN-...` on KF-0004 and flip status to `promoted`."
19
+ <commentary>
20
+ The `--target tech-debt` flag is the secondary promotion path — useful when the engineer recognizes the kernel-fit gap but does not yet have a defensible Decision section for an ADR. Tech-debt captures "we know about this; here's what fixing it would look like; here's what's blocking us from fixing it now." Same reciprocal write contract as the ADR path.
21
+ </commentary>
22
+ </example>
23
+
24
+ <example>
25
+ Context: A scan-Skill run between `/hstack:kernel-fit-promote` invocations has superseded the finding the engineer is now trying to promote.
26
+ user: "/hstack:kernel-fit-promote KF-0001 --slug internal-tooling-split"
27
+ assistant: "I'll halt — KF-0001 is at `status: superseded` (`superseded-by: KF-0007`). Promote KF-0007 instead; its body restates the same pattern with newer evidence. The promote Skill never promotes a superseded finding because the audit trail would route the ADR back to a stale body."
28
+ <commentary>
29
+ Promote refuses on terminal-from-promote statuses (`promoted`, `dismissed`, `superseded`, `archived`). The Skill is conservative — re-running on a superseded finding would write the back-reference on the wrong artifact and the audit trail would skew. The engineer's recovery is to promote the superseding finding.
30
+ </commentary>
31
+ </example>
32
+
33
+ tools:
34
+ - Read
35
+ - Write
36
+ - Edit
37
+ - Grep
38
+ - Glob
39
+ - Bash
40
+ - Task
41
+ - "{{TODO-SCRIPT: hstack/scripts/validate-spec.ts — validates the finding back-reference and the new ADR/TD frontmatter (KF-04 reciprocity, AD-01..AD-04 for ADR, TD-01..TD-04 for tech-debt)}}"
42
+ ---
43
+
44
+ ## Purpose
45
+
46
+ `hstack-kernel-fit-promote` elevates a kernel-fit finding to a durable kernel-change artifact (ADR by default; tech-debt as a secondary path). It is the only Skill that flips a kernel-fit finding to `status: promoted`. The Skill is a mechanical wrapper per ADR-0001: it constructs the seed material from the finding's body and invokes the appropriate authoring Skill, then performs the reciprocal back-reference write directly. No subagent is invoked by this Skill itself — the authoring Skill it routes to (`/hstack:adr-new` or `/hstack:tech-debt-new`) invokes `spec-author` for the open-ended sections.
47
+
48
+ The contract the user explicitly required at design time: **no agent creates an ADR without a human gate.** Here the human gate is `spec-author`'s Nygard interview, which the engineer walks every section of before the ADR's `status: accepted` write lands. The promote Skill never auto-creates an ADR — it routes; the engineer confirms.
49
+
50
+ ## When to invoke
51
+
52
+ Invoke when:
53
+
54
+ - A kernel-fit finding at `status: open` or `acknowledged` warrants a kernel change and the engineer is ready to author the ADR (or capture the tech-debt). The team has discussed the finding; the proposed direction is approximately right; the engineer is ready to commit time to the interview.
55
+ - The finding's Counter-explanations have been considered and the team has decided they do not weaken the finding enough to dismiss.
56
+
57
+ Do NOT invoke when:
58
+
59
+ - The finding is at `confidence: low`. Low-confidence findings should be either dismissed (with substantive rationale) or left at `open` until the next scan accumulates evidence enough to upgrade them. Promoting `low` skips the implicit signal the analyst encoded.
60
+ - The finding has just been written and not yet triaged. Take 24–48 hours to think; the kernel is a high-stakes artifact and the cost of a bad ADR cascades.
61
+ - A scan run has just superseded the finding (`status: superseded`). Promote the superseding finding instead.
62
+
63
+ ## Inputs
64
+
65
+ - `<finding-id>` (required, positional): the finding id, e.g. `KF-0001-category-a-claim-spans-production` or the short form `KF-0001`.
66
+ - `--slug <text>` (required): kebab-case slug for the destination artifact. Passed through to the authoring Skill (`/hstack:adr-new --slug <text>` or `/hstack:tech-debt-new --slug <text>`).
67
+ - `--target <adr | tech-debt>` (optional, default `adr`): destination artifact type. ADR is the primary path; tech-debt is the secondary path for findings that name a gap but do not yet have a defensible Decision section.
68
+
69
+ ## Preconditions
70
+
71
+ - `hstack/kernel-fit/findings/<finding-id>*.md` exists. If missing, halt.
72
+ - The finding is at `status: open` or `status: acknowledged`. If at any other status (`dismissed`, `promoted`, `superseded`, `archived`), halt with the current status named.
73
+ - `--slug` is non-empty and matches `^[a-z][a-z0-9-]*$`.
74
+ - `--target` is in the controlled enum (`adr` or `tech-debt`).
75
+ - The downstream Skill (`hstack-adr-new` or `hstack-tech-debt-new`) is reachable from the consuming repo's `.claude/skills/`. (Auto-wired via the symlink delta on `npx hstack update`; this check is a defense against drift.)
76
+
77
+ ## Orchestration steps
78
+
79
+ 1. **Resolve and read the finding.** Glob `hstack/kernel-fit/findings/<finding-id>*.md`; on zero or multiple matches, halt. Parse frontmatter; verify status precondition. Print the finding body in full — the engineer reviews before committing the time to the interview.
80
+
81
+ 2. **Construct the seed material.** Extract three sections from the finding body verbatim:
82
+ - `## Evidence` (the bullet list with inline citations)
83
+ - `## Kernel surface implicated` (the single-sentence pointer)
84
+ - `## Proposed direction` (the one paragraph sketch)
85
+
86
+ Compose them into a "Seeded from kernel-fit finding <KF-id>" preface plus the three section bodies, verbatim. This preface becomes the Context seed for the downstream authoring Skill. The Counter-explanations section is NOT seeded — the engineer's Decision section must engage with the kernel-change question fresh, not pre-anchored by the analyst's challenge-prompt output.
87
+
88
+ 3. **Confirm before routing.** Print the proposed slug, the target (adr or tech-debt), and the seed preface. Ask "Route to /hstack:<target>-new with this seed? (Y/n)". Default Yes. On `n`, abort without writing.
89
+
90
+ 4. **Route to the authoring Skill.**
91
+
92
+ For `--target adr` (default):
93
+ - Invoke `/hstack:adr-new --from-kernel-fit <finding-id> --slug <slug>` via the Task tool or Skill orchestration mechanism. `spec-author` walks the six Nygard sections; the seeded Context is the engineer's starting material to review and revise.
94
+ - On the engineer's confirmation at `status: accepted`, `adr-new` writes the ADR file with `promoted-from-kernel-fit: [<finding-id>]` in its frontmatter and auto-commits with message `adr(ADR-NNNN-<slug>): accepted`.
95
+ - The ADR commit lands first. The back-reference commit lands second. See Failure modes for the recoverable carve-out.
96
+
97
+ For `--target tech-debt`:
98
+ - Invoke `/hstack:tech-debt-new --from-kernel-fit <finding-id> --slug <slug>`. `spec-author` walks the six TD sections; the seeded Context is split into Why we took the shortcut, What it costs us, and Fix sketch as appropriate.
99
+ - On terminal-state, `tech-debt-new` writes the TD file with `introduced-by: kernel-fit:<finding-id>` (the kernel-fit-origin variant of the TD `introduced-by` field; documented as a v1 carve-out — the existing field accepts a `kernel-fit:` prefix to disambiguate from change-spec origins) and auto-commits.
100
+
101
+ 5. **Capture the new artifact id.** Parse the downstream Skill's terminal commit message for `ADR-NNNN-<slug>` or `TD-NNNN-<slug>`. On parse failure (downstream Skill halted mid-interview), the promote Skill halts too — re-invocation will resume from the same step once the downstream Skill completes.
102
+
103
+ 6. **Write the reciprocal back-reference on the finding.** Per ADR-0001 (mechanical writes by the Skill orchestrator), the promote Skill performs the `Edit` itself:
104
+ - `promoted-to: adr:ADR-NNNN-<slug>` (or `tech-debt:TD-NNNN-<slug>`)
105
+ - `status: <prev> → promoted`
106
+ - `owner: <git-handle>` (if not already set by triage)
107
+ - `updated: <today>`
108
+ - Append to `## Triage Log`: `- \`status: <prev> → promoted\` on <today> by <owner>. Promoted to: <promoted-to>. Triggered by \`/hstack:kernel-fit-promote <id> --slug <slug>\`.`
109
+
110
+ Defensive Triage Log check: if `## Triage Log` is not present (legacy finding), append the section header first.
111
+
112
+ 7. **Print the proposed-diff preview** for the back-reference edit (per the kernel's mechanical-operations confirmation gate). Ask "Apply back-reference and flip status to promoted? (Y/n)". Default Yes.
113
+
114
+ 8. **Edit + validate + commit.** On `Y`:
115
+ - `Edit` the finding file.
116
+ - Run `{{TODO-SCRIPT: hstack/scripts/validate-spec.ts}}` against the finding. KF-04 (promoted requires `promoted-to` non-null AND referenced ADR/TD exists) must pass; the reciprocity check verifies the ADR's `promoted-from-kernel-fit` contains this finding's id.
117
+ - On validation pass: `git add` the finding file and commit with message `kernel-fit(<finding-id>): promoted to <promoted-to>`.
118
+ - On validation failure: halt; revert via `git checkout -- <finding-file>`. The ADR commit from step 4 has already landed and is correct — re-invoke promote (it is idempotent on the finding's promoted status, and the back-reference write will retry).
119
+
120
+ 9. **Confirm completion.** Print "Promote complete. Finding <finding-id> is now `promoted` with `promoted-to: <promoted-to>`. The kernel change lives in <promoted-to>; this finding is now read-only from kernel-fit's perspective (further edits would require a new finding via the next scan)."
121
+
122
+ ## Outputs
123
+
124
+ - A new ADR at `hstack/adr/ADR-NNNN-<slug>.md` OR a new TD at `hstack/tech-debt/TD-NNNN-<slug>.md` (via the downstream authoring Skill).
125
+ - An edit to `hstack/kernel-fit/findings/<finding-id>*.md` setting `promoted-to` and `status: promoted`, plus a Triage Log entry.
126
+ - Two commits — one from the authoring Skill (ADR or TD), one from this Skill (finding back-reference).
127
+
128
+ ## Auto-commit triggers
129
+
130
+ - Two commits per promote: the authoring Skill's own commit at the ADR or TD terminal state, and this Skill's own commit at the finding's `status: promoted` flip.
131
+
132
+ ## Idempotency contract
133
+
134
+ - Re-running on a finding already at `status: promoted`: the Skill prints "already promoted to <promoted-to>" and exits no-op.
135
+ - Re-running after step 4 succeeded but step 8 failed (the recoverable carve-out): the Skill detects the ADR or TD exists with the correct `promoted-from-kernel-fit` (or `introduced-by: kernel-fit:<id>`), skips re-invoking the authoring Skill, and goes straight to step 6 (back-reference write). Convergence in one re-invocation.
136
+ - Re-running on a `dismissed` or `superseded` finding: halt with the status named. The engineer cannot promote a finding that has been ruled out or restated.
137
+
138
+ ## Stop conditions
139
+
140
+ Beyond the kernel's general stop conditions:
141
+
142
+ - The finding does not exist, or is at a terminal-from-promote status (`dismissed`, `promoted`, `superseded`, `archived`). Halt with the status named.
143
+ - `--slug` is missing or malformed. Halt with the regex shown.
144
+ - `--target` is not in the enum. Halt with usage.
145
+ - The downstream authoring Skill halts mid-interview (`spec-author` could not produce two consequences for the ADR Consequences challenge, or the engineer aborted). Propagate the halt; re-invoke when ready.
146
+ - The downstream artifact's commit cannot be parsed for its id at step 5 (e.g., the authoring Skill committed under a non-canonical message format). Halt with a diagnostic and let the engineer reconcile.
147
+ - The validator fails at step 8 because the back-reference is inconsistent with the ADR's `promoted-from-kernel-fit` array. Halt and reconcile — most likely cause is the engineer aborted the authoring Skill before reciprocal write, leaving inconsistent on-disk state.
148
+
149
+ ## Failure modes
150
+
151
+ - **Recoverable two-commit carve-out (analogous to `/hstack:finalize` in-progress carve-out).** The kernel's atomicity rule (CLAUDE.md `## Mechanical operations § Atomicity for reciprocal pairs`) requires both halves of a reciprocal write to land in the same commit. The finalize Skill carves out an exception for multi-TD resolutions where intermediate state is intentional and recoverable by re-running. Promote adopts the same shape: the ADR (or TD) commit and the finding back-reference commit are separate, and an interruption between them is recoverable by re-running this Skill. During the window between the two commits, on-disk state shows the ADR with `promoted-from-kernel-fit: [<id>]` and the finding still at `status: open` or `acknowledged` — this is intentional and re-runnable. The Forbidden-no-matter-what kernel bullet about reciprocal-pair atomicity applies to **standing** state (post-promote), not the transient window during a single promote invocation. v2 substrate could add `--defer-commit` to the authoring Skills so both writes land atomically; v1 accepts the two-commit pattern for honest implementation reality.
152
+ - **Authoring Skill writes the wrong `promoted-from-kernel-fit` id.** Defense: this Skill's validation at step 8 cross-checks. If the ADR's array does not contain the finding id, the back-reference write is refused and the engineer reconciles by editing the ADR's frontmatter (this is itself a mechanical write per ADR-0001; manual `git commit --amend` is the recovery path, but the engineer should prefer re-running promote after correcting the ADR).
153
+ - **Drive-by promote.** The Skill's preflight does not detect promote attempts on `low`-confidence findings — the engineer is trusted to make this judgment. If a pattern of `low`-confidence promotes emerges, that itself becomes a future kernel-fit detection pattern (KF-Pn: "engineers promote findings the analyst rated low").
154
+ - **Engineer wants to promote two findings to one ADR.** Not supported in v1. Run promote twice with the same `--slug` — the second invocation will halt because the slug collides on `adr-new`'s precondition check. The engineer's recovery is to dismiss one of the findings with a rationale ("subsumed by KF-other-id promoting under slug X") and promote only the canonical one.
155
+
156
+ ## Anti-patterns
157
+
158
+ - Never auto-promote without engineer invocation. The contract is non-negotiable per ADR-0004.
159
+ - Never promote a finding at `low` confidence without a real reason. The analyst encoded a signal by setting confidence; ignoring it is a smell.
160
+ - Never edit the finding's body (Evidence, Kernel surface, Proposed direction, Counter-explanations, Confidence rationale) during promote. Those are the analyst's domain and are immutable from this Skill's perspective. The Triage Log append and the four frontmatter changes (status, promoted-to, owner, updated) are the only writes permitted.
161
+ - Never write the ADR or TD body. That is `spec-author`'s job, routed via the authoring Skill. Even pre-filling the Decision section based on the finding's Proposed direction is forbidden — the engineer's Decision must engage with the kernel-change question fresh.
162
+ - Never promote a `superseded` finding. The audit trail would route the ADR to a stale body.
163
+ - Never invoke `spec-author` directly from this Skill. Route through the appropriate authoring Skill (`hstack-adr-new` or `hstack-tech-debt-new`) so the existing challenge-prompts and validation rules apply.
164
+ - Never bypass the validator at step 8. KF-04 reciprocity is the load-bearing check that makes the kernel-fit-to-ADR audit chain reconstructible.
@@ -0,0 +1,180 @@
1
+ ---
2
+ name: hstack-kernel-fit-scan
3
+ description: |
4
+ Use this skill when the engineer wants to scan for kernel-fit gaps — patterns in shipped practice suggesting the kernel itself (CLAUDE.md, templates, validators, Skill flows) needs revision. The Skill runs the detection layer (`scripts/telemetry/run_kernel_fit.py`), invokes the `kernel-fit-analyst` subagent to synthesize one finding file per fired pattern, then nudges Slack on `high`/`medium` confidence findings with a 14-day dedup gate. Findings land at `hstack/kernel-fit/findings/KF-NNNN-<slug>.md`; the disk artifact is canonical, Slack is a side-channel pointer with graceful degradation when MCP is unwired. Promotion to a kernel change is human-gated via `/hstack:kernel-fit-promote`. See ADR-0004.
5
+
6
+ <example>
7
+ Context: The engineer wants a weekly kernel-fit scan after shipping several changes.
8
+ user: "/hstack:kernel-fit-scan"
9
+ assistant: "I'll run the detector against this repo's shipped changes / ADRs / tech-debt / commits / transcripts in the last 30 days. If any pattern fires, I'll invoke `kernel-fit-analyst` to synthesize findings (one file per pattern). Slack nudge on medium/high; dedup against open findings within 14 days. Disk-write is the load-bearing action — if Slack MCP is unreachable, the scan still completes and findings land on disk."
10
+ <commentary>
11
+ The Skill is opt-in by invocation. It is the only Skill that causes the `kernel-fit-analyst` subagent to run. Detection is post-hoc and derivative — no decision-time instrumentation; the no-parallel-tracker rule is preserved because the detector reads, never writes.
12
+ </commentary>
13
+ </example>
14
+
15
+ <example>
16
+ Context: The detector returns no fired patterns — the kernel is in good shape against the current corpus.
17
+ user: "/hstack:kernel-fit-scan"
18
+ assistant: "Detection ran; no patterns fired. No findings written, no Slack notification, no commit. The kernel-fit-analyst subagent was not invoked (no work to do). Exit clean."
19
+ <commentary>
20
+ No-fire is the most common output on a healthy repo, especially in the first 4–8 weeks of operation. The Skill must not invoke the subagent or write anything when no pattern fires — every Slack message has a cost in attention, and the dedup gate's purpose is undermined if scans run noise-free but the engineer still sees messages.
21
+ </commentary>
22
+ </example>
23
+
24
+ <example>
25
+ Context: KF-P1 fires but an open finding for the same pattern already exists from a scan 5 days ago.
26
+ user: "/hstack:kernel-fit-scan"
27
+ assistant: "KF-P1 fired with 3 evidence rows. An open finding (KF-0003) already exists for KF-P1 from 5 days ago — within the 14-day dedup window. I'll invoke the analyst; if the new evidence is materially equivalent the analyst will skip writing and surface the dedup; if it is materially stronger, the analyst writes a new KF-NNNN and supersedes KF-0003. Either way no Slack message fires today — the engineer was already notified within the window."
28
+ <commentary>
29
+ Dedup happens at two layers: (a) the analyst decides whether to write a new finding or skip (per its supersession carve-out); (b) the Skill decides whether to fire Slack. The 14-day window is the v1 default — tunable when calibration data exists.
30
+ </commentary>
31
+ </example>
32
+
33
+ tools:
34
+ - Read
35
+ - Write
36
+ - Edit
37
+ - Grep
38
+ - Glob
39
+ - Bash
40
+ - Task
41
+ - "{{TODO-MCP: Slack — mcp__claude_ai_Slack__slack_send_message — required for kernel-fit notifications. Without it, findings still land on disk; the Skill logs to stderr and exits 0 (deliberate carve-out from the kernel's general MCP-unreachable stop condition — Slack is a side-channel pointer, not authoritative state).}}"
42
+ - "{{TODO-SCRIPT: hstack/scripts/validate-spec.ts — validates kernel-fit-finding frontmatter and KF-01 through KF-05}}"
43
+ ---
44
+
45
+ ## Purpose
46
+
47
+ `hstack-kernel-fit-scan` is the entry point to the kernel-fit closed-loop system. The Skill orchestrates four steps: detection (Python script, pure read), synthesis (`kernel-fit-analyst` subagent, one file per fired pattern), notification (Slack, best-effort), and the atomic commit. Promotion to a kernel change is a separate, human-invoked Skill (`/hstack:kernel-fit-promote`).
48
+
49
+ The Skill is the only path by which the `kernel-fit-analyst` subagent runs. The kernel's "AI writes, humans confirm" contract is preserved at the kernel-modification layer because the analyst writes findings only — never ADRs, never change-specs, never kernel edits.
50
+
51
+ ## When to invoke
52
+
53
+ Common cadence:
54
+
55
+ - Weekly for active engineering — catches kernel-vs-practice drift early.
56
+ - After every ~5 shipped changes — surfaces patterns single-change inspection cannot.
57
+ - After a kernel edit lands — confirm the edit moved the relevant pattern off the fired list (or did not introduce a new one).
58
+ - Whenever a `kernel-fit-analyst` finding is restated or superseded by triage, to let the analyst write the supersession edit atomically.
59
+
60
+ The Skill is opt-in. There is no cron, no automatic invocation, no event-driven trigger in v1.
61
+
62
+ ## Inputs
63
+
64
+ - `--window <N>` (optional): limit detector history to the last N days. Default 30. `--window 0` means all-history.
65
+ - `--no-slack` (optional): skip the Slack notification step even if MCP is wired. Useful for dry-runs or for replaying a scan when the engineer has already been notified manually.
66
+
67
+ ## Preconditions
68
+
69
+ - `hstack/config.yaml` exists at `init-status: complete`. If init is incomplete, halt with `HSTACK-HALT: reason=missing-context`.
70
+ - The wrapper script `hstack/scripts/telemetry/run_kernel_fit.py` exists. If missing, halt with `HSTACK-HALT: reason=missing-context` and the recommendation "re-vendor hstack or run `/hstack:configure`".
71
+ - The repo contains at least 3 shipped change-specs (count by `status: shipped` across `hstack/specs/changes/*/spec.md`). Below this threshold, the corpus is too small for cross-correlation; the Skill halts with a one-line note: "kernel-fit scan needs ≥3 shipped changes; corpus is too small to produce honest evidence."
72
+ - Python 3.10+ is available on PATH (matches `hstack-telemetry`).
73
+ - The git repository is intact (`.git/` present).
74
+
75
+ ## Orchestration steps
76
+
77
+ 1. **Verify preconditions.** Walk the checks above; halt with the named reason if any fail. Print "kernel-fit-scan: preconditions OK, N shipped changes in corpus, M-day window" to stderr.
78
+
79
+ 2. **Run detection.** Shell out: `python3 hstack/scripts/telemetry/run_kernel_fit.py --repo <repo-root> --window <N>`. Capture stdout as JSON; capture stderr as the diagnostic log. On non-zero exit, surface the traceback and halt with `HSTACK-HALT: reason=missing-context`.
80
+
81
+ 3. **Decide whether to invoke the subagent.** Parse the JSON. If every pattern has `fired: false`, exit clean: print "kernel-fit-scan: no patterns fired in window — kernel is consistent with shipped practice in current corpus" to stdout, write nothing, do not invoke the subagent, do not nudge Slack, do not commit. This is the most common outcome on a healthy repo.
82
+
83
+ 4. **Invoke `kernel-fit-analyst`.** When at least one pattern fired, invoke via Task tool with `subagent_type: kernel-fit-analyst`. The prompt includes (a) the JSON evidence blob verbatim, (b) the canonical reminder of session-isolation (no implementer transcripts loaded), (c) the explicit instruction "one file per fired pattern; mandatory two-bullet counter-explanation; never write outside `hstack/kernel-fit/findings/`". The subagent's writes land in the working tree before the subagent returns.
84
+
85
+ 5. **Stage and commit findings.** Compute the set of newly-written or modified finding files (analyst may have superseded a prior finding atomically with a new one). Print the proposed diff for engineer confirmation (per the kernel's "AI writes, humans confirm" mechanical-operations adaptation). On Y/n confirmation `Y` (default Yes), `git add` the finding files and commit with message `kernel-fit: <N> finding(s) detected` (or `kernel-fit: <N> finding(s) detected, <M> superseded` when supersession edits also landed). One commit per scan run, atomic across all new/edited findings.
86
+
87
+ 6. **Notification — Slack nudge (best-effort).** Compute the notification set:
88
+
89
+ ```
90
+ notify = [f for f in newly_written_findings
91
+ if f.confidence in ("high", "medium")
92
+ and not _open_finding_exists_for_pattern_in_window(f.pattern, days=14)]
93
+ ```
94
+
95
+ For each finding in `notify`, send a Slack message via `mcp__claude_ai_Slack__slack_send_message` with the canonical body:
96
+
97
+ ```
98
+ hstack kernel-fit: <N> new finding(s)
99
+
100
+ • KF-NNNN — <title> [confidence: high|medium]
101
+ Pattern: <KF-P1|KF-P2|KF-P3>
102
+ Kernel surface: <one-line>
103
+ hstack/kernel-fit/findings/KF-NNNN-<slug>.md
104
+
105
+ Triage: /hstack:kernel-fit-triage KF-NNNN --action acknowledge
106
+ Dismiss: /hstack:kernel-fit-triage KF-NNNN --action dismiss --reason "..."
107
+ Promote: /hstack:kernel-fit-promote KF-NNNN --slug <adr-slug>
108
+ ```
109
+
110
+ Bundle multiple findings into a single message when more than one fires in this scan.
111
+
112
+ 7. **Graceful degradation when Slack is unreachable.** If the MCP call raises (tool not configured, network failure, channel-not-found, etc.), log to stderr: `kernel-fit: Slack MCP unreachable; <N> finding(s) written to disk without notification. Triage via /hstack:help to discover open findings.` Exit 0. The disk write from step 5 is the load-bearing action; Slack is a side-channel pointer. This is a deliberate carve-out from the kernel's general MCP-unreachable stop condition — Slack is not load-bearing for kernel-fit (the canonical state lives on disk and is reachable via `/hstack:help`). The carve-out is documented here and in the kernel's `## How hstack improves itself` section.
113
+
114
+ 8. **Report.** Print to stdout: the count of patterns fired, the count of new findings written, the count of supersessions, the count of Slack notifications fired (or "skipped — Slack unreachable" / "skipped — within dedup window for all findings"). Done.
115
+
116
+ ## Outputs
117
+
118
+ - Zero or more files at `hstack/kernel-fit/findings/KF-NNNN-<slug>.md` at `status: open`.
119
+ - Zero or more supersession edits (status flip + `superseded-by` set) on prior finding files.
120
+ - Zero or one git commits.
121
+ - Zero or one Slack messages (bundled when multiple findings notify).
122
+
123
+ ## Auto-commit triggers
124
+
125
+ - One commit at the writing of finding files (per the kernel's auto-commit-at-status-transition rule applied at the artifact-creation moment). No commit when no patterns fire.
126
+
127
+ ## Idempotency contract
128
+
129
+ - Re-running the Skill when no patterns fire: zero new disk artifacts, no commit, no Slack message. Pure no-op.
130
+ - Re-running when patterns fire that already have open findings within the dedup window: the analyst is invoked, sees existing findings, and may skip-write or supersede; Slack notification is suppressed by the dedup gate.
131
+ - Re-running when the same patterns fire with new evidence: the analyst may produce supersession edits; the dedup gate still suppresses Slack (already-notified within the window).
132
+ - Re-running with `--no-slack`: identical to a run with Slack unreachable — findings land on disk, no Slack message.
133
+
134
+ ## Stop conditions
135
+
136
+ Beyond the kernel's general stop conditions:
137
+
138
+ - Init is incomplete or `run_kernel_fit.py` is missing (`HSTACK-HALT: reason=missing-context`).
139
+ - Corpus is below the 3-shipped-changes floor — halt with the one-line note above.
140
+ - Detector script exits non-zero or produces malformed JSON — halt with the traceback and `HSTACK-HALT: reason=missing-context`.
141
+ - The `kernel-fit-analyst` subagent halts mid-run — propagate the halt sentinel; do not commit partial output (the analyst's writes-so-far remain unstaged on disk; the engineer can either delete them or re-invoke the Skill which will see them on next run).
142
+ - The proposed-diff confirmation at step 5 is declined — abort the commit; the analyst's writes-so-far remain in the working tree. Re-invoke to continue.
143
+
144
+ Slack-MCP-unreachable is NOT a stop condition. See step 7.
145
+
146
+ ## Failure modes
147
+
148
+ - **Detector raises on malformed frontmatter in a shipped change-spec.** The detector is read-only and tolerant of missing fields; the parser falls back to a degraded YAML reader. If a corrupted frontmatter file genuinely crashes the parser, the traceback names the offending file — fix it and re-run.
149
+ - **Subagent produces a finding without two counter-explanations.** The analyst auto-downgrades `confidence: low` per KF-03; the finding still lands on disk but does not nudge Slack. No remediation needed — this is the designed behavior.
150
+ - **Subagent writes a finding outside `hstack/kernel-fit/findings/`.** Scope-lock violation. The subagent's prompt forbids this at every Write call. If observed, surface as a kernel-fit-analyst contract violation and do not commit; report the offending path for diagnosis.
151
+ - **Slack message rejected by the MCP server (rate limit, channel-not-found, auth error).** Same as MCP unreachable — log to stderr, exit 0, the engineer triages via `/hstack:help` on next session.
152
+
153
+ ## Configuring Slack notifications (consumer-side)
154
+
155
+ Slack notifications are opt-in per consumer. Without wiring, the Skill still works — findings land on disk and the engineer discovers them via `/hstack:help`. To enable Slack nudges on `medium`/`high` confidence findings:
156
+
157
+ 1. **Wire the MCP server.** Add the Slack MCP to your Claude Code MCP configuration so `mcp__claude_ai_Slack__slack_send_message` is callable from the session that runs `/hstack:kernel-fit-scan`. Follow Anthropic's Slack MCP install docs; the auth scope `chat:write` is required.
158
+
159
+ 2. **Configure the destination channel.** Add a `kernel-fit` block to `hstack/config.yaml`:
160
+
161
+ ```yaml
162
+ kernel-fit:
163
+ slack-channel: "#hstack-kernel-fit" # public channel id or name; the bot must be invited
164
+ slack-fallback: "dm" # "dm" | "off" — behavior when slack-channel is absent or unreachable
165
+ ```
166
+
167
+ `slack-channel` is optional. When absent and `slack-fallback: "dm"`, the Skill sends to the invoking engineer's DM via the bot. When `slack-fallback: "off"`, missing channel behaves identically to unreachable MCP (log to stderr, exit 0).
168
+
169
+ 3. **Verify with a dry-run.** Run `/hstack:kernel-fit-scan --no-slack` first to confirm the detection layer produces output on your corpus, then re-run without the flag once Slack is wired. The first non-`--no-slack` run will surface any auth or channel issues as the documented graceful-degradation log line.
170
+
171
+ What you do NOT need to do: no code to write, no hook to install. The Skill is prose-driven; the runtime LLM agent invokes the MCP when the tool is available in the session and the config names a destination. The `{{TODO-MCP}}` placeholder in the tools array is the framework convention naming the contract — the consumer's MCP wiring satisfies it.
172
+
173
+ ## Anti-patterns
174
+
175
+ - Never invoke the `kernel-fit-analyst` subagent when no patterns fired. Empty invocations waste tokens and produce nothing.
176
+ - Never auto-promote a finding to an ADR. Promotion is a separate, human-invoked Skill (`/hstack:kernel-fit-promote`). The contract is non-negotiable per ADR-0004.
177
+ - Never silently retry Slack on transient failure. The disk artifact is canonical; the engineer's `/hstack:help` covers the missed-notification case.
178
+ - Never write outside `hstack/kernel-fit/findings/` or modify any artifact not produced by the analyst this run. This Skill orchestrates; it does not author.
179
+ - Never claim the analyst's output is measured truth. Frame every finding as LLM-strategized judgment per the kernel's v1 / v2 split rule.
180
+ - Never bundle a Slack notification across scan runs. One scan, one message (or zero, when the dedup gate suppresses or Slack is unreachable).