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 +35 -9
- package/VERSION +1 -1
- package/dist/manifest.js +1 -0
- package/dist/manifest.js.map +1 -1
- package/package.json +1 -1
- package/template/.claude/agents/kernel-fit-analyst.md +137 -0
- package/template/.claude/agents/spec-author.md +7 -2
- package/template/.claude/skills/hstack-adr-new/SKILL.md +6 -1
- package/template/.claude/skills/hstack-change-new/SKILL.md +7 -5
- package/template/.claude/skills/hstack-help/SKILL.md +6 -1
- package/template/.claude/skills/hstack-implement/SKILL.md +2 -2
- package/template/.claude/skills/hstack-kernel-fit-promote/SKILL.md +164 -0
- package/template/.claude/skills/hstack-kernel-fit-scan/SKILL.md +180 -0
- package/template/.claude/skills/hstack-kernel-fit-triage/SKILL.md +159 -0
- package/template/.claude/skills/hstack-ship/SKILL.md +6 -5
- package/template/.claude/skills/hstack-story-draft/SKILL.md +7 -5
- package/template/.claude/skills/hstack-telemetry/SKILL.md +3 -2
- package/template/.claude/skills/hstack-ui-brief/SKILL.md +1 -1
- package/template/CLAUDE.md +26 -0
- package/template/scripts/telemetry/insights/kernel_fit.py +438 -0
- package/template/scripts/telemetry/render.py +73 -0
- package/template/scripts/telemetry/report.py +4 -1
- package/template/scripts/telemetry/run_kernel_fit.py +91 -0
- package/template/templates/adr.md +2 -1
- package/template/templates/change-spec.md +3 -1
- package/template/templates/kernel-fit-finding.md +62 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: hstack-kernel-fit-scan
|
|
3
|
+
description: |
|
|
4
|
+
Use this skill when the engineer wants to scan for kernel-fit gaps — patterns in shipped practice suggesting the kernel itself (CLAUDE.md, templates, validators, Skill flows) needs revision. The Skill runs the detection layer (`scripts/telemetry/run_kernel_fit.py`), invokes the `kernel-fit-analyst` subagent to synthesize one finding file per fired pattern, then nudges Slack on `high`/`medium` confidence findings with a 14-day dedup gate. Findings land at `hstack/kernel-fit/findings/KF-NNNN-<slug>.md`; the disk artifact is canonical, Slack is a side-channel pointer with graceful degradation when MCP is unwired. Promotion to a kernel change is human-gated via `/hstack:kernel-fit-promote`. See ADR-0004.
|
|
5
|
+
|
|
6
|
+
<example>
|
|
7
|
+
Context: The engineer wants a weekly kernel-fit scan after shipping several changes.
|
|
8
|
+
user: "/hstack:kernel-fit-scan"
|
|
9
|
+
assistant: "I'll run the detector against this repo's shipped changes / ADRs / tech-debt / commits / transcripts in the last 30 days. If any pattern fires, I'll invoke `kernel-fit-analyst` to synthesize findings (one file per pattern). Slack nudge on medium/high; dedup against open findings within 14 days. Disk-write is the load-bearing action — if Slack MCP is unreachable, the scan still completes and findings land on disk."
|
|
10
|
+
<commentary>
|
|
11
|
+
The Skill is opt-in by invocation. It is the only Skill that causes the `kernel-fit-analyst` subagent to run. Detection is post-hoc and derivative — no decision-time instrumentation; the no-parallel-tracker rule is preserved because the detector reads, never writes.
|
|
12
|
+
</commentary>
|
|
13
|
+
</example>
|
|
14
|
+
|
|
15
|
+
<example>
|
|
16
|
+
Context: The detector returns no fired patterns — the kernel is in good shape against the current corpus.
|
|
17
|
+
user: "/hstack:kernel-fit-scan"
|
|
18
|
+
assistant: "Detection ran; no patterns fired. No findings written, no Slack notification, no commit. The kernel-fit-analyst subagent was not invoked (no work to do). Exit clean."
|
|
19
|
+
<commentary>
|
|
20
|
+
No-fire is the most common output on a healthy repo, especially in the first 4–8 weeks of operation. The Skill must not invoke the subagent or write anything when no pattern fires — every Slack message has a cost in attention, and the dedup gate's purpose is undermined if scans run noise-free but the engineer still sees messages.
|
|
21
|
+
</commentary>
|
|
22
|
+
</example>
|
|
23
|
+
|
|
24
|
+
<example>
|
|
25
|
+
Context: KF-P1 fires but an open finding for the same pattern already exists from a scan 5 days ago.
|
|
26
|
+
user: "/hstack:kernel-fit-scan"
|
|
27
|
+
assistant: "KF-P1 fired with 3 evidence rows. An open finding (KF-0003) already exists for KF-P1 from 5 days ago — within the 14-day dedup window. I'll invoke the analyst; if the new evidence is materially equivalent the analyst will skip writing and surface the dedup; if it is materially stronger, the analyst writes a new KF-NNNN and supersedes KF-0003. Either way no Slack message fires today — the engineer was already notified within the window."
|
|
28
|
+
<commentary>
|
|
29
|
+
Dedup happens at two layers: (a) the analyst decides whether to write a new finding or skip (per its supersession carve-out); (b) the Skill decides whether to fire Slack. The 14-day window is the v1 default — tunable when calibration data exists.
|
|
30
|
+
</commentary>
|
|
31
|
+
</example>
|
|
32
|
+
|
|
33
|
+
tools:
|
|
34
|
+
- Read
|
|
35
|
+
- Write
|
|
36
|
+
- Edit
|
|
37
|
+
- Grep
|
|
38
|
+
- Glob
|
|
39
|
+
- Bash
|
|
40
|
+
- Task
|
|
41
|
+
- "{{TODO-MCP: Slack — mcp__claude_ai_Slack__slack_send_message — required for kernel-fit notifications. Without it, findings still land on disk; the Skill logs to stderr and exits 0 (deliberate carve-out from the kernel's general MCP-unreachable stop condition — Slack is a side-channel pointer, not authoritative state).}}"
|
|
42
|
+
- "{{TODO-SCRIPT: hstack/scripts/validate-spec.ts — validates kernel-fit-finding frontmatter and KF-01 through KF-05}}"
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Purpose
|
|
46
|
+
|
|
47
|
+
`hstack-kernel-fit-scan` is the entry point to the kernel-fit closed-loop system. The Skill orchestrates four steps: detection (Python script, pure read), synthesis (`kernel-fit-analyst` subagent, one file per fired pattern), notification (Slack, best-effort), and the atomic commit. Promotion to a kernel change is a separate, human-invoked Skill (`/hstack:kernel-fit-promote`).
|
|
48
|
+
|
|
49
|
+
The Skill is the only path by which the `kernel-fit-analyst` subagent runs. The kernel's "AI writes, humans confirm" contract is preserved at the kernel-modification layer because the analyst writes findings only — never ADRs, never change-specs, never kernel edits.
|
|
50
|
+
|
|
51
|
+
## When to invoke
|
|
52
|
+
|
|
53
|
+
Common cadence:
|
|
54
|
+
|
|
55
|
+
- Weekly for active engineering — catches kernel-vs-practice drift early.
|
|
56
|
+
- After every ~5 shipped changes — surfaces patterns single-change inspection cannot.
|
|
57
|
+
- After a kernel edit lands — confirm the edit moved the relevant pattern off the fired list (or did not introduce a new one).
|
|
58
|
+
- Whenever a `kernel-fit-analyst` finding is restated or superseded by triage, to let the analyst write the supersession edit atomically.
|
|
59
|
+
|
|
60
|
+
The Skill is opt-in. There is no cron, no automatic invocation, no event-driven trigger in v1.
|
|
61
|
+
|
|
62
|
+
## Inputs
|
|
63
|
+
|
|
64
|
+
- `--window <N>` (optional): limit detector history to the last N days. Default 30. `--window 0` means all-history.
|
|
65
|
+
- `--no-slack` (optional): skip the Slack notification step even if MCP is wired. Useful for dry-runs or for replaying a scan when the engineer has already been notified manually.
|
|
66
|
+
|
|
67
|
+
## Preconditions
|
|
68
|
+
|
|
69
|
+
- `hstack/config.yaml` exists at `init-status: complete`. If init is incomplete, halt with `HSTACK-HALT: reason=missing-context`.
|
|
70
|
+
- The wrapper script `hstack/scripts/telemetry/run_kernel_fit.py` exists. If missing, halt with `HSTACK-HALT: reason=missing-context` and the recommendation "re-vendor hstack or run `/hstack:configure`".
|
|
71
|
+
- The repo contains at least 3 shipped change-specs (count by `status: shipped` across `hstack/specs/changes/*/spec.md`). Below this threshold, the corpus is too small for cross-correlation; the Skill halts with a one-line note: "kernel-fit scan needs ≥3 shipped changes; corpus is too small to produce honest evidence."
|
|
72
|
+
- Python 3.10+ is available on PATH (matches `hstack-telemetry`).
|
|
73
|
+
- The git repository is intact (`.git/` present).
|
|
74
|
+
|
|
75
|
+
## Orchestration steps
|
|
76
|
+
|
|
77
|
+
1. **Verify preconditions.** Walk the checks above; halt with the named reason if any fail. Print "kernel-fit-scan: preconditions OK, N shipped changes in corpus, M-day window" to stderr.
|
|
78
|
+
|
|
79
|
+
2. **Run detection.** Shell out: `python3 hstack/scripts/telemetry/run_kernel_fit.py --repo <repo-root> --window <N>`. Capture stdout as JSON; capture stderr as the diagnostic log. On non-zero exit, surface the traceback and halt with `HSTACK-HALT: reason=missing-context`.
|
|
80
|
+
|
|
81
|
+
3. **Decide whether to invoke the subagent.** Parse the JSON. If every pattern has `fired: false`, exit clean: print "kernel-fit-scan: no patterns fired in window — kernel is consistent with shipped practice in current corpus" to stdout, write nothing, do not invoke the subagent, do not nudge Slack, do not commit. This is the most common outcome on a healthy repo.
|
|
82
|
+
|
|
83
|
+
4. **Invoke `kernel-fit-analyst`.** When at least one pattern fired, invoke via Task tool with `subagent_type: kernel-fit-analyst`. The prompt includes (a) the JSON evidence blob verbatim, (b) the canonical reminder of session-isolation (no implementer transcripts loaded), (c) the explicit instruction "one file per fired pattern; mandatory two-bullet counter-explanation; never write outside `hstack/kernel-fit/findings/`". The subagent's writes land in the working tree before the subagent returns.
|
|
84
|
+
|
|
85
|
+
5. **Stage and commit findings.** Compute the set of newly-written or modified finding files (analyst may have superseded a prior finding atomically with a new one). Print the proposed diff for engineer confirmation (per the kernel's "AI writes, humans confirm" mechanical-operations adaptation). On Y/n confirmation `Y` (default Yes), `git add` the finding files and commit with message `kernel-fit: <N> finding(s) detected` (or `kernel-fit: <N> finding(s) detected, <M> superseded` when supersession edits also landed). One commit per scan run, atomic across all new/edited findings.
|
|
86
|
+
|
|
87
|
+
6. **Notification — Slack nudge (best-effort).** Compute the notification set:
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
notify = [f for f in newly_written_findings
|
|
91
|
+
if f.confidence in ("high", "medium")
|
|
92
|
+
and not _open_finding_exists_for_pattern_in_window(f.pattern, days=14)]
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
For each finding in `notify`, send a Slack message via `mcp__claude_ai_Slack__slack_send_message` with the canonical body:
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
hstack kernel-fit: <N> new finding(s)
|
|
99
|
+
|
|
100
|
+
• KF-NNNN — <title> [confidence: high|medium]
|
|
101
|
+
Pattern: <KF-P1|KF-P2|KF-P3>
|
|
102
|
+
Kernel surface: <one-line>
|
|
103
|
+
hstack/kernel-fit/findings/KF-NNNN-<slug>.md
|
|
104
|
+
|
|
105
|
+
Triage: /hstack:kernel-fit-triage KF-NNNN --action acknowledge
|
|
106
|
+
Dismiss: /hstack:kernel-fit-triage KF-NNNN --action dismiss --reason "..."
|
|
107
|
+
Promote: /hstack:kernel-fit-promote KF-NNNN --slug <adr-slug>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Bundle multiple findings into a single message when more than one fires in this scan.
|
|
111
|
+
|
|
112
|
+
7. **Graceful degradation when Slack is unreachable.** If the MCP call raises (tool not configured, network failure, channel-not-found, etc.), log to stderr: `kernel-fit: Slack MCP unreachable; <N> finding(s) written to disk without notification. Triage via /hstack:help to discover open findings.` Exit 0. The disk write from step 5 is the load-bearing action; Slack is a side-channel pointer. This is a deliberate carve-out from the kernel's general MCP-unreachable stop condition — Slack is not load-bearing for kernel-fit (the canonical state lives on disk and is reachable via `/hstack:help`). The carve-out is documented here and in the kernel's `## How hstack improves itself` section.
|
|
113
|
+
|
|
114
|
+
8. **Report.** Print to stdout: the count of patterns fired, the count of new findings written, the count of supersessions, the count of Slack notifications fired (or "skipped — Slack unreachable" / "skipped — within dedup window for all findings"). Done.
|
|
115
|
+
|
|
116
|
+
## Outputs
|
|
117
|
+
|
|
118
|
+
- Zero or more files at `hstack/kernel-fit/findings/KF-NNNN-<slug>.md` at `status: open`.
|
|
119
|
+
- Zero or more supersession edits (status flip + `superseded-by` set) on prior finding files.
|
|
120
|
+
- Zero or one git commits.
|
|
121
|
+
- Zero or one Slack messages (bundled when multiple findings notify).
|
|
122
|
+
|
|
123
|
+
## Auto-commit triggers
|
|
124
|
+
|
|
125
|
+
- One commit at the writing of finding files (per the kernel's auto-commit-at-status-transition rule applied at the artifact-creation moment). No commit when no patterns fire.
|
|
126
|
+
|
|
127
|
+
## Idempotency contract
|
|
128
|
+
|
|
129
|
+
- Re-running the Skill when no patterns fire: zero new disk artifacts, no commit, no Slack message. Pure no-op.
|
|
130
|
+
- Re-running when patterns fire that already have open findings within the dedup window: the analyst is invoked, sees existing findings, and may skip-write or supersede; Slack notification is suppressed by the dedup gate.
|
|
131
|
+
- Re-running when the same patterns fire with new evidence: the analyst may produce supersession edits; the dedup gate still suppresses Slack (already-notified within the window).
|
|
132
|
+
- Re-running with `--no-slack`: identical to a run with Slack unreachable — findings land on disk, no Slack message.
|
|
133
|
+
|
|
134
|
+
## Stop conditions
|
|
135
|
+
|
|
136
|
+
Beyond the kernel's general stop conditions:
|
|
137
|
+
|
|
138
|
+
- Init is incomplete or `run_kernel_fit.py` is missing (`HSTACK-HALT: reason=missing-context`).
|
|
139
|
+
- Corpus is below the 3-shipped-changes floor — halt with the one-line note above.
|
|
140
|
+
- Detector script exits non-zero or produces malformed JSON — halt with the traceback and `HSTACK-HALT: reason=missing-context`.
|
|
141
|
+
- The `kernel-fit-analyst` subagent halts mid-run — propagate the halt sentinel; do not commit partial output (the analyst's writes-so-far remain unstaged on disk; the engineer can either delete them or re-invoke the Skill which will see them on next run).
|
|
142
|
+
- The proposed-diff confirmation at step 5 is declined — abort the commit; the analyst's writes-so-far remain in the working tree. Re-invoke to continue.
|
|
143
|
+
|
|
144
|
+
Slack-MCP-unreachable is NOT a stop condition. See step 7.
|
|
145
|
+
|
|
146
|
+
## Failure modes
|
|
147
|
+
|
|
148
|
+
- **Detector raises on malformed frontmatter in a shipped change-spec.** The detector is read-only and tolerant of missing fields; the parser falls back to a degraded YAML reader. If a corrupted frontmatter file genuinely crashes the parser, the traceback names the offending file — fix it and re-run.
|
|
149
|
+
- **Subagent produces a finding without two counter-explanations.** The analyst auto-downgrades `confidence: low` per KF-03; the finding still lands on disk but does not nudge Slack. No remediation needed — this is the designed behavior.
|
|
150
|
+
- **Subagent writes a finding outside `hstack/kernel-fit/findings/`.** Scope-lock violation. The subagent's prompt forbids this at every Write call. If observed, surface as a kernel-fit-analyst contract violation and do not commit; report the offending path for diagnosis.
|
|
151
|
+
- **Slack message rejected by the MCP server (rate limit, channel-not-found, auth error).** Same as MCP unreachable — log to stderr, exit 0, the engineer triages via `/hstack:help` on next session.
|
|
152
|
+
|
|
153
|
+
## Configuring Slack notifications (consumer-side)
|
|
154
|
+
|
|
155
|
+
Slack notifications are opt-in per consumer. Without wiring, the Skill still works — findings land on disk and the engineer discovers them via `/hstack:help`. To enable Slack nudges on `medium`/`high` confidence findings:
|
|
156
|
+
|
|
157
|
+
1. **Wire the MCP server.** Add the Slack MCP to your Claude Code MCP configuration so `mcp__claude_ai_Slack__slack_send_message` is callable from the session that runs `/hstack:kernel-fit-scan`. Follow Anthropic's Slack MCP install docs; the auth scope `chat:write` is required.
|
|
158
|
+
|
|
159
|
+
2. **Configure the destination channel.** Add a `kernel-fit` block to `hstack/config.yaml`:
|
|
160
|
+
|
|
161
|
+
```yaml
|
|
162
|
+
kernel-fit:
|
|
163
|
+
slack-channel: "#hstack-kernel-fit" # public channel id or name; the bot must be invited
|
|
164
|
+
slack-fallback: "dm" # "dm" | "off" — behavior when slack-channel is absent or unreachable
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
`slack-channel` is optional. When absent and `slack-fallback: "dm"`, the Skill sends to the invoking engineer's DM via the bot. When `slack-fallback: "off"`, missing channel behaves identically to unreachable MCP (log to stderr, exit 0).
|
|
168
|
+
|
|
169
|
+
3. **Verify with a dry-run.** Run `/hstack:kernel-fit-scan --no-slack` first to confirm the detection layer produces output on your corpus, then re-run without the flag once Slack is wired. The first non-`--no-slack` run will surface any auth or channel issues as the documented graceful-degradation log line.
|
|
170
|
+
|
|
171
|
+
What you do NOT need to do: no code to write, no hook to install. The Skill is prose-driven; the runtime LLM agent invokes the MCP when the tool is available in the session and the config names a destination. The `{{TODO-MCP}}` placeholder in the tools array is the framework convention naming the contract — the consumer's MCP wiring satisfies it.
|
|
172
|
+
|
|
173
|
+
## Anti-patterns
|
|
174
|
+
|
|
175
|
+
- Never invoke the `kernel-fit-analyst` subagent when no patterns fired. Empty invocations waste tokens and produce nothing.
|
|
176
|
+
- Never auto-promote a finding to an ADR. Promotion is a separate, human-invoked Skill (`/hstack:kernel-fit-promote`). The contract is non-negotiable per ADR-0004.
|
|
177
|
+
- Never silently retry Slack on transient failure. The disk artifact is canonical; the engineer's `/hstack:help` covers the missed-notification case.
|
|
178
|
+
- Never write outside `hstack/kernel-fit/findings/` or modify any artifact not produced by the analyst this run. This Skill orchestrates; it does not author.
|
|
179
|
+
- Never claim the analyst's output is measured truth. Frame every finding as LLM-strategized judgment per the kernel's v1 / v2 split rule.
|
|
180
|
+
- Never bundle a Slack notification across scan runs. One scan, one message (or zero, when the dedup gate suppresses or Slack is unreachable).
|
|
@@ -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.
|
|
@@ -47,7 +47,7 @@ Invoke when every per-change artifact is at terminal status and the engineer is
|
|
|
47
47
|
Before any work:
|
|
48
48
|
|
|
49
49
|
- Verify the change folder `hstack/specs/changes/<change-id>/` exists.
|
|
50
|
-
- Verify the change-spec exists. Read its `surfaces`, `internal-tooling`, `trivial` flags to know which conditional gates apply.
|
|
50
|
+
- Verify the change-spec exists. Read its `surfaces`, `internal-tooling`, `enables`, `enabled-by`, `trivial` flags to know which conditional gates apply.
|
|
51
51
|
|
|
52
52
|
The Skill does not pre-halt on artifact non-terminal status — that is what the scorecard reports. It halts only on missing artifacts or unreadable frontmatter.
|
|
53
53
|
|
|
@@ -55,7 +55,7 @@ The Skill does not pre-halt on artifact non-terminal status — that is what the
|
|
|
55
55
|
|
|
56
56
|
1. **Read every change artifact.** Read `spec.md`, `plan.md`, `test-plan.md`, `security-review.md`, `data-review.md` (when surfaces includes db), `ui-brief.md` and `figma-handoff.md` (when surfaces includes ui), `verification.md`, `adversarial-review.md`. Capture each artifact's `status` and key gating fields.
|
|
57
57
|
|
|
58
|
-
2. **Compute the
|
|
58
|
+
2. **Compute the twelve-gate scorecard.** Run `{{TODO-SCRIPT: hstack/scripts/compute-merge-readiness.ts}}` against the artifact set, or inline the equivalent logic:
|
|
59
59
|
- GT-01: spec presence — change folder exists with non-draft change-spec, or PR carries `trivial: true`.
|
|
60
60
|
- GT-02: diff within scope — every file in the PR diff (against the merge target) is a subset of `change-spec.in-scope`.
|
|
61
61
|
- GT-03: pattern lints — every `hstack/lints/*.yaml` rule passes (the Skill runs `{{TODO-SCRIPT: hstack/scripts/run-gates.sh}}` for this and reads the exit code).
|
|
@@ -63,10 +63,11 @@ The Skill does not pre-halt on artifact non-terminal status — that is what the
|
|
|
63
63
|
- GT-05: security-review at `passed` or `concerns-acknowledged`.
|
|
64
64
|
- GT-06: data-review at `passed` or `concerns-acknowledged` (when applicable).
|
|
65
65
|
- GT-07: ui-brief at `drafted` and figma-handoff at `ready` (when applicable).
|
|
66
|
-
- GT-08: `user-stories` non-empty
|
|
66
|
+
- GT-08: `user-stories` non-empty UNLESS `internal-tooling: true` (Category A) UNLESS `enables` non-empty (Category B). The audit-chain assumption: a Category-B spec's user value lives in one of the change-specs named in `enables`; this gate does not transitively verify that downstream spec has `user-stories` non-empty — that's the downstream's GT-08 check, run at its own ship time.
|
|
67
67
|
- GT-09: every cross-reference rule (CG-01..CG-04) passes.
|
|
68
68
|
- GT-10: test-plan at `passed` or `concerns-acknowledged`, and `verification.test-plan-coverage` shows no missing tenant-isolation tests and no out-of-budget performance assertions.
|
|
69
69
|
- GT-11: When `change-spec.resolves-tech-debt` is non-empty: (a) every referenced tech-debt must exist and be at `status: in-progress` with `resolution-attempted-at` set; (b) the adversarial-review must contain the AR-07 Acceptance-satisfied confirmation enumerating each TD's Acceptance bullets against the diff; (c) no referenced tech-debt may have a non-null `resolved-by` already (that would indicate a double-resolution attempt). When `resolves-tech-debt` is empty, GT-11 is `not-applicable`.
|
|
70
|
+
- GT-12 (SP-13 mutual exclusion): `internal-tooling: true` AND `enables` non-empty is forbidden. Hard FAIL. Reciprocity (SP-14): for every id in `enables`, the named downstream spec must exist on disk and must list this change-id in its `enabled-by` array. Missing downstream specs are a FAIL (forward references are only legal at authoring time — by ship time, the downstream must be scaffolded so reciprocity holds). The reverse direction (`enabled-by` entries that point at non-existent or non-listing upstream specs) is also FAIL.
|
|
70
71
|
|
|
71
72
|
3. **Frontmatter validation.** Run `{{TODO-SCRIPT: hstack/scripts/validate-spec.ts}}` across every artifact. Any FM-* or per-type validation failure blocks ship.
|
|
72
73
|
|
|
@@ -78,7 +79,7 @@ The Skill does not pre-halt on artifact non-terminal status — that is what the
|
|
|
78
79
|
- Tech-debt created: pointers from `change-spec.creates-tech-debt`.
|
|
79
80
|
- Test plan: pulled from `test-plan.md` — pyramid summary, tenant-isolation tests, and performance budgets — cross-referenced with `verification.test-plan-coverage` to show observed-vs-promised.
|
|
80
81
|
- Tech-debt resolved: pointers from `change-spec.resolves-tech-debt` with each TD's Title and Acceptance summary. When non-empty, the body explicitly notes that `/hstack:finalize <change-id>` must be run post-merge to flip each TD to `resolved`.
|
|
81
|
-
- Scorecard summary: the
|
|
82
|
+
- Scorecard summary: the twelve-gate table from step 2.
|
|
82
83
|
|
|
83
84
|
The pr-body.md is for the engineer to copy into the actual PR description — the Skill does not call `gh pr create` or otherwise open the PR.
|
|
84
85
|
|
|
@@ -122,7 +123,7 @@ Beyond the kernel's general stop conditions:
|
|
|
122
123
|
- Never write status transitions on any artifact from this Skill. Ship is read-only across the artifact set.
|
|
123
124
|
- Never call `gh pr create` or perform the merge. The engineer opens the PR.
|
|
124
125
|
- Never silently pass a gate. Every FAIL names the artifact and field.
|
|
125
|
-
- Never collapse the
|
|
126
|
+
- Never collapse the twelve gates into a single PASS / FAIL. The scorecard is per-gate.
|
|
126
127
|
- Never flip a tech-debt status from this Skill. That is `/hstack:finalize`'s job and only runs post-merge. Ship surfaces the directive; it does not perform the write.
|
|
127
128
|
- Never extend `change-spec.in-scope` to make GT-02 pass — the scope amendment goes through `spec-author`, not this Skill.
|
|
128
129
|
- Never overwrite `pr-body.md` content the engineer has hand-edited without confirmation. If the file exists with edits beyond the template, surface a diff and ask before rewriting.
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: hstack-story-draft
|
|
3
3
|
description: |
|
|
4
|
-
Use this skill when a user-facing change needs a story drafted or refined, anchored on an existing persona, with a concrete success metric and the user-visible edge cases enumerated. The Skill orchestrates the `product-manager` subagent and is conditional — it does not run when the parent change-spec
|
|
4
|
+
Use this skill when a user-facing change needs a story drafted or refined, anchored on an existing persona, with a concrete success metric and the user-visible edge cases enumerated. The Skill orchestrates the `product-manager` subagent and is conditional — it does not run when the parent change-spec is Category A (`internal-tooling: true`) or Category B (`enables` non-empty), since both carve-outs satisfy SP-09 without a story. Examples:
|
|
5
5
|
|
|
6
6
|
<example>
|
|
7
7
|
Context: The engineer just scaffolded a billing-overage change-spec and needs a linked user story before SP-09 lets the spec advance past draft.
|
|
8
8
|
user: "Draft a story for the billing overage warning, anchored on the growth-marketer persona."
|
|
9
9
|
assistant: "I'll invoke product-manager to walk the five story sections with the growth-marketer persona as the anchor. The story id will land in the configured story store; the change-spec's user-stories array updates reciprocally."
|
|
10
10
|
<commentary>
|
|
11
|
-
Stories are gated by SP-09 (`user-stories` non-empty
|
|
11
|
+
Stories are gated by SP-09 (`user-stories` non-empty UNLESS `internal-tooling: true` UNLESS `enables` non-empty). The Skill produces the story before the change-spec can advance, and writes the reciprocal `linked-change-specs` entry on the story.
|
|
12
12
|
</commentary>
|
|
13
13
|
</example>
|
|
14
14
|
|
|
@@ -36,7 +36,7 @@ tools:
|
|
|
36
36
|
|
|
37
37
|
## Purpose
|
|
38
38
|
|
|
39
|
-
`hstack-story-draft` produces or refines one user story by orchestrating the `product-manager` subagent. It maintains the reciprocal `user-stories` ↔ `linked-change-specs` linkage between the story and its parent change-spec. It is conditional — skipped automatically for changes marked `internal-tooling: true
|
|
39
|
+
`hstack-story-draft` produces or refines one user story by orchestrating the `product-manager` subagent. It maintains the reciprocal `user-stories` ↔ `linked-change-specs` linkage between the story and its parent change-spec. It is conditional — skipped automatically for changes marked Category A (`internal-tooling: true`) or Category B (`enables` non-empty). For Category B, the user value lives in the downstream change-spec(s) named in `enables`; the story (if any) is drafted against that downstream spec, not this one.
|
|
40
40
|
|
|
41
41
|
## When to invoke
|
|
42
42
|
|
|
@@ -56,7 +56,9 @@ Before any work:
|
|
|
56
56
|
- Verify the parent change-spec exists when `--change` is provided or when context implies it.
|
|
57
57
|
- Verify `hstack/context/personas/` (or the configured personas store) contains at least one persona at `status: current`.
|
|
58
58
|
- Verify the configured story store's MCP is reachable when the store is Notion / Linear / GitHub. If unreachable, halt — the kernel forbids silent fallback to a different store.
|
|
59
|
-
- If the parent change-spec carries `internal-tooling: true`, halt and surface
|
|
59
|
+
- If the parent change-spec carries `internal-tooling: true`, halt and surface: "Story not required — change is Category A (internal tooling, never on a user path)."
|
|
60
|
+
- If the parent change-spec carries `enables` non-empty, halt and surface: "Story not required — change is Category B (foundational prerequisite; user value lives in <enables-ids>). Draft a story against the downstream spec instead."
|
|
61
|
+
- If the parent change-spec carries BOTH `internal-tooling: true` AND `enables` non-empty, halt with SP-13 violation: "Categories A and B are mutually exclusive. Pick one via `spec-author`."
|
|
60
62
|
- Read `hstack/context/vision.md`, `mvp-scope.md`, and the personas index (required by `product-manager`'s session-start protocol).
|
|
61
63
|
|
|
62
64
|
## Orchestration steps
|
|
@@ -104,7 +106,7 @@ Beyond the kernel's general stop conditions:
|
|
|
104
106
|
|
|
105
107
|
## Failure modes
|
|
106
108
|
|
|
107
|
-
- **Parent change-spec is `internal-tooling: true
|
|
109
|
+
- **Parent change-spec is `internal-tooling: true` (Category A) or `enables` non-empty (Category B).** Halt early; the story is unnecessary. For Category B, redirect the engineer to draft a story against the downstream change-spec named in `enables`.
|
|
108
110
|
- **Reciprocal write to the parent change-spec would advance its status.** It should not — the reciprocal write only touches the `user-stories` array. If the validator detects a status change, halt and ask.
|
|
109
111
|
- **Validator fails.** Halt and surface; the engineer rewords the failing field.
|
|
110
112
|
|
|
@@ -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
|
|
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
|
|
|
@@ -52,7 +52,7 @@ Before any work:
|
|
|
52
52
|
- Verify `surfaces` includes `ui`. If not, halt with the surface-conditional message.
|
|
53
53
|
- Verify the configured design-system resources are reachable per their declared source in `hstack/config.yaml` (`design-system.components.source`, `tokens.source`, `brand-guidelines.source`). For each resource the brief will need: `in-repo` paths must resolve; `figma-mcp` / `notion-mcp` sources require the corresponding MCP to be wired and reachable (UI-surface changes are high-stakes — graceful degradation is not safe here); `submodule` / `npm` / `external-other` must fetch. A resource at `source: none` is treated as "not yet captured" — halt if the brief genuinely needs it, otherwise proceed against the resources that ARE configured.
|
|
54
54
|
- Verify the `design-system-version` declared in `hstack/config.yaml` is current and is what the brief will reference. Halt on drift.
|
|
55
|
-
- Read the change-spec's `user-stories` array; verify each story is reachable in the configured store and read it. Halt if any linked story or its persona is missing.
|
|
55
|
+
- Read the change-spec's `user-stories` array; verify each story is reachable in the configured store and read it. Halt if any linked story or its persona is missing. An empty `user-stories` is acceptable when the change is Category A (`internal-tooling: true`) — e.g., an internal dev dashboard — or Category B (`enables` non-empty) — e.g., a design-system primitive whose user-facing consumer is the downstream change. In the Category-B case, surface the upstream/downstream context: the brief still describes layout/copy for THIS change's UI, but the user-value story lives in the downstream spec named in `enables`.
|
|
56
56
|
|
|
57
57
|
## Orchestration steps
|
|
58
58
|
|
package/template/CLAUDE.md
CHANGED
|
@@ -139,6 +139,13 @@ Naming rules: `id` is kebab-case and immutable once written; dates are ISO 8601;
|
|
|
139
139
|
|
|
140
140
|
**Change-spec carries an optional `revisits-change` array.** When a new change-spec is filed to fix a defect, regression, or missed adversarial-review finding from a prior shipped change, the engineer populates `revisits-change: [<predecessor-change-id>]` so post-merge defect correlation is computable (`/hstack:telemetry` § QO-6 when promoted from watch-list to dashboard). Default empty. The field is informational, not gating — no Skill refuses to advance because the array is empty or non-empty.
|
|
141
141
|
|
|
142
|
+
**Change-spec carries `internal-tooling` (Category A) and `enables` (Category B) as the two no-story carve-outs.** A change-spec with no driving user story must declare one of two categories before status advances past `draft` (SP-09):
|
|
143
|
+
|
|
144
|
+
- **Category A — `internal-tooling: true`.** Engineering-only code that never ships on a user path: CI tooling, dev scripts, repo automation, internal dashboards. No `enables` linkage exists because no downstream user-facing change is teed up.
|
|
145
|
+
- **Category B — `enables: [<downstream-change-spec-id>, ...]`.** Production code that ships, but user value is realized by a named downstream change-spec that consumes this one's output. Typical case: schema or plumbing landed ahead of the UI that surfaces it. The reciprocal field `enabled-by: []` on the downstream spec is written atomically with `enables`.
|
|
146
|
+
|
|
147
|
+
The two flags are mutually exclusive (SP-13): a change is Category A *or* Category B, never both. If neither applies, `user-stories` must be non-empty. The audit query *"what's the user value of this change?"* follows the `enables` chain until it hits a spec with `user-stories` non-empty (the user-value realization point) or a dead end. Forward references are permitted at authoring time — if `enables` names a not-yet-scaffolded id, `/hstack:change-new` reconciles the reciprocal `enabled-by` when the downstream spec is later scaffolded. Reciprocity (`change-spec.enables ↔ change-spec.enabled-by`) is enforced by SP-14 and lands in a single atomic commit, matching the kernel's other reciprocal-pair rules.
|
|
148
|
+
|
|
142
149
|
---
|
|
143
150
|
|
|
144
151
|
## Status lifecycle
|
|
@@ -314,6 +321,7 @@ Load-at-session-start rules by subagent:
|
|
|
314
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).
|
|
315
322
|
- `verifier`: change-spec, plan, test-plan, ci-cd.
|
|
316
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.
|
|
317
325
|
- `researcher`: query context plus relevant product-context docs as the query requires.
|
|
318
326
|
|
|
319
327
|
A subagent that cannot reach a required context document halts and asks the human, rather than proceeding without it.
|
|
@@ -383,6 +391,24 @@ When this kernel is loaded in a session that is adding or removing a Skill or su
|
|
|
383
391
|
|
|
384
392
|
---
|
|
385
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
|
+
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 rationale; see `template/templates/kernel-fit-finding.md` for the artifact schema.
|
|
409
|
+
|
|
410
|
+
---
|
|
411
|
+
|
|
386
412
|
## References
|
|
387
413
|
|
|
388
414
|
- Architecture document (long-form companion): https://www.notion.so/360d6791656c813d955af822cb8814d1
|