hstack 0.2.0 → 0.4.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.
@@ -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,197 @@
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. **Enumerate pending engineer flags.** Glob `hstack/kernel-fit/flags/pending/*.md`. Capture the list (may be empty). The analyst processes these per ADR-0005's Pending Flags Processing loop documented in `template/.claude/agents/kernel-fit-analyst.md`; this Skill is responsible for surfacing the list to the analyst and for the post-analyst pin-file moves at step 5.
82
+
83
+ 4. **Decide whether to invoke the subagent.** Parse the detector JSON. If every pattern has `fired: false` **and** no pending flags exist, exit clean: print "kernel-fit-scan: no patterns fired in window, no pending flags — 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. If patterns fired OR pending flags exist (or both), proceed to step 5.
84
+
85
+ 5. **Invoke `kernel-fit-analyst`.** Invoke via Task tool with `subagent_type: kernel-fit-analyst`. The prompt includes (a) the JSON evidence blob verbatim, (b) the list of pending flag paths from step 3 (or "no pending flags" when empty), (c) the canonical reminder of session-isolation (no implementer transcripts loaded), (d) the explicit instruction "one file per fired pattern; mandatory two-bullet counter-explanation; never write outside `hstack/kernel-fit/findings/`; process pending flags per the Pending Flags Processing section of the analyst prompt". The subagent's writes land in the working tree before the subagent returns. The analyst's response object reports the flag-processing counts (`processed`, `folded`, `emitted`, `not_actionable`, `transcript_truncated`).
86
+
87
+ 6. **Move processed pin files.** For each pin the analyst updated to `status: processed`, `git mv hstack/kernel-fit/flags/pending/<pin>.md hstack/kernel-fit/flags/processed/<pin>.md`. Create the `processed/` directory if missing. This move lands in the same commit as the finding writes at step 7 — the analyst's pin frontmatter updates (classification, classification-rationale, folded-into, emitted-as, status) plus the file move must be atomic from git's perspective. **Note**: pin files are gitignored per ADR-0005, so `git mv` operates only on the filesystem (git will not stage either side). The "atomicity" here is filesystem-only — the analyst's in-place frontmatter update and the directory move complete together before stdout reports flag-processing counts. If the gitignore is later relaxed (would require an ADR amendment), this step's `git mv` becomes a real staged operation.
88
+
89
+ 7. **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, or appended evidence rows to existing findings via fold). 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 landed; or `kernel-fit: <N> finding(s) detected (<F> from flags), <M> superseded` when flag-emit landed). One commit per scan run, atomic across all new/edited findings. When patterns did not fire and only pending flags were processed, the commit message reads `kernel-fit: <F> flag(s) processed (<E> emit, <FF> fold, <NA> not-actionable, <TT> transcript-truncated)`.
90
+
91
+ 8. **Notification — Slack nudge (best-effort).** Compute the notification set:
92
+
93
+ ```
94
+ notify = [f for f in newly_written_findings
95
+ if f.confidence in ("high", "medium")
96
+ and not _open_finding_exists_for_pattern_in_window(f.pattern, days=14)]
97
+ ```
98
+
99
+ For each finding in `notify`, send a Slack message via `mcp__claude_ai_Slack__slack_send_message` with the canonical body:
100
+
101
+ ```
102
+ hstack kernel-fit: <N> new finding(s)
103
+
104
+ • KF-NNNN — <title> [confidence: high|medium] [via: detector|flag]
105
+ Pattern: <KF-P1|KF-P2|KF-P3|KF-FLAG-NNNN>
106
+ Kernel surface: <one-line>
107
+ hstack/kernel-fit/findings/KF-NNNN-<slug>.md
108
+
109
+ Triage: /hstack:kernel-fit-triage KF-NNNN --action acknowledge
110
+ Dismiss: /hstack:kernel-fit-triage KF-NNNN --action dismiss --reason "..."
111
+ Promote: /hstack:kernel-fit-promote KF-NNNN --slug <adr-slug>
112
+ ```
113
+
114
+ Bundle multiple findings into a single message when more than one fires in this scan. When flag-processing occurred, append the **flag tail summary** as the last line of the same Slack message:
115
+
116
+ ```
117
+ Flags processed: <P> total — <FF> folded, <E> emitted, <NA> not-actionable, <TT> transcript-truncated.
118
+ ```
119
+
120
+ The tail is included only when at least one pin was processed AND at least one of `folded` or `emitted` is non-zero, OR when `transcript_truncated > 0` (the transcript-truncated count is operationally interesting because it surfaces v1-heuristic edge cases the engineer should know about). When every processed pin was classified `not-actionable` the tail is suppressed — it would otherwise be pure noise. When the scan produces zero detector findings AND only `not-actionable` pin processing, the Slack message is suppressed entirely (no findings, no tail-worthy signal).
121
+
122
+ 9. **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. <P> flag(s) processed; triage via /hstack:help to discover open findings.` Exit 0. The disk write from step 7 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.
123
+
124
+ 10. **Report.** Print to stdout: the count of patterns fired, the count of new findings written, the count of supersessions, the count of flags processed broken down by classification (`folded`, `emitted`, `not-actionable`, `transcript-truncated`), and the count of Slack notifications fired (or "skipped — Slack unreachable" / "skipped — within dedup window for all findings" / "skipped — no signal worth surfacing"). Done.
125
+
126
+ ## Outputs
127
+
128
+ - Zero or more files at `hstack/kernel-fit/findings/KF-NNNN-<slug>.md` at `status: open`.
129
+ - Zero or more supersession edits (status flip + `superseded-by` set) on prior finding files.
130
+ - Zero or more evidence-row appends to existing findings (from flag fold).
131
+ - Zero or more pin transitions from `hstack/kernel-fit/flags/pending/` to `hstack/kernel-fit/flags/processed/` with `status: processed` and the analyst-owned classification fields populated. Pins are gitignored per ADR-0005, so the moves are filesystem-only — not staged in git.
132
+ - Zero or one git commits (covering finding writes only; pin moves are not staged).
133
+ - Zero or one Slack messages (bundled when multiple findings notify, with the flag tail summary appended when applicable).
134
+
135
+ ## Auto-commit triggers
136
+
137
+ - 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 AND no flag-emit or flag-fold landed (i.e., only `not-actionable` / `transcript-truncated` flag processing occurred). Pin file moves are filesystem-only (pins are gitignored) and do not require a commit.
138
+
139
+ ## Idempotency contract
140
+
141
+ - Re-running the Skill when no patterns fire AND no pending flags exist: zero new disk artifacts, no commit, no Slack message. Pure no-op.
142
+ - Re-running when no patterns fire but pending flags exist: the analyst is invoked to process flags only. Outcomes depend on classification — fold/emit may produce finding writes and a commit; not-actionable/transcript-truncated produce only pin moves (no commit).
143
+ - 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.
144
+ - 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).
145
+ - Re-running with `--no-slack`: identical to a run with Slack unreachable — findings land on disk, flags are processed normally, no Slack message.
146
+ - Re-running after pending flags were processed in a prior scan: the prior pins are now in `processed/` and the analyst is forbidden from re-processing them (per the kernel-fit-analyst's discipline rules). Only newly-flagged pins (added to `pending/` since the last scan) are processed this run.
147
+
148
+ ## Stop conditions
149
+
150
+ Beyond the kernel's general stop conditions:
151
+
152
+ - Init is incomplete or `run_kernel_fit.py` is missing (`HSTACK-HALT: reason=missing-context`).
153
+ - Corpus is below the 3-shipped-changes floor — halt with the one-line note above.
154
+ - Detector script exits non-zero or produces malformed JSON — halt with the traceback and `HSTACK-HALT: reason=missing-context`.
155
+ - 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).
156
+ - 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.
157
+
158
+ Slack-MCP-unreachable is NOT a stop condition. See step 7.
159
+
160
+ ## Failure modes
161
+
162
+ - **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.
163
+ - **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.
164
+ - **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.
165
+ - **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.
166
+
167
+ ## Configuring Slack notifications (consumer-side)
168
+
169
+ 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:
170
+
171
+ 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.
172
+
173
+ 2. **Configure the destination channel.** Add a `kernel-fit` block to `hstack/config.yaml`:
174
+
175
+ ```yaml
176
+ kernel-fit:
177
+ slack-channel: "#hstack-kernel-fit" # public channel id or name; the bot must be invited
178
+ slack-fallback: "dm" # "dm" | "off" — behavior when slack-channel is absent or unreachable
179
+ ```
180
+
181
+ `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).
182
+
183
+ 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.
184
+
185
+ 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.
186
+
187
+ ## Anti-patterns
188
+
189
+ - Never invoke the `kernel-fit-analyst` subagent when no patterns fired. Empty invocations waste tokens and produce nothing.
190
+ - 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.
191
+ - Never silently retry Slack on transient failure. The disk artifact is canonical; the engineer's `/hstack:help` covers the missed-notification case.
192
+ - 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.
193
+ - Never claim the analyst's output is measured truth. Frame every finding as LLM-strategized judgment per the kernel's v1 / v2 split rule.
194
+ - Never bundle a Slack notification across scan runs. One scan, one message (or zero, when the dedup gate suppresses or Slack is unreachable).
195
+ - Never re-process a pin already in `hstack/kernel-fit/flags/processed/`. The analyst's discipline rule (no re-processing) is mirrored here: the Skill globs only `pending/`, never `processed/`. If the engineer believes a processed pin was mis-classified, the path is to re-flag (creating a fresh pin), not to move the prior pin back.
196
+ - Never include the flag tail summary in the Slack message when every pin was classified `not-actionable`. The tail's purpose is to surface actionable signal — the all-not-actionable case is pure noise and the suppression is deliberate.
197
+ - Never commit a pin move. Pins are gitignored per ADR-0005; the `git mv` from `pending/` to `processed/` is filesystem-only and produces no staged change.
@@ -0,0 +1,159 @@
1
+ ---
2
+ name: hstack-kernel-fit-triage
3
+ description: |
4
+ Use this skill when the engineer wants to triage an open kernel-fit finding — either acknowledge it (intent to act, not yet promoted) or dismiss it (won't pursue). The Skill is mechanical per ADR-0001: structured-elicitation loop, proposed-diff preview, Y/n confirmation, direct `Edit` writes, one atomic commit. No subagent invoked. Promotion to an ADR is a separate Skill (`/hstack:kernel-fit-promote`); this Skill never touches ADRs or change-specs. Examples:
5
+
6
+ <example>
7
+ Context: The engineer received a Slack nudge about KF-0001 (KF-P1, Category-A claim with production paths) and wants to acknowledge it for later promotion.
8
+ user: "/hstack:kernel-fit-triage KF-0001 --action acknowledge"
9
+ assistant: "I'll read KF-0001, print it in full, show the proposed-diff (status: open → acknowledged; owner: <git-handle>; updated: today; Triage Log entry appended), wait for Y/n, then Edit + commit `kernel-fit(KF-0001): acknowledge`. No reason is required for acknowledge. If you later decide to promote, run `/hstack:kernel-fit-promote KF-0001 --slug <adr-slug>`."
10
+ <commentary>
11
+ Acknowledge does not require a `--reason`. It is the "I've seen this, I intend to act, but I'm not promoting yet" state. The Triage Log records the transition. The finding stays at `status: acknowledged` until promote or dismiss happens.
12
+ </commentary>
13
+ </example>
14
+
15
+ <example>
16
+ Context: The engineer reviewed KF-0002 and decided the pattern is engineer-error, not a kernel gap.
17
+ user: "/hstack:kernel-fit-triage KF-0002 --action dismiss --reason \"The two changes flagged actually do have downstream consumers; the change-specs simply omitted the user-stories array because the engineer copied an old template. Not a kernel-flag issue, just stale boilerplate.\""
18
+ assistant: "I'll verify the reason is ≥50 characters (KF-05), read KF-0002, print the proposed-diff (status: open → dismissed; dismissed-reason: <text>; owner; updated; Triage Log entry), wait for Y/n, then commit `kernel-fit(KF-0002): dismiss`. Dismissal is terminal — re-running on this finding will surface that it's already dismissed and exit no-op."
19
+ <commentary>
20
+ Dismissal requires a reason of ≥50 characters per KF-05. The check is mechanical at validation time; the Skill enforces it at step 2. Drive-by dismissals are exactly the failure mode the rule exists to catch. The dismissed-reason is itself audit signal — a high rate of dismissals on a given pattern means either the pattern is poorly tuned or the engineer is gaming the gate.
21
+ </commentary>
22
+ </example>
23
+
24
+ <example>
25
+ Context: The engineer tries to dismiss a finding with too-short reason.
26
+ user: "/hstack:kernel-fit-triage KF-0003 --action dismiss --reason \"not relevant\""
27
+ assistant: "I'll halt — `dismissed-reason` is 12 characters; KF-05 requires ≥50. Drive-by dismissals defeat the audit signal. Either provide a more substantive reason or leave the finding at `open` (open is a legitimate long-term state when triage is genuinely deferred)."
28
+ <commentary>
29
+ Short-reason rejection is a hard halt. The Skill does not negotiate — write a real reason or leave the finding alone. Same shape as the deferral-check in `hstack-tech-debt-wontfix`.
30
+ </commentary>
31
+ </example>
32
+
33
+ tools:
34
+ - Read
35
+ - Write
36
+ - Edit
37
+ - Grep
38
+ - Glob
39
+ - Bash
40
+ - "{{TODO-SCRIPT: hstack/scripts/validate-spec.ts — validates the finding frontmatter flip and KF-05 (dismissed-reason ≥50 chars when status: dismissed)}}"
41
+ ---
42
+
43
+ ## Purpose
44
+
45
+ `hstack-kernel-fit-triage` is the mechanical status-flip Skill for kernel-fit findings. It moves a finding from `status: open` to either `acknowledged` (intent to promote, not yet) or `dismissed` (won't pursue, with mandatory rationale). Per ADR-0001, the Skill performs the `Edit` writes directly in the main session — no subagent invocation; the value to write is fully determined by the engineer's invocation arguments.
46
+
47
+ This Skill does not promote findings to ADRs. Promotion is a separate Skill (`/hstack:kernel-fit-promote`) with its own structured flow. This Skill also does not edit ADRs, change-specs, or any artifact outside `hstack/kernel-fit/findings/`.
48
+
49
+ ## When to invoke
50
+
51
+ Invoke when a finding is at `status: open` and the engineer wants to:
52
+
53
+ - **Acknowledge** (`--action acknowledge`): record intent to act on the finding without committing to an ADR yet. Useful when the finding is real but the team needs more thinking time, or when the right kernel-change shape is unclear.
54
+ - **Dismiss** (`--action dismiss --reason <text>`): close the finding without pursuing a kernel change. The reason is mandatory (≥50 characters per KF-05) and becomes part of the audit trail.
55
+
56
+ For findings already at `acknowledged`, re-invoking with `--action acknowledge` is a no-op; re-invoking with `--action dismiss --reason <text>` is permitted (acknowledged → dismissed is a valid transition when the engineer reconsiders). Findings at terminal status (`dismissed`, `promoted`, `superseded`, `archived`) are immutable from this Skill's perspective — the Skill halts.
57
+
58
+ ## Inputs
59
+
60
+ - `<finding-id>` (required, positional): the finding id, e.g. `KF-0001-category-a-claim-spans-production` or the short form `KF-0001` (the Skill resolves the latter to the matching file via glob).
61
+ - `--action <acknowledge | dismiss>` (required): the triage action.
62
+ - `--reason <text>` (required when `--action dismiss`; forbidden when `--action acknowledge`): the dismissal rationale. Must be ≥50 characters per KF-05.
63
+
64
+ ## Preconditions
65
+
66
+ - `hstack/kernel-fit/findings/<finding-id>*.md` exists. If missing, halt.
67
+ - The finding is at `status: open` (or `acknowledged` when transitioning to `dismissed`). If at any other status, halt with the current status named and the explanation that the status is terminal.
68
+ - `--action` is one of `acknowledge | dismiss` (controlled enum).
69
+ - When `--action dismiss`, `--reason` is non-empty and ≥50 characters.
70
+ - When `--action acknowledge`, `--reason` is absent (the Skill rejects redundant reasons to keep the audit signal clean — acknowledge reasons live in the next promote / dismiss invocation if needed).
71
+
72
+ ## Orchestration steps
73
+
74
+ 1. **Resolve the finding file.** Glob `hstack/kernel-fit/findings/<finding-id>*.md`. If zero matches, halt. If multiple matches (shouldn't happen with the immutable-id rule, but defense in depth), halt and ask the engineer to disambiguate.
75
+
76
+ 2. **Validate inputs against preconditions.** Walk the precondition checks above. On any failure, halt with the named reason. For the `--reason` length check (`dismiss`), surface the actual character count in the halt message so the engineer can size their next attempt.
77
+
78
+ 3. **Print the finding in full.** Read the resolved file and print its full body to the conversation. The engineer should re-read before committing to the triage action.
79
+
80
+ 4. **Compose the frontmatter edit.** Compute the exact frontmatter changes:
81
+
82
+ For `--action acknowledge`:
83
+ - `status: open → acknowledged`
84
+ - `owner: <git-handle>` (read from git config; fall back to the engineer's hstack config owner)
85
+ - `updated: <today>` (ISO date)
86
+
87
+ For `--action dismiss`:
88
+ - `status: open → dismissed` (or `acknowledged → dismissed`)
89
+ - `dismissed-reason: <text>` (the engineer's `--reason` verbatim)
90
+ - `owner: <git-handle>`
91
+ - `updated: <today>`
92
+
93
+ 5. **Compose the Triage Log append.** Compute the entry to append to the `## Triage Log` section:
94
+
95
+ For acknowledge:
96
+ ```
97
+ - `status: open → acknowledged` on <today> by <owner>. Triggered by `/hstack:kernel-fit-triage <id> --action acknowledge`.
98
+ ```
99
+
100
+ For dismiss:
101
+ ```
102
+ - `status: <prev> → dismissed` on <today> by <owner>. Reason: <dismissed-reason>.
103
+ ```
104
+
105
+ Defensive Triage Log check: if `## Triage Log` is not present in the file (legacy findings authored before the template included this section), append `\n## Triage Log\n` to the end of the file first.
106
+
107
+ 6. **Print the proposed diff.** Show the engineer the exact frontmatter changes and the exact Triage Log entry that will land. This is the kernel's mechanical-operations confirmation gate — until `validate-spec.ts` ships, the proposed-diff preview is the only contract check between the `Edit` and the commit.
108
+
109
+ 7. **Confirm.** Print "Apply triage to <finding-id>? (Y/n)". Default Yes. On `n`, abort without writing.
110
+
111
+ 8. **Edit + validate + commit.** On `Y`:
112
+ - `Edit` the file: frontmatter changes + Triage Log append.
113
+ - Run `{{TODO-SCRIPT: hstack/scripts/validate-spec.ts}}` against the file. KF-01 through KF-05 must pass; specifically KF-05 (`dismissed-reason` non-null and ≥50 chars when `status: dismissed`) gates dismissal.
114
+ - On validation pass: `git add` the file and commit with message `kernel-fit(<finding-id>): <action>` (e.g. `kernel-fit(KF-0001): acknowledge`).
115
+ - On validation failure: halt; revert via `git checkout -- <finding-file>`. Report the failing rule to the engineer.
116
+
117
+ 9. **Confirm completion.** Print "Triage applied: <finding-id> is now `<new-status>`. Next steps: `/hstack:kernel-fit-promote <finding-id> --slug <adr-slug>` to elevate to an ADR; or leave at <new-status> and let the next `/hstack:kernel-fit-scan` re-evaluate."
118
+
119
+ ## Outputs
120
+
121
+ - One edit to `hstack/kernel-fit/findings/<finding-id>*.md` — frontmatter flip + Triage Log append.
122
+ - One commit. Message: `kernel-fit(<finding-id>): <action>`.
123
+
124
+ ## Auto-commit triggers
125
+
126
+ - One commit at the status flip. The kernel's auto-commit-at-status-transition rule applies.
127
+
128
+ ## Idempotency contract
129
+
130
+ - Re-running with `--action acknowledge` on a finding already at `acknowledged`: no-op. The Skill prints "already acknowledged" and exits without writing.
131
+ - Re-running with `--action dismiss` on a finding already at `dismissed`: no-op. The Skill prints "already dismissed" + the existing `dismissed-reason` and exits without writing.
132
+ - Re-running with `--action dismiss` on a finding at `acknowledged`: permitted — the `acknowledged → dismissed` transition is valid. The Triage Log entry uses the `<prev> → dismissed` shape.
133
+ - Re-running on a finding at `promoted`, `superseded`, or `archived`: halt with the status named. These are terminal from this Skill's perspective.
134
+
135
+ ## Stop conditions
136
+
137
+ Beyond the kernel's general stop conditions:
138
+
139
+ - The finding does not exist or is at a terminal-from-triage status (`dismissed`, `promoted`, `superseded`, `archived`). Halt with the status named.
140
+ - `--action` is missing or not in the enum. Halt with usage.
141
+ - `--action dismiss` without `--reason`, or with `--reason` <50 characters. Halt with the actual length count.
142
+ - `--action acknowledge` with `--reason` present. Halt — acknowledge does not take a reason.
143
+ - The engineer declines confirmation at step 7. Abort cleanly.
144
+ - Validator fails at step 8 — halt with the failing rule; revert the unstaged edit.
145
+
146
+ ## Failure modes
147
+
148
+ - **Edit fails (filesystem, validator, git).** The frontmatter flip and the Triage Log append must land together in a single auto-commit. If `Edit` succeeds but `git add` or `git commit` fails, the working tree carries the unstaged change — revert via `git checkout -- <finding-file>` and re-invoke.
149
+ - **Drive-by dismissal attempt.** The ≥50-character check at step 2 is the v1 defense. Short reasons fail before any write occurs.
150
+ - **Stale finding (post-scan supersession in flight).** If a concurrent `/hstack:kernel-fit-scan` has just superseded the finding the engineer is triaging, the post-edit validator would catch the inconsistent state (superseded finding cannot be re-triaged). Halt and let the engineer re-fetch the working tree.
151
+
152
+ ## Anti-patterns
153
+
154
+ - Never invoke a subagent for triage. The action's value is fully determined by `--action` and `--reason`; the kernel's Mechanical operations section requires direct Skill writes for cases like this (saving ~25k subagent-context tokens per call).
155
+ - Never accept a dismissal reason shorter than 50 characters. The audit signal depends on substantive rationales — drive-bys defeat the loop.
156
+ - Never edit a finding's `pattern`, `evidence-rows`, `confidence`, or `evidence-row-count` fields. Those are the analyst's domain; triage only flips `status`, sets `owner` / `updated`, and writes `dismissed-reason` (when dismissing).
157
+ - Never edit a `promoted` finding's `promoted-to` field. That is owned by `/hstack:kernel-fit-promote` and is reciprocally bound to the target ADR.
158
+ - Never re-open a `dismissed` or `superseded` finding. If the kernel-fit pattern recurs with new evidence, the next `/hstack:kernel-fit-scan` will produce a new finding with a new id — that's the right path.
159
+ - Never invoke `spec-author` for the triage action. Per ADR-0001, the Skill writes directly.
@@ -31,15 +31,16 @@ tools:
31
31
 
32
32
  `hstack-telemetry` produces a retrospective observability report — a markdown file under `hstack/telemetry/reports/` — by parsing on-disk artifacts, git history, and Claude Code transcripts. The Skill is mechanical: it shells out to a Python script, reports the output path, and returns. No subagent is invoked. The kernel's "no parallel tracker" rule is preserved because the report is derivative — re-runnable from source, never authoritative.
33
33
 
34
- The report covers five buckets:
34
+ The report covers six buckets:
35
35
 
36
36
  1. **Token economics** — TE-1 cost-score per Skill, TE-2 cache-hit ratio per Skill, TE-3 subagent entry-tax amortization.
37
37
  2. **Workflow shape** — WS-1 phase duration, WS-2 gate findings density, WS-4 scope-amendment rate, WS-6 halt reasons.
38
38
  3. **Quality outcomes** — QO-2 severity × resolution-type mix, QO-3 test-immutability audit, QO-4 verifier observed-vs-promised.
39
39
  4. **Overengineering** — OE-1 artifact tokens per diff line, OE-3 subagent invocations × host cost, OE-5 trivial-eligible changes that ran the full gauntlet.
40
40
  5. **Contract drift** — module-spec staleness × recent commit activity, ADR supersession lag, tech-debt half-life by exit path.
41
+ 6. **Kernel-fit candidates** — KF-P1 Category-A claim spans production paths (post-PR-#5 misclassification), KF-P2 halt-reason cluster, KF-P3 missed-gate recovery. Detection-only rollup; the canonical findings live at `hstack/kernel-fit/findings/` and are produced by `/hstack:kernel-fit-scan`. See ADR-0004.
41
42
 
42
- A watch-list at the report bottom surfaces anomalies (low cache-hit Skills, high-severity findings resolved as `justified-in-prose`, candidate test-immutability violations, scope-amendment rate above 30%, module drift).
43
+ A watch-list at the report bottom surfaces anomalies (low cache-hit Skills, high-severity findings resolved as `justified-in-prose`, candidate test-immutability violations, scope-amendment rate above 30%, module drift, fired kernel-fit patterns).
43
44
 
44
45
  ## When to invoke
45
46
 
@@ -321,6 +321,7 @@ Load-at-session-start rules by subagent:
321
321
  - `implementer`: change-spec, plan, test-plan, security-review, data-review and ui-brief and figma-handoff when present, tech-stack, infrastructure (when surfaces includes infra).
322
322
  - `verifier`: change-spec, plan, test-plan, ci-cd.
323
323
  - `adversarial-reviewer`: all change artifacts (including test-plan); explicitly no implementer transcripts.
324
+ - `kernel-fit-analyst`: hstack/CLAUDE.md (the artifact under analysis), the latest hstack/telemetry/reports/<date>.md, every prior finding at hstack/kernel-fit/findings/, all change-specs at status: shipped (full bodies), all ADRs, all tech-debt, all module-specs; explicitly no implementer transcripts and no scratchpads from in-flight authoring sessions.
324
325
  - `researcher`: query context plus relevant product-context docs as the query requires.
325
326
 
326
327
  A subagent that cannot reach a required context document halts and asks the human, rather than proceeding without it.
@@ -390,6 +391,26 @@ When this kernel is loaded in a session that is adding or removing a Skill or su
390
391
 
391
392
  ---
392
393
 
394
+ ## How hstack improves itself
395
+
396
+ hstack ships a closed-loop system for detecting when the kernel itself — this file, the templates, the validators, the Skill flows — is misaligned with how engineers and AI agents actually use it. The loop has five layers and one non-negotiable contract: **the human gates promotion to a kernel change.** Detection and synthesis can be automated; the decision to amend the kernel cannot.
397
+
398
+ - **Detection is post-hoc and derivative.** `hstack/scripts/telemetry/insights/kernel_fit.py` pattern-matches across shipped change-specs, ADRs, tech-debt, halt sentinels, and adversarial-review findings. Every detection is reconstructible from git + frontmatter; the no-parallel-tracker rule is preserved because the detector reads, never writes.
399
+
400
+ - **Synthesis is delegated to the `kernel-fit-analyst` subagent.** Model `opus`, loads the kernel and all shipped artifacts and every prior finding, explicitly *not* implementer transcripts (same session-isolation rule as `adversarial-reviewer`). The analyst produces one finding file per pattern at `hstack/kernel-fit/findings/KF-NNNN-<slug>.md`, with a mandatory two-bullet counter-explanation challenge prompt that defends against false-positives. Findings carry a `confidence` enum and a `status` lifecycle (`open → acknowledged → promoted` for actionable findings; `open → dismissed` for non-actionable; `open → superseded` for restated findings).
401
+
402
+ - **Three Skills drive the lifecycle.** `/hstack:kernel-fit-scan` runs detection + synthesis + Slack nudge. `/hstack:kernel-fit-triage <id> --action acknowledge|dismiss --reason <text>` is a mechanical status flip per ADR-0001. `/hstack:kernel-fit-promote <id> --slug <adr-slug>` routes to `/hstack:adr-new --from-kernel-fit <id>`, mirroring the `--from-research` pattern already in use by `/hstack:research --promote`. The ADR's Context section is seeded from the finding's Evidence + Kernel Surface + Proposed Direction; `spec-author` runs the normal Nygard interview — this is the human gate. The reciprocal `promoted-to` write on the finding lands atomically with the ADR commit per the kernel's reciprocal-pair atomicity rule.
403
+
404
+ - **Notification is best-effort via Slack MCP.** Threshold-gated (notify on `high` and `medium` confidence only; `low` lands silently on disk) and de-duplicated (no re-notification on an open pattern within a 14-day window). Graceful degradation: if the Slack MCP is unreachable or unwired, findings still land on disk; the Skill logs to stderr and exits 0. This is a deliberate carve-out from the kernel's general MCP-unreachable stop condition — the disk write is load-bearing, Slack is a side-channel pointer, not authoritative state.
405
+
406
+ - **The analyst never writes ADRs, change-specs, or edits existing findings** (one carve-out: it may set `status: superseded` on a prior finding when restating it more cleanly). Promotion is engineer-initiated and routes through the established authoring Skills. Auto-creation of ADRs is forbidden — the kernel's "AI writes, humans confirm" contract applies most forcefully at the kernel-modification layer, where the cost of a bad ADR cascades through every subsequent change.
407
+
408
+ - **Engineer-triggered flags feed the loop with in-the-moment friction signal.** `/hstack:flag [hint]` is a one-shot Skill that drops a tiny frontmatter-only pin to `hstack/kernel-fit/flags/pending/` carrying session-id, transcript path, branch, HEAD, and timestamp. No interview, no confirmation, no commit, sub-second wall-clock. The next `/hstack:kernel-fit-scan` reads each pin, opens the referenced Claude Code session transcript, classifies the friction (`friction | missing-guardrail | kernel-vs-practice-mismatch | not-actionable | transcript-truncated`), then folds the signal into an existing finding or emits a new one with `detected-via: flag`. The pin carries no engineer interpretation of the friction — the analyst forms its classification from the transcript window, preserving the no-contamination contract. Pins are gitignored in the consuming repo (derivative signal, mirroring `.telemetry/` sidecars); the audit trail lives at the finding layer once the analyst processes them. Phase-1 of ADR-0005 ships the Skill and pin template; phase-2 ships the analyst processing extension — until phase-2 lands, pins accumulate harmlessly on disk. See ADR-0005 for the rationale and the trade-offs.
409
+
410
+ The loop is the smallest expression of the kernel reasoning about itself without auto-modifying itself. v1 honesty: the analyst's output is an LLM-strategized judgment, not measured truth; the counter-explanation challenge is the false-positive mitigation. Same framing rule as `test-strategist` and `security-reviewer`. See ADR-0004 for the detector-side rationale, ADR-0005 for the engineer-trigger side; see `template/templates/kernel-fit-finding.md` and `template/templates/kernel-fit-flag.md` for the artifact schemas.
411
+
412
+ ---
413
+
393
414
  ## References
394
415
 
395
416
  - Architecture document (long-form companion): https://www.notion.so/360d6791656c813d955af822cb8814d1