hstack 0.1.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
@@ -2,7 +2,39 @@
2
2
 
3
3
  All notable changes to hstack are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versioning follows [SemVer](https://semver.org/).
4
4
 
5
- ## [Unreleased] — 0.1.0
5
+ ## [Unreleased]
6
+
7
+ _Nothing yet._
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
+
29
+ ## [0.2.0] - 2026-05-22
30
+
31
+ ### Added
32
+ - **Category-B `enables` carve-out on change-spec.** New frontmatter fields `enables: []` and `enabled-by: []` separate foundational-prerequisite production code (Category B) from engineering-only `internal-tooling: true` (Category A). SP-09 expands to `user-stories` non-empty UNLESS `internal-tooling: true` UNLESS `enables` non-empty. New validator rules SP-13 (mutual exclusion of A and B) and SP-14 (`enables ↔ enabled-by` reciprocity). `/hstack:change-new` reconciles forward references at downstream scaffold time; `/hstack:help` renders the audit chain; `/hstack:ship` gains GT-12. Backwards-compatible — no schema-version bump; existing specs without the fields validate unchanged. See [ADR-0003](adr/ADR-0003-category-b-enables-field.md).
33
+
34
+ ### Infrastructure
35
+ - **GitHub Action `publish.yml`.** Pushes to npm on `v*` tags. Verifies tag matches `package.json` version, publishes with `--provenance` and `--access public`. Requires repo secret `NPM_TOKEN`.
36
+
37
+ ## [0.1.0] - 2026-05-22
6
38
 
7
39
  ### Added
8
40
  - **CLI installer.** Three commands shipped on the `hstack` npm package:
@@ -18,14 +50,8 @@ All notable changes to hstack are documented here. Format follows [Keep a Change
18
50
  - Framework files relocated from repo root into `template/`. Consumer-facing layout is unchanged — consumers still see `hstack/CLAUDE.md`, `hstack/templates/`, etc. after install.
19
51
  - README installation section: `npx hstack init` is now the documented path; manual `cp -r` is the legacy fallback.
20
52
 
21
- ### Known limitations (v0.1)
53
+ ### Notes
54
+ - 16 Skills, 10 subagents, 25 templates, kernel — first published npm release.
22
55
  - No local-edit detection: `hstack update` overwrites consumer hand-edits without warning; the diff preview is the only signal. Hash-manifest mode is planned for v0.2.
23
56
  - No CI for the CLI itself; coverage is manual smoke tests across happy / negative paths plus one real consumer (moso-app).
24
57
  - No migration scripts: template schema changes between versions need CHANGELOG-driven manual action.
25
-
26
- ## [0.1.0] - 2026-05-22
27
-
28
- Initial pre-release. Vendored / symlinked distribution only; npm CLI in progress.
29
-
30
- - 16 Skills, 10 subagents, 25 templates, kernel.
31
- - See repo history prior to this changelog for detail.
package/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.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.1.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.
@@ -77,7 +77,12 @@ For change-spec / module-spec / ADR / tech-debt, fill the YAML frontmatter and p
77
77
  - Use challenge prompts for sections where omission is the failure mode. Invariants on change-spec and module-spec require minimum three bullets, elicited via "Name three things that look like they could change but must not. If you can't name three, why is the change so narrow?"
78
78
  - For ADRs, walk Michael Nygard format strictly: Title, Status, Context, Decision, Consequences, Alternatives Considered. Use the challenge prompt on Consequences: "Name two consequences that look bad."
79
79
  - Reference, do not duplicate. When a change-spec needs to cite a persona, story, or ADR, write the id, not the prose.
80
- - Maintain reciprocity. When `tech-debt.origin` is a change-spec id, ensure that change-spec's `creates-tech-debt` array includes the new tech-debt id (TD-01). When writing `tech-debt.resolved-by`, ensure that change-spec's `resolves-tech-debt` array includes this tech-debt id (TD-04). Same for ADR `supersedes` / `superseded-by`. The reciprocal pair always lands in a single auto-commit; one-sided writes are not permitted.
80
+ - Maintain reciprocity. When `tech-debt.origin` is a change-spec id, ensure that change-spec's `creates-tech-debt` array includes the new tech-debt id (TD-01). When writing `tech-debt.resolved-by`, ensure that change-spec's `resolves-tech-debt` array includes this tech-debt id (TD-04). Same for ADR `supersedes` / `superseded-by`. For `change-spec.enables` (Category B foundational-prerequisite linkage), when writing or editing the `enables` array, also write the reciprocal `enabled-by` entry on each downstream change-spec named in the array (SP-14). Forward references — `enables` entries pointing at a not-yet-scaffolded id — are permitted; `/hstack:change-new` reconciles the reciprocal `enabled-by` when the downstream is later scaffolded. The reciprocal pair always lands in a single auto-commit; one-sided writes are not permitted.
81
+ - **The no-story interview branch.** When a change-spec's `user-stories` array would be empty, do not silently set `internal-tooling: true` (the old default). Ask the engineer: "This change has no linked user story. Which category applies?
82
+ - **(A) Internal tooling** — engineering-only code that never ships on a user path (scripts, dev dashboards, CI tooling). Sets `internal-tooling: true`.
83
+ - **(B) Foundational prerequisite** — production code that ships, but user value lives in a named downstream change-spec that consumes this one's output (schema before UI, plumbing before consumer). Sets `enables: [<downstream-id>, ...]` and writes the reciprocal `enabled-by` on each downstream spec.
84
+ - **(C) Neither** — there is actually a user story; let's draft it via `/hstack:story-draft`."
85
+ Categories A and B are mutually exclusive (SP-13). If the engineer is uncertain, walk the audit-query test: "After this ships, if someone asks 'what's the user value of this change?', is the honest answer (A) 'none, it's internal', (B) 'it teed up change-spec X', or (C) 'this user-facing thing'?"
81
86
  - **Mechanical operations are not your job.** Per the kernel's Mechanical operations section, status flips, reciprocal writes, Resolution Log appends, and `updated:` date bumps are performed by Skills directly in the main Claude Code session, not by this subagent. The four resolution Skills (`/hstack:tech-debt-resolve`, `/hstack:tech-debt-wontfix`, `/hstack:tech-debt-stale`, `/hstack:finalize`) own those writes themselves. If you are invoked for a mechanical operation, refuse and direct the engineer to run the appropriate Skill — the invocation is a workflow error, not a request to fulfil.
82
87
  - ADR ids are sequential. Read the highest existing `ADR-NNNN` and increment by one. No gaps, no reuse.
83
88
  - For module-spec, you may grep the In-Scope module's source to verify claims about exports, RPCs, and tables — but do not modify code.
@@ -96,7 +101,7 @@ Stop and ask the human when:
96
101
 
97
102
  A change-spec at terminal author-state (`status: ready-to-plan`) has:
98
103
 
99
- - All universal frontmatter (id, type, status, owner, created, updated, schema-version), all change-spec-specific fields (area, surfaces, user-stories, related-spec, in-scope, out-of-scope), and any conditional fields populated.
104
+ - All universal frontmatter (id, type, status, owner, created, updated, schema-version), all change-spec-specific fields (area, surfaces, user-stories, related-spec, in-scope, out-of-scope, internal-tooling, enables, enabled-by), and any conditional fields populated. Exactly one of {`user-stories` non-empty, `internal-tooling: true`, `enables` non-empty} must hold (SP-09); `internal-tooling: true` and `enables` non-empty must not both hold (SP-13).
100
105
  - All ten sections from the schema, with Invariants holding three or more bullets and Open Questions either resolved or explicitly punted.
101
106
  - A passing validator run.
102
107
 
@@ -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
@@ -60,15 +60,17 @@ Before any work:
60
60
 
61
61
  2. **Create the folder.** `mkdir -p hstack/specs/changes/<id>/` via Bash.
62
62
 
63
- 3. **Seed `spec.md`.** Read `hstack/templates/change-spec.md`, instantiate the frontmatter with `id`, `type: change-spec`, `status: draft`, `owner` (from `git config user.name` or `hstack/config.yaml`'s default owner), `area: <area>`, `related-spec: <area>`, `created` and `updated` set to today, `schema-version: 1`. Leave `surfaces`, `user-stories`, `in-scope`, `out-of-scope`, `related-adrs`, `creates-tech-debt`, `parent-change`, `threat-model-delta`, `internal-tooling`, `trivial` as their template defaults (typically empty arrays or `null`). Leave every prose section empty, with the template's interview-prompt comments intact for `spec-author` to consume.
63
+ 3. **Seed `spec.md`.** Read `hstack/templates/change-spec.md`, instantiate the frontmatter with `id`, `type: change-spec`, `status: draft`, `owner` (from `git config user.name` or `hstack/config.yaml`'s default owner), `area: <area>`, `related-spec: <area>`, `created` and `updated` set to today, `schema-version: 1`. Leave `surfaces`, `user-stories`, `in-scope`, `out-of-scope`, `related-adrs`, `creates-tech-debt`, `parent-change`, `threat-model-delta`, `internal-tooling`, `enables`, `enabled-by`, `trivial` as their template defaults (typically empty arrays or `null`). Leave every prose section empty, with the template's interview-prompt comments intact for `spec-author` to consume.
64
64
 
65
- 4. **Validate.** Run `{{TODO-SCRIPT: hstack/scripts/validate-spec.ts}}` against the seeded file. Validation at `status: draft` is permissiveempty arrays and empty prose are allowed at draftbut the universal floor (FM-01) must pass.
65
+ 4. **Forward-reference reconciliation for `enables` chains.** Grep every existing `hstack/specs/changes/*/spec.md` for the new `<id>` in `enables:` arrays. For each match: the matched (upstream) spec already declares this new (downstream) spec as a Category-B enabler. Per SP-14, write the reciprocal `enabled-by: [<upstream-id>, ...]` array on the newly-seeded spec in the same scaffold commit (this is the atomic-pair guarantee both halves land together). When no match exists, leave `enabled-by: []`. Read-only on the upstream spec its `enables` array was already written when the upstream was authored; no edit there. This is a mechanical operation per the kernel's Mechanical-operations section; no subagent is invoked. Surface the reconciliation to the engineer: "Detected upstream spec(s) declaring `enables: [<id>]` — populating `enabled-by` reciprocally."
66
66
 
67
- 5. **Offer branch creation.** Run `git branch --show-current`. If the current branch is `main` (or the configured default), ask the engineer: "You're on `<current-branch>`. Convention is one branch per change-spec — create `change/<id>` from here and check out before the scaffold commits? [Y/n/type-different-name]". Default Yes. On confirmation, run `git checkout -b change/<id>` BEFORE step 6 so the scaffold commit lands on the correct branch from the start. If the engineer declines or names a different branch, honor the choice and continue on the chosen branch. If the current branch is anything other than the configured default (i.e., already on a feature branch), do nothing — the engineer has a deliberate branching strategy and the Skill respects it.
67
+ 5. **Validate.** Run `{{TODO-SCRIPT: hstack/scripts/validate-spec.ts}}` against the seeded file. Validation at `status: draft` is permissive empty arrays and empty prose are allowed at draft but the universal floor (FM-01) must pass.
68
68
 
69
- 6. **Auto-commit.** Commit message: `chore(change-new): scaffold <id>`.
69
+ 6. **Offer branch creation.** Run `git branch --show-current`. If the current branch is `main` (or the configured default), ask the engineer: "You're on `<current-branch>`. Convention is one branch per change-spec — create `change/<id>` from here and check out before the scaffold commits? [Y/n/type-different-name]". Default Yes. On confirmation, run `git checkout -b change/<id>` BEFORE step 7 so the scaffold commit lands on the correct branch from the start. If the engineer declines or names a different branch, honor the choice and continue on the chosen branch. If the current branch is anything other than the configured default (i.e., already on a feature branch), do nothing — the engineer has a deliberate branching strategy and the Skill respects it.
70
70
 
71
- 7. **Surface next steps.** Print a one-line instruction directing the engineer to invoke `spec-author` (or `hstack-story-draft` first if the change is user-facing and no story exists yet).
71
+ 7. **Auto-commit.** Commit message: `chore(change-new): scaffold <id>`.
72
+
73
+ 8. **Surface next steps.** Print a one-line instruction directing the engineer to invoke `spec-author` (or `hstack-story-draft` first if the change is user-facing and no story exists yet). If the change has Category-B `enabled-by` populated from step 4, also note the upstream linkage so the engineer is reminded which prerequisite this realizes.
72
74
 
73
75
  The Skill does not invoke any subagent. Scaffolding is mechanical and the engineer's subsequent moves vary by change.
74
76
 
@@ -66,7 +66,12 @@ No subagents are invoked. Every step is a direct file read or shell call.
66
66
 
67
67
  1. **Section 1 — Where you are.**
68
68
  - Read `hstack/config.yaml` for `init-status` and the active MCP set.
69
- - Glob `hstack/specs/changes/*/spec.md`. For each, read frontmatter (`id`, `status`, `surfaces`, `owner`, `internal-tooling`, `trivial`, `parent-change`). Filter to non-terminal status (anything before `shipped`, `archived`).
69
+ - Glob `hstack/specs/changes/*/spec.md`. For each, read frontmatter (`id`, `status`, `surfaces`, `owner`, `internal-tooling`, `enables`, `enabled-by`, `trivial`, `parent-change`). Filter to non-terminal status (anything before `shipped`, `archived`).
70
+ - For each in-flight change, classify and surface the no-story carve-out when present:
71
+ - Category A (`internal-tooling: true`) → annotate "[Category A — internal tooling]".
72
+ - Category B (`enables` non-empty) → annotate "[Category B — enables → <comma-separated downstream ids>]". For each downstream id, also note its on-disk status (or "not yet scaffolded" — informational, not a blocker; reconciliation happens at downstream `/hstack:change-new` time).
73
+ - `enabled-by` non-empty → annotate "[Realizes ← <comma-separated upstream ids>]" so the reverse direction of the chain is visible.
74
+ - SP-13 violation (both `internal-tooling: true` AND `enables` non-empty) → flag explicitly as an error.
70
75
  - For each in-flight change, compute the **next blocking action**:
71
76
  - `status: draft` → "Author via `spec-author` directly (or run `/hstack:story-draft` first if user-facing)."
72
77
  - `status: ready-to-plan` and missing conditional artifacts → name them; suggest the appropriate Skill (`/hstack:test-plan` first if missing — it gates the planner, `/hstack:security-review`, `/hstack:data-review` if db, `/hstack:ui-brief` if ui, `/hstack:change-plan`).
@@ -47,7 +47,7 @@ tools:
47
47
 
48
48
  ## When to invoke
49
49
 
50
- Invoke once the change-spec is at `status: ready-for-implementation` (which means every upstream gate is terminal: test-plan at `passed` or `concerns-acknowledged`, plan at `ready`, security-review at `passed` or `concerns-acknowledged`, data-review at `passed` or `concerns-acknowledged` when applicable, ui-brief at `drafted` and figma-handoff at `ready` when applicable, user-stories non-empty unless internal-tooling). One invocation per phase. Re-invoke for each subsequent phase.
50
+ Invoke once the change-spec is at `status: ready-for-implementation` (which means every upstream gate is terminal: test-plan at `passed` or `concerns-acknowledged`, plan at `ready`, security-review at `passed` or `concerns-acknowledged`, data-review at `passed` or `concerns-acknowledged` when applicable, ui-brief at `drafted` and figma-handoff at `ready` when applicable, user-stories non-empty UNLESS `internal-tooling: true` UNLESS `enables` non-empty). One invocation per phase. Re-invoke for each subsequent phase.
51
51
 
52
52
  ## Inputs
53
53
 
@@ -64,7 +64,7 @@ Before any work — the Skill re-checks every gate even when the change-spec car
64
64
  - Security-review at `status: passed` or `concerns-acknowledged`.
65
65
  - Data-review at `status: passed` or `concerns-acknowledged` when `surfaces` includes `db`.
66
66
  - ui-brief at `status: drafted` and figma-handoff at `status: ready` when `surfaces` includes `ui`.
67
- - User-stories non-empty unless `internal-tooling: true`.
67
+ - User-stories non-empty UNLESS `internal-tooling: true` (Category A — engineering-only) UNLESS `enables` non-empty (Category B — foundational prerequisite; user value lives in the named downstream change-spec). SP-13: `internal-tooling: true` and `enables` non-empty are mutually exclusive; if both are set, halt with an SP-13 violation message and direct the engineer to `spec-author` to pick one.
68
68
  - The relevant module-spec at `status: current`.
69
69
  - **Branch state.** Run `git branch --show-current`. If the current branch is `main` (or the configured default) AND `change-spec.trivial` is not `true`, HARD HALT with: "Refusing to implement on `main` — change-spec `<id>` is not marked trivial. Check out `change/<id>` first, or run `/hstack:branch <id>` to create-and-switch." Trivial changes (`trivial: true`) may proceed on main per the kernel's trivial-changes carve-out. This check enforces the kernel's branch-hygiene contract at the last moment before code lands.
70
70
 
@@ -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.