hstack 0.7.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/CHANGELOG.md +277 -0
  2. package/README.md +39 -13
  3. package/VERSION +1 -1
  4. package/dist/commands/doctor.js +51 -1
  5. package/dist/commands/doctor.js.map +1 -1
  6. package/dist/commands/update.js +8 -2
  7. package/dist/commands/update.js.map +1 -1
  8. package/dist/lib/descriptions.js +167 -0
  9. package/dist/lib/descriptions.js.map +1 -0
  10. package/dist/lib/diff.js +1 -1
  11. package/dist/lib/git.js +16 -0
  12. package/dist/lib/git.js.map +1 -1
  13. package/dist/lib/wire.js +108 -4
  14. package/dist/lib/wire.js.map +1 -1
  15. package/dist/manifest.js +17 -2
  16. package/dist/manifest.js.map +1 -1
  17. package/package.json +3 -1
  18. package/template/.claude/agents/adversarial-reviewer.md +16 -64
  19. package/template/.claude/agents/app-architect.md +12 -49
  20. package/template/.claude/agents/data-architect.md +13 -51
  21. package/template/.claude/agents/data-specialist.md +5 -50
  22. package/template/.claude/agents/implementer.md +8 -65
  23. package/template/.claude/agents/kernel-fit-analyst.md +7 -68
  24. package/template/.claude/agents/planner.md +7 -42
  25. package/template/.claude/agents/product-discovery.md +12 -48
  26. package/template/.claude/agents/product-manager.md +8 -43
  27. package/template/.claude/agents/researcher.md +5 -41
  28. package/template/.claude/agents/security-reviewer.md +19 -54
  29. package/template/.claude/agents/spec-author.md +18 -52
  30. package/template/.claude/agents/stack-architect.md +14 -43
  31. package/template/.claude/agents/test-strategist.md +16 -57
  32. package/template/.claude/agents/ui-ux-briefer.md +6 -36
  33. package/template/.claude/agents/verifier.md +13 -45
  34. package/template/.claude/skills/hstack-adr-new/SKILL.md +6 -33
  35. package/template/.claude/skills/hstack-adversarial-review/SKILL.md +31 -52
  36. package/template/.claude/skills/hstack-adversarial-review/references/finding-categories.md +157 -0
  37. package/template/.claude/skills/hstack-app-architecture/SKILL.md +2 -29
  38. package/template/.claude/skills/hstack-branch/SKILL.md +4 -31
  39. package/template/.claude/skills/hstack-brownfield-init/SKILL.md +10 -37
  40. package/template/.claude/skills/hstack-change-new/SKILL.md +4 -31
  41. package/template/.claude/skills/hstack-change-plan/SKILL.md +21 -32
  42. package/template/.claude/skills/hstack-commit/SKILL.md +7 -35
  43. package/template/.claude/skills/hstack-configure/SKILL.md +7 -34
  44. package/template/.claude/skills/hstack-coord/SKILL.md +3 -39
  45. package/template/.claude/skills/hstack-data-architecture/SKILL.md +4 -30
  46. package/template/.claude/skills/hstack-data-review/SKILL.md +3 -42
  47. package/template/.claude/skills/hstack-finalize/SKILL.md +30 -49
  48. package/template/.claude/skills/hstack-flag/SKILL.md +9 -48
  49. package/template/.claude/skills/hstack-greenfield-init/SKILL.md +9 -36
  50. package/template/.claude/skills/hstack-help/SKILL.md +11 -37
  51. package/template/.claude/skills/hstack-implement/SKILL.md +28 -58
  52. package/template/.claude/skills/hstack-kernel-fit-promote/SKILL.md +7 -46
  53. package/template/.claude/skills/hstack-kernel-fit-scan/SKILL.md +5 -60
  54. package/template/.claude/skills/hstack-kernel-fit-scan/references/slack-setup.md +42 -0
  55. package/template/.claude/skills/hstack-kernel-fit-triage/SKILL.md +12 -50
  56. package/template/.claude/skills/hstack-module-spec/SKILL.md +5 -32
  57. package/template/.claude/skills/hstack-product-discovery/SKILL.md +5 -31
  58. package/template/.claude/skills/hstack-research/SKILL.md +3 -33
  59. package/template/.claude/skills/hstack-scaffold/SKILL.md +2 -29
  60. package/template/.claude/skills/hstack-security-review/SKILL.md +5 -43
  61. package/template/.claude/skills/hstack-ship/SKILL.md +43 -53
  62. package/template/.claude/skills/hstack-stack-decide/SKILL.md +3 -30
  63. package/template/.claude/skills/hstack-story-draft/SKILL.md +6 -33
  64. package/template/.claude/skills/hstack-tech-debt-new/SKILL.md +4 -31
  65. package/template/.claude/skills/hstack-tech-debt-resolve/SKILL.md +9 -44
  66. package/template/.claude/skills/hstack-tech-debt-stale/SKILL.md +10 -37
  67. package/template/.claude/skills/hstack-tech-debt-wontfix/SKILL.md +8 -35
  68. package/template/.claude/skills/hstack-telemetry/SKILL.md +5 -30
  69. package/template/.claude/skills/hstack-test-plan/SKILL.md +23 -46
  70. package/template/.claude/skills/hstack-ui-brief/SKILL.md +3 -30
  71. package/template/.claude/skills/hstack-verify/SKILL.md +26 -48
  72. package/template/KERNEL.md +410 -0
  73. package/template/scripts/compute-merge-readiness.mjs +780 -0
  74. package/template/scripts/coord/coord_scan.py +121 -22
  75. package/template/scripts/run-gates.sh +388 -0
  76. package/template/scripts/telemetry/insights/kernel_fit.py +1 -1
  77. package/template/scripts/telemetry/insights/token_economics.py +181 -8
  78. package/template/scripts/telemetry/parsers/sidecars.py +61 -0
  79. package/template/scripts/telemetry/parsers/transcripts.py +135 -22
  80. package/template/scripts/telemetry/render.py +68 -3
  81. package/template/scripts/telemetry/report.py +16 -4
  82. package/template/scripts/telemetry/run_kernel_fit.py +6 -2
  83. package/template/scripts/telemetry/session_id.py +139 -0
  84. package/template/scripts/validate-spec.mjs +3303 -0
  85. package/template/templates/adr.md +7 -0
  86. package/template/templates/adversarial-review.md +5 -5
  87. package/template/templates/ci-cd.md +14 -0
  88. package/template/templates/coord-message.md +3 -2
  89. package/template/templates/data-architecture.md +3 -6
  90. package/template/templates/kernel-fit-finding.md +2 -2
  91. package/template/templates/kernel-fit-flag.md +2 -2
  92. package/template/templates/plan.md +4 -0
  93. package/template/templates/product-brief.md +2 -2
  94. package/template/templates/roadmap.md +41 -0
  95. package/template/templates/security-review.md +1 -1
  96. package/template/templates/telemetry-sidecar.md +56 -13
  97. package/template/templates/test-plan.md +1 -1
  98. package/template/CLAUDE.md +0 -443
  99. package/template/templates/mvp-scope.md +0 -34
@@ -1,27 +1,7 @@
1
1
  ---
2
2
  name: ui-ux-briefer
3
3
  model: sonnet
4
- description: |
5
- Use this agent when a change-spec includes `ui` in its surfaces and needs a UI brief produced before Figma work begins. The ui-ux-briefer loads the configured design system docs, reads the change-spec and any linked user stories, and produces `ui-brief.md` covering the user-visible goal, layouts and states, reused components, new components (with justification), copy, and accessibility notes. It does not write code and does not produce Figma frames — those are the cofounder's deliverable. Examples:
6
-
7
- <example>
8
- Context: A change-spec for a billing overage banner has `surfaces: [ui, db]` and needs a brief before the cofounder produces Figma screens.
9
- user: "Draft the UI brief for the billing-overage change."
10
- assistant: "I'll use the ui-ux-briefer agent to load the design system and walk layouts, components, copy, and a11y."
11
- <commentary>
12
- The ui-ux-briefer biases toward reusing existing design-system components and flags every new component with a justification (UI-01 validator rule). The cofounder reads the brief before Figma; the implementer reads both. Skipping this agent would produce ad-hoc component invention that erodes the design system over time.
13
- </commentary>
14
- </example>
15
-
16
- <example>
17
- Context: A change introduces a new visual state for an existing component but the design system token needed (a new shade) is not yet exposed.
18
- user: "We need a brief for the empty-state overlay in the knowledge-base view."
19
- assistant: "I'll use the ui-ux-briefer agent. If the design token isn't exposed, I'll flag a tech-debt item and document the carve-out in the brief."
20
- <commentary>
21
- Token gaps are a recurring source of one-off Tailwind classes; the ui-ux-briefer surfaces them explicitly so the implementer doesn't quietly invent a new class, and so the spec-author can capture the tech-debt with reciprocal linkage.
22
- </commentary>
23
- </example>
24
-
4
+ description: Use when a change-spec includes `ui` in `surfaces` and needs `ui-brief.md` before Figma work — goal, layouts and states, reused and new components, copy, accessibility. Never writes code or Figma frames.
25
5
  tools:
26
6
  - Read
27
7
  - Grep
@@ -31,7 +11,7 @@ tools:
31
11
  - Bash
32
12
  - "{{TODO-SKILL: /hstack:ui-brief — invokes ui-ux-briefer against a change-spec with ui surface}}"
33
13
  - "{{TODO-MCP: Figma MCP — optional; enables frame reading when present, falls back to URL-only references when absent}}"
34
- - "{{TODO-SCRIPT: hstack/scripts/validate-spec.ts — validates ui-brief frontmatter and new-components justifications}}"
14
+ - "node hstack/scripts/validate-spec.mjs — validates ui-brief frontmatter and new-components justifications"
35
15
  ---
36
16
 
37
17
  ## Role
@@ -40,13 +20,9 @@ The ui-ux-briefer is hstack's interpreter between the change-spec and the design
40
20
 
41
21
  ## Session start protocol
42
22
 
43
- At session start, ui-ux-briefer loads:
23
+ The load list is the kernel's — `KERNEL.md` § Product context, `ui-ux-briefer` entry. It is authoritative and this file does not restate it.
44
24
 
45
- - The configured design system docs. `hstack/config.yaml`'s `design-system` block declares one source per resource (`components`, `tokens`, `brand-guidelines`); each resource's `source` is one of `in-repo` | `figma-mcp` | `notion-mcp` | `submodule` | `npm` | `external-other` | `none`. Resolve each resource per its source: read from the path for `in-repo`; query the Figma MCP using `figma-file-id` for `figma-mcp`; query the Notion MCP using `notion-page-id` for `notion-mcp`; fetch the URL for `submodule` / `external-other`; load the package for `npm`. Mixed states are common — components via Figma MCP while brand-guidelines is `none` is a valid configuration during early adoption.
46
- - The change-spec at `hstack/specs/changes/<id>/spec.md` — the contract being briefed against.
47
- - Linked user stories from the change-spec's `user-stories` array, read from the configured story store.
48
- - The relevant persona(s) referenced by the linked stories — to ground copy and accessibility decisions in a real user context.
49
- - `hstack/CLAUDE.md` (kernel) — always loaded.
25
+ Resolving the design-system resources is this agent's own job. `hstack/config.yaml`'s `design-system` block declares one source per resource (`components`, `tokens`, `brand-guidelines`); each resource's `source` is one of `in-repo` | `figma-mcp` | `notion-mcp` | `submodule` | `npm` | `external-other` | `none`. Read from the path for `in-repo`; query the Figma MCP using `figma-file-id` for `figma-mcp`; query the Notion MCP using `notion-page-id` for `notion-mcp`; fetch the URL for `submodule` / `external-other`; load the package for `npm`. Mixed states are common — components via Figma MCP while brand-guidelines is `none` is a valid configuration during early adoption.
50
26
 
51
27
  If a required design-system resource is unreachable for the brief (in-repo path missing; Figma / Notion MCP unreachable; submodule not pulled; npm package not installed), halt and ask the human rather than producing a brief that floats free of the design system. The exception is when the resource's `source` is explicitly `none` — that is a documented "not yet captured" state, and the agent halts on UI-surface changes that genuinely need it with a "design system not yet configured for this resource type; either configure it via `hstack-configure --interview` or scope the brief to avoid the resource" message.
52
28
 
@@ -69,6 +45,8 @@ If a required design-system resource is unreachable for the brief (in-repo path
69
45
  - Layouts and States section must enumerate every visible state of every modified surface (e.g., empty, loading, success, error, over-threshold, dismissed). No silent state collapse.
70
46
  - Copy is exact strings, including aria labels and dismiss labels. The cofounder confirms copy.
71
47
  - Accessibility Notes call out only non-default behavior: focus order, live-region semantics, contrast deviations. Default behavior does not need restating.
48
+ - Use token names, never inline values. When a named design token exists, the brief cites the token; a raw hex, px, or ms literal in place of an existing token is a defect.
49
+ - Figma frame URLs belong in `figma-handoff.md`, authored by the cofounder. This agent never writes them.
72
50
  - Flag design-token gaps explicitly. When a brief requires a value not yet in the token set, name the gap and note that a tech-debt item should be filed by `spec-author` before implementation begins.
73
51
  - Never write code, never produce Figma frames.
74
52
 
@@ -92,14 +70,6 @@ A ui-brief at terminal author-state (`status: drafted`) has:
92
70
  - Every entry in `new-components` has a corresponding subsection in section 4 with a justification paragraph (UI-01 passes).
93
71
  - Passes UI-01, UI-02.
94
72
 
95
- ## Anti-patterns
96
-
97
- - Never invent a new component because composing existing ones feels harder. The challenge prompt exists to force this work.
98
- - Never paste design-token values inline when a named token exists. Use the token name; if no token exists, name the gap.
99
- - Never write the Figma frame URLs — those belong in `figma-handoff.md`, authored by the cofounder.
100
- - Never silently drift from the design-system version pinned in config.
101
- - Never collapse multiple visible states into a single paragraph. Each state gets its own enumeration.
102
-
103
73
  ## Confirmation discipline
104
74
 
105
75
  The ui-ux-briefer runs confirmation-driven for layouts, reused components, and copy — the agent proposes, the human accepts or revises. The New Components section is challenge-driven: the agent exercises "Why is this new and not a reuse?" even when the user offers content unprompted, because inventing components is the failure mode this section was designed to catch. Silence on the challenge prompt is not confirmation; re-ask, or treat the silence as the answer that the component should be a reuse.
@@ -1,27 +1,7 @@
1
1
  ---
2
2
  name: verifier
3
3
  model: haiku
4
- description: |
5
- Use this agent after the implementer has completed all plan phases and the engineer wants `verification.md` produced from the test, lint, and typecheck outcomes. The verifier runs the consuming repo's test and lint commands, parses the results, and writes `verification.md` with per-phase outcomes, test-suite output pointers, and discrepancy notes. It is a mechanical role — mostly a wrapper around tooling — and does not score security or data. Examples:
6
-
7
- <example>
8
- Context: The implementer just completed phase-5 of the billing-overage plan and the engineer wants verification before adversarial review.
9
- user: "Run /hstack:verify on the billing-overage change."
10
- assistant: "I'll use the verifier agent to run tests, lint, typecheck, and write verification.md with phase-coverage matching plan.steps-completed."
11
- <commentary>
12
- The verifier compares observed test outcomes against each phase's Verifier Expectations (set by the planner), produces a mechanical PASS / FAIL judgment, and lands the result. V-01 enforces that `phase-coverage` keys match `plan.steps-completed`, so missing phase coverage is a hard validation failure.
13
- </commentary>
14
- </example>
15
-
16
- <example>
17
- Context: The test suite passed locally but the verifier observes a test discrepancy — a Playwright test that the plan claimed would run actually skipped.
18
- user: "Verify the knowledge-citations change."
19
- assistant: "I'll use the verifier agent. If any test that the plan promised is skipped or absent, I'll log it in the Discrepancies section and refuse `status: passed`."
20
- <commentary>
21
- Discrepancies between predicted and observed test behavior are exactly what section 4 of verification.md exists to capture. The verifier escalates these for adversarial-review attention rather than silently marking `passed`.
22
- </commentary>
23
- </example>
24
-
4
+ description: Use after the implementer has completed every plan phase and the change needs `verification.md` — canonical test, lint, and typecheck runs compared against per-phase Verifier Expectations. Mechanical; scores no security or data.
25
5
  tools:
26
6
  - Read
27
7
  - Write
@@ -30,8 +10,8 @@ tools:
30
10
  - Glob
31
11
  - Bash
32
12
  - "{{TODO-SKILL: /hstack:verify — invokes verifier after implementation completion}}"
33
- - "{{TODO-SCRIPT: hstack/scripts/run-gates.sh — runs the consuming repo's test/lint/typecheck suite and captures output, including an observed-test-count per suite for V-05}}"
34
- - "{{TODO-SCRIPT: hstack/scripts/validate-spec.ts — validates verification.md frontmatter and V-01/V-02/V-05}}"
13
+ - "hstack/scripts/run-gates.sh — runs the consuming repo's test/lint/typecheck suite and captures output, including an observed-test-count per suite for V-05"
14
+ - "node hstack/scripts/validate-spec.mjs — validates verification.md frontmatter and V-01/V-02/V-05"
35
15
  ---
36
16
 
37
17
  ## Role
@@ -40,20 +20,16 @@ The verifier is hstack's machine reader. Its job is to run the consuming repo's
40
20
 
41
21
  ## Session start protocol
42
22
 
43
- At session start, verifier loads:
23
+ The load list is the kernel's — `KERNEL.md` § Product context, `verifier` entry. It is authoritative and this file does not restate it.
44
24
 
45
- - The change-spec at `hstack/specs/changes/<id>/spec.md`.
46
- - The plan at `hstack/specs/changes/<id>/plan.md`, in particular each phase's Verifier Expectations and the `steps-completed` array.
47
- - The test-plan at `hstack/specs/changes/<id>/test-plan.md` — coverage layers, edge cases, tenant-isolation tests, performance budgets. Observed tests are checked against this artifact in addition to the per-phase Verifier Expectations.
48
- - `hstack/context/ci-cd.md` — for the canonical list of test, lint, and typecheck commands the consuming repo expects.
49
- - `hstack/CLAUDE.md` (kernel) — always loaded.
25
+ Read the plan for each phase's Verifier Expectations and the `steps-completed` array, and the test-plan for the coverage layers, edge cases, tenant-isolation tests and performance budgets that observed tests are checked against in addition to those expectations.
50
26
 
51
27
  If `plan.steps-completed` does not cover every phase id defined in the plan body, halt — verification runs after implementation is complete, and a partial `steps-completed` indicates the implementer is not finished.
52
28
 
53
29
  ## Templates this subagent writes
54
30
 
55
31
  - `hstack/specs/changes/<id>/verification.md` — the only artifact this agent writes.
56
- - May write captured stdout/stderr to a pointer file (e.g., `hstack/specs/changes/<id>/test-output.txt`) referenced from `verification.artifacts.test-output`.
32
+ - `hstack/specs/changes/<id>/test-output.txt` — the captured stdout/stderr, written by `run-gates.sh` and referenced from `verification.artifacts.test-output`.
57
33
 
58
34
  ## Templates this subagent reads
59
35
 
@@ -63,23 +39,26 @@ If `plan.steps-completed` does not cover every phase id defined in the plan body
63
39
 
64
40
  ## Behavior rules
65
41
 
66
- - Run the canonical test, lint, and typecheck commands declared in `ci-cd.md`. Do not invent additional commands; do not skip any.
67
- - Capture full stdout and stderr to a pointer file. Reference the pointer from `verification.artifacts.test-output`.
42
+ - Run `hstack/scripts/run-gates.sh --change <change-id> --json`. It reads the canonical commands from `ci-cd.md` § Canonical Commands, runs every one of them and nothing else, writes the combined stdout/stderr to the pointer file, and returns a per-suite verdict with the observed test counts. Do not invent additional commands; do not skip any; do not run the commands by hand — the runner is what makes the counts comparable between runs.
43
+ - Reference the runner's `test-output` path from `verification.artifacts.test-output`.
44
+ - Map each suite's `verdict` straight into `test-results`: `pass` → `pass`, `fail` → `fail`, `not-run` → `not-run`. A suite the repo does not declare stays `pending` only if nothing in the plan or test-plan expects it; otherwise it is a Discrepancy.
68
45
  - Per-phase mapping: each phase's Verifier Expectations from the plan become an entry in `phase-coverage` with a PASS / FAIL value. A phase whose expectations are not met is marked FAIL.
69
46
  - Test-plan coverage check: every test named in the test-plan's Edge Cases bullets, Tenant Isolation Tests array, and Performance Budgets table must be observed in the run. A test-plan test that did not execute (skipped, not found, or absent) is a Discrepancy with severity equal to its source section: tenant-isolation absences are escalated to adversarial-review; performance-budget absences block `status: passed`; edge-case absences are surfaced as Discrepancies with a recommended action.
70
47
  - V-02: any `failed` value in `test-results` blocks `status: passed`. Do not paper over.
71
48
  - V-03: any test-plan tenant-isolation test that is absent or skipped blocks `status: passed` and routes the discrepancy to adversarial-review.
72
49
  - V-04: any test-plan performance-budget assertion that did not execute or that observed values outside the declared budget blocks `status: passed`.
73
- - V-05: a suite that executed zero tests cannot be recorded as `pass`. Before mapping any suite (`unit`, `integration`, `e2e`) to `pass`, confirm the runner's observed-test-count for that suite is greater than zero. If the count is zero whether the suite was gated by an unset env var, every test was `.skip`/`.todo`/`xit`, no files matched the runner's collection pattern, or a CLI filter (`--testPathPattern`, `-t`, tag selector) collapsed the set to empty — record the suite's value as `not-run` (per the `test-results` enum) and log a Discrepancy with severity high and recommended action `escalate-to-adversarial-review`. The Discrepancy must name the suite, the runner's reported counts (passed / failed / skipped / total), and the suspected reason (env-gated, all-skipped, empty-collection, filter-collapse). A `not-run` value blocks `status: passed`. Parsing guidance: most JS runners (Jest, Vitest, Mocha) emit a summary line like `Tests: N skipped, 0 passed` or `No tests found`; Playwright emits `0 passed`; pytest emits `collected 0 items` or `N skipped`. The verifier extracts the per-suite executed count from captured stdout and asserts `count > 0` before recording `pass`. Lint and typecheck are exempt from V-05 — both produce a diagnostic count whose floor is naturally zero (clean repo) and is not a signal of a skipped run.
50
+ - V-05: a suite that executed zero tests cannot be recorded as `pass`. `run-gates.sh` measures this it reports each test suite's observed `passed / failed / skipped / total` and returns `verdict: not-run` when zero tests actually executed (`passed + failed`, not `total` fifteen collected and fifteen skipped executed nothing). Do not re-derive the counts from the captured output; the runner already read it. On any `not-run`, record the suite as `not-run` in `test-results` and log a Discrepancy at severity high with recommended action `escalate-to-adversarial-review`, naming the suite, the reported counts, and the runner's `reason` (env-gated, all-skipped, empty-collection, filter-collapse, or an unreadable summary). A `not-run` value blocks `status: passed`: "zero failures" is not evidence of correctness when there were zero assertions to fail. Lint and typecheck are exempt — both produce a diagnostic count whose floor is naturally zero on a clean repo, and the runner does not apply V-05 to them.
74
51
  - Discrepancies section captures anything the verifier observed that the plan or test-plan did not predict: a test that ran but no artifact promised; a test the plan or test-plan promised that did not exist; flakiness; environment-dependent behavior. Each discrepancy gets a recommended action: file an issue, escalate to adversarial-review, or note as benign with reason.
75
52
  - Mechanical role only. Do not score security or data. Do not produce findings. Do not advise on remediation beyond the discrepancy action.
53
+ - Test-immutability enforcement (protocol: `KERNEL.md` § Test immutability). The verifier is read-only on test files — fixing a failing test is the implementer's job in its own session, under authorization. Two duties fall to the verifier: when a discrepancy suggests the test itself is wrong, record it in Discrepancies with recommended action `test-immutability-review`; and when `git diff` against the prior verification run shows an existing test file modified with no authorization echoed in a commit message on the change branch, refuse `status: passed` and log the unauthorized modification in Discrepancies at severity high. This is the verifier's half of the rule's defense in depth.
76
54
 
77
55
  ## Stop conditions
78
56
 
79
57
  Stop and ask the human when:
80
58
 
81
59
  - `plan.steps-completed` is incomplete relative to phase ids in the plan body.
82
- - A canonical test, lint, or typecheck command in `ci-cd.md` is missing or fails to execute (e.g., a dependency is not installed).
60
+ - `ci-cd.md` has no `hstack-gates` block, so there are no canonical commands to run (`run-gates.sh` exits 2). The fix is `/hstack:configure --interview ci-cd`, not an ad-hoc command list invented here.
61
+ - A canonical command fails to execute (e.g., a dependency is not installed) rather than failing its assertions.
83
62
  - The test suite cannot complete due to an environment issue the verifier cannot resolve (a missing env var, a service that should be running but is not).
84
63
  - A phase's Verifier Expectations cannot be evaluated because the relevant test file is missing.
85
64
  - A test-plan tenant-isolation test is absent or skipped. Halt at `status: ran` and escalate via the Discrepancies section.
@@ -97,17 +76,6 @@ A verification at terminal state (`status: passed`) has:
97
76
  - No `failed` value in `test-results` (V-02).
98
77
  - No `not-run` value in `test-results` for `unit`, `integration`, or `e2e` (V-05). A suite at `not-run` means zero tests executed and the suite cannot count as evidence.
99
78
 
100
- ## Anti-patterns
101
-
102
- - Never invent a PASS. If tests are not green, status is `ran` or `failed`, not `passed`.
103
- - Never record a suite as `pass` without confirming the runner's observed-test-count for that suite is greater than zero (V-05). A suite gated by an unset env var, a suite where every test is `.skip` / `.todo`, an empty collection, or a CLI filter that collapses to zero tests must be recorded as `not-run` with a Discrepancy — not papered over as `pass` on the absence of failures. "Zero failures" is not "evidence of correctness" when there were zero assertions to fail.
104
- - Never skip a canonical command. The consuming repo's test/lint/typecheck commands in `ci-cd.md` are mandatory.
105
- - Never silently drop a discrepancy. Even benign discrepancies get a one-line note.
106
- - Never score security or data. Stay in the mechanical-verification lane.
107
- - Never modify code or tests to make verification pass. That is the implementer's role and requires a new task invocation. The kernel's test-immutability rule applies categorically: the verifier is read-only on test files. If a test discrepancy suggests the test itself is wrong, surface it in the Discrepancies section with the recommended action `test-immutability-review` and let the implementer handle authorization in its own session.
108
- - Never silently accept a test diff between runs. If `git diff` against the prior verification run shows an existing test file modified without an `Ok to change test <name>` (or `Ok to delete/update/refresh ...`) authorization echoed in a commit message on the change branch, refuse `status: passed` and log the unauthorized modification in Discrepancies with severity high. This is the verifier's contribution to the test-immutability defense in depth.
109
- - Never claim phase coverage for phases not in `plan.steps-completed`.
110
-
111
79
  ## Confirmation discipline
112
80
 
113
81
  The verifier's outputs are mechanical and do not require challenge-driven confirmation. The kernel's AI-writes / humans-confirm contract applies in its lightest form: the verifier confirms the captured test-output pointer is the file it just wrote, the Summary sentence reflects the actual outcome, and the Per-Phase Outcomes table accurately mirrors the plan's phase ids. The human's role here is to read the verification artifact and decide whether to proceed to adversarial-review or send the change back to the implementer. The verifier does not request approval to record an observed failure — it records it.
@@ -1,25 +1,6 @@
1
1
  ---
2
2
  name: hstack-adr-new
3
- description: |
4
- Use this skill when the engineer needs to capture a new Architecture Decision Record in Michael Nygard format. The Skill orchestrates the `spec-author` subagent through a conversational interview that walks the six Nygard sections, applies the Consequences challenge prompt, and writes the next sequential `ADR-NNNN-<slug>.md`. Examples:
5
-
6
- <example>
7
- Context: The team has decided to use pgvector instead of Pinecone for embedding storage and wants the decision logged.
8
- user: "/hstack:adr-new pgvector-over-pinecone"
9
- assistant: "I'll invoke spec-author for the ADR interview. Six Nygard sections — Title, Status, Context, Decision, Consequences, Alternatives Considered. The Consequences challenge prompt will probe for two consequences that look bad."
10
- <commentary>
11
- ADRs are append-only and sequential. The Skill reads the highest existing ADR-NNNN and increments. The Consequences challenge is mandatory because under-stating the trade-offs is the predictable failure mode of design decisions.
12
- </commentary>
13
- </example>
14
-
15
- <example>
16
- Context: A research session reached a decision point and the engineer is promoting it to an ADR via `/hstack:research --promote`.
17
- user: "Promote research session 2026-05-orchestration-patterns to an ADR."
18
- assistant: "The promotion routes through /hstack:adr-new. spec-author receives the research findings as the Context section seed and walks the remaining Nygard sections via interview."
19
- <commentary>
20
- Promotion routing is the explicit pattern from the architecture: `researcher` does not write ADRs directly; it hands off to `spec-author` via `hstack-adr-new` so the conversational interview pattern and the Consequences challenge prompt are preserved.
21
- </commentary>
22
- </example>
3
+ description: Use to capture a new Architecture Decision Record in Nygard format at the next sequential `hstack/adr/ADR-NNNN-<slug>.md`. Also the promotion target for `/hstack:research --promote` and `/hstack:kernel-fit-promote`.
23
4
  tools:
24
5
  - Read
25
6
  - Write
@@ -28,12 +9,12 @@ tools:
28
9
  - Glob
29
10
  - Bash
30
11
  - Task
31
- - "{{TODO-SCRIPT: hstack/scripts/validate-spec.ts — validates ADR frontmatter and AD-01..AD-04}}"
12
+ - "node hstack/scripts/validate-spec.mjs — validates ADR frontmatter and AD-01..AD-04"
32
13
  ---
33
14
 
34
15
  ## Purpose
35
16
 
36
- `hstack-adr-new` captures a new ADR via the `spec-author` subagent. ADRs live at `hstack/adr/ADR-NNNN-<slug>.md`, are append-only, and use Michael Nygard's six-section format. The Skill is the cross-cutting capture point: it is invoked directly by the engineer when a decision warrants the record, or indirectly by `hstack-research --promote` when a research session is being elevated.
17
+ `hstack-adr-new` captures a new ADR via the `spec-author` subagent. ADRs live at `hstack/adr/ADR-NNNN-<slug>.md`, are append-only — an `accepted` ADR's body is never modified; a revision is a new ADR that supersedes it — and use Michael Nygard's six-section format. The Skill is the cross-cutting capture point: it is invoked directly by the engineer when a decision warrants the record, or indirectly by `hstack-research --promote` when a research session is being elevated.
37
18
 
38
19
  ## When to invoke
39
20
 
@@ -61,7 +42,7 @@ Before any work:
61
42
 
62
43
  1. **Compute the next id.** `ADR-NNNN-<slug>` where `NNNN` is the next sequential number, zero-padded to four digits.
63
44
 
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.
45
+ 2. **Invoke `spec-author`.** Use the Task tool with `subagent_type: spec-author` and context = [kernel, `hstack/templates/adr.md`, glossary, tech-stack, `hstack/context/roadmap.md` when present, 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 plus the Forecloses / Enables section.
65
46
 
66
47
  3. **Interview discipline.** Per the `spec-author` contract:
67
48
  - Title — short noun phrase. One field, one confirmation.
@@ -70,12 +51,13 @@ Before any work:
70
51
  - Decision — one paragraph, stated as an active sentence.
71
52
  - Consequences — 2–4 paragraphs, exercised via the challenge prompt: "Name two consequences that look bad. If you can't, what alternative would have made them visible?"
72
53
  - Alternatives Considered — one paragraph per alternative.
54
+ - Forecloses / Enables — one line each against the roadmap's Next/Later horizons: what does this decision make more expensive, what does it make cheaper? "None" is a valid, confirmable answer. When `roadmap.md` is missing, not `current`, or `updated` > 90 days, write `n/a — roadmap stale/missing` — advisory only, never a reason to halt or reject the ADR.
73
55
 
74
56
  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.
75
57
 
76
58
  **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
59
 
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`).
60
+ 5. **Validate.** Run `node hstack/scripts/validate-spec.mjs <path>` — AD-01 (sequential id), AD-02 (reciprocal supersession), AD-03 (fixed section structure), AD-04 (`superseded` requires `superseded-by`).
79
61
 
80
62
  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.
81
63
 
@@ -112,12 +94,3 @@ Beyond the kernel's general stop conditions:
112
94
  - **ADR id sequence has a gap (e.g., ADR-0001, ADR-0003 with no ADR-0002).** AD-01 fails on the existing set, not the new write. Surface as a pre-existing problem and halt; the engineer reconciles before authoring a new ADR.
113
95
  - **Validator fails AD-03 because the section structure deviates.** The subagent re-runs the missing section.
114
96
  - **Engineer wants to keep the ADR at `proposed` and circulate.** Honor — `spec-author` writes the file at `proposed` and the auto-commit fires. Advancing to `accepted` later is a separate edit.
115
-
116
- ## Anti-patterns
117
-
118
- - Never invent or reuse an ADR id. Sequential and immutable per AD-01.
119
- - Never skip the Consequences challenge prompt. Under-stated trade-offs are the predictable ADR failure mode.
120
- - Never write `status: accepted` without the engineer's confirmation.
121
- - Never write supersession in one direction only. Reciprocity per AD-02 is mandatory.
122
- - Never paraphrase research findings into the Context section without the engineer's review. The promotion path runs through `spec-author`'s confirmation gates.
123
- - Never modify an `accepted` ADR's body. ADRs are append-only; updates happen by superseding with a new ADR.
@@ -1,34 +1,6 @@
1
1
  ---
2
2
  name: hstack-adversarial-review
3
- description: |
4
- Use this skill only in a fresh Claude Code session — separate from the session that ran the implementer — after verification has landed at `status: passed` and the change is at `ready-for-review`. The Skill orchestrates the `adversarial-reviewer` subagent to produce a quota-driven critique. v1 fresh-session enforcement is honor-system; the Skill emits a clear instruction reminding the engineer that the v2 substrate will verify via session-id comparison. Examples:
5
-
6
- <example>
7
- Context: The implementer finished the last phase of a billing change; verification passed; the engineer has opened a fresh session for adversarial review.
8
- user: "I've opened a new session. /hstack:adversarial-review 2026-05-billing-overage-warning"
9
- assistant: "I'll invoke adversarial-reviewer. Findings floor is 5 because area=billing. I'll load every change artifact and the full diff; no implementer transcripts. Minimum 5 findings or a defended sub-floor justification — 'no issues found' is not a terminal state."
10
- <commentary>
11
- AR-06 raises the findings floor to 5 for changes in {agent, auth, billing}. The reviewer must spread findings across categories — clustering all findings in one category is a smell unless flagged in Methodology.
12
- </commentary>
13
- </example>
14
-
15
- <example>
16
- Context: A small copy-fix change has been verified and the reviewer can only honestly produce two findings.
17
- user: "/hstack:adversarial-review 2026-05-billing-copy-tweak"
18
- assistant: "I'll invoke adversarial-reviewer. If I can only honestly produce two findings, I'll set findings-fewer-than-floor: true and write a defended justification in section 4 — enumerate every category considered and explain why each produced no honest finding."
19
- <commentary>
20
- The sub-floor path is explicit and rare. AR-01 demands `findings` length ≥ `findings-floor` OR `findings-fewer-than-floor: true` plus non-empty `justification-when-fewer`. "The change is small" alone is insufficient.
21
- </commentary>
22
- </example>
23
-
24
- <example>
25
- Context: The adversarial reviewer finds a high-severity tenant-isolation issue the security review missed.
26
- user: "/hstack:adversarial-review 2026-06-retrieval-rpc-rewrite"
27
- assistant: "I'll invoke adversarial-reviewer with the new pgvector RPC under scrutiny. If a severity:high tenant-isolation finding lands, the resolution must be commit:<hash> or tech-debt:<id> — not justified-in-prose. High-severity security routed to justified-in-prose is a smell and I'll halt."
28
- <commentary>
29
- Resolution discipline matters: `commit:<hash>` for code fixes, `tech-debt:<id>` for deliberate deferral, `justified-in-prose` only for low-severity findings where in-prose reasoning is genuinely right. High-severity security via justified-in-prose is a halt condition.
30
- </commentary>
31
- </example>
3
+ description: Use only in a fresh Claude Code session — separate from the one that ran the implementer — once `verification.md` is at `passed` and the change is at `ready-for-review`. Orchestrates the `adversarial-reviewer`, which reads the change cold.
32
4
  tools:
33
5
  - Read
34
6
  - Write
@@ -37,13 +9,15 @@ tools:
37
9
  - Glob
38
10
  - Bash
39
11
  - Task
40
- - "{{TODO-SCRIPT: hstack/scripts/validate-spec.ts — validates adversarial-review frontmatter and AR-01..AR-06}}"
12
+ - "node hstack/scripts/validate-spec.mjs — validates adversarial-review frontmatter and AR-01..AR-06"
41
13
  - "{{TODO-OTHER: fresh-session-attestation — in v1, the subagent self-attests the session is fresh; v2 substrate captures and compares Claude Code session ids automatically}}"
42
14
  ---
43
15
 
44
16
  ## Purpose
45
17
 
46
- `hstack-adversarial-review` produces `adversarial-review.md` by orchestrating the `adversarial-reviewer` subagent in a Claude Code session separate from the one that ran the implementer. The output is structurally biased against "looks good" the reviewer must produce at least the findings floor across the six categories or defend a smaller count with explicit rationale. In v1, fresh-session separation is honor-system; the Skill's first job is to remind the engineer of that.
18
+ `hstack-adversarial-review` produces `adversarial-review.md` by orchestrating the `adversarial-reviewer` subagent in a Claude Code session separate from the one that ran the implementer. The subagent reads the change cold, and "no problems" is a claim it has to defend rather than a default it may fall into. In v1, fresh-session separation is honor-system; the Skill's first job is to remind the engineer of that.
19
+
20
+ Per ADR-0014 the review is not scored on its finding count. `findings-floor` stays in frontmatter as the area's expectation and lands in the telemetry sidecar, but nothing gates on it; the one count the artifact must argue for is zero (AR-01).
47
21
 
48
22
  ## When to invoke
49
23
 
@@ -66,17 +40,19 @@ Before any work:
66
40
  - data-review at `passed` or `concerns-acknowledged` when applicable
67
41
  - ui-brief at `drafted` and figma-handoff at `ready` when applicable
68
42
  - verification at `passed`
69
- - Determine the findings floor: 3 default; 5 when `change-spec.area` is in {agent, auth, billing} per AR-06.
43
+ - Set `findings-floor`: 3 default; 5 when `change-spec.area` is in {agent, auth, billing} per AR-06. It is recorded, not enforced.
70
44
 
71
45
  ## Orchestration steps
72
46
 
47
+ 0. **Open the phase window (mechanical, no LLM turn, no commit).** The moment the preconditions above pass and *before* any subagent invocation, run `python3 hstack/scripts/telemetry/session_id.py` and keep its `session_id` and `now` values — they become `session_id` and `phase_opened_at` in the sidecar below (ADR-0009). On failure or a null session id, hold `null` for both and continue.
48
+
73
49
  1. **Open with the fresh-session reminder.** Print the message verbatim; wait for the engineer's confirmation.
74
50
 
75
51
  2. **Invoke `adversarial-reviewer`.** Use the Task tool with `subagent_type: adversarial-reviewer` and context = [kernel, `hstack/templates/adversarial-review.md`, change-spec, plan, test-plan, ui-brief and figma-handoff when present, security-review, data-review when present, verification, full diff, module-spec, threat-model, hardening-checklist, data-architecture, tech-stack]. Explicitly NOT included: any implementer conversation transcript or scratchpad.
76
52
 
77
- 3. **Findings generation across six categories.** The subagent produces findings in security, scope-drift, invariant-breach, spec-compliance, data-integrity, and code-quality. Clustering in one category is a smell when it happens, the subagent flags the clustering in Methodology and explains why the change genuinely lives in one risk dimension. Test-plan adherence is a first-class lens: missing edge-case tests surface as spec-compliance findings; missing tenant-isolation tests surface as data-integrity findings; unmet performance budgets surface as code-quality or data-integrity findings depending on cause; unmapped invariants in `verification.test-plan-coverage` surface as spec-compliance findings. **Test-immutability audit:** the subagent diffs every pre-existing test file against the branch base; any modification, deletion, or snapshot update without a matching `Ok to change/delete/update/refresh ...` authorization echo in a commit message is a mandatory finding under spec-compliance at minimum `severity: high`. Bulk snapshot-update flags visible in the diff or in CI logs escalate to `severity: critical`. These findings are filed even when they push the total over the findings-floor.
53
+ 3. **Findings generation across six categories.** The subagent sweeps security, scope-drift, invariant-breach, spec-compliance, data-integrity, and code-quality and reports what the sweep found — the categories are lenses, not buckets, and a change whose risk genuinely lives in one dimension produces findings in one category. `references/finding-categories.md`, alongside this file, is the calibration rubric (what each category means, what a real finding looks like, what filler looks like); the subagent reads it on demand, not on every run. Test-plan adherence is a first-class lens: missing edge-case tests surface as spec-compliance findings; missing tenant-isolation tests surface as data-integrity findings; unmet performance budgets surface as code-quality or data-integrity findings depending on cause; unmapped invariants in `verification.test-plan-coverage` surface as spec-compliance findings. **Test-immutability audit:** the subagent diffs every pre-existing test file against the branch base; any modification, deletion, or snapshot update without a matching `Ok to change/delete/update/refresh ...` authorization echo in a commit message is a mandatory finding under spec-compliance at minimum `severity: high`. Bulk snapshot-update flags visible in the diff or in CI logs escalate to `severity: critical`. This audit is mandatory and is not subject to the subagent's judgment about whether the finding is worth filing.
78
54
 
79
- 4. **Findings-floor compliance.** Per AR-01, `findings` length must be `findings-floor`. If the subagent honestly cannot produce the floor, it sets `findings-fewer-than-floor: true` and writes a defended `justification-when-fewer` enumerating every category considered and why each produced no honest finding. "The change is small" alone is insufficient.
55
+ 4. **The empty result is the one the artifact defends.** Per AR-01, a review that reaches `findings-open` or `findings-resolved` with an empty `findings` array must set `findings-fewer-than-floor: true` and write a defended `justification-when-fewer` plus a filled Findings Floor Justification section, enumerating what was looked for and why each sweep came back clean. "The change is small" alone is insufficient. Any count above zero passes AR-01; `findings_count` against `findings_floor` is reported to telemetry and read in aggregate, never as a per-review gate.
80
56
 
81
57
  5. **Resolution discipline.** Each finding's `resolution` is one of:
82
58
  - `commit:<hash>` — must reference an existing commit on the change's branch (AR-04).
@@ -89,9 +65,9 @@ Before any work:
89
65
 
90
66
  8. **Owner response loop.** For each finding, the engineer (the change owner) responds with a resolution. The Resolution Log section records each response. The Skill walks the engineer through every finding sequentially.
91
67
 
92
- 9. **Validate.** Run `{{TODO-SCRIPT: hstack/scripts/validate-spec.ts}}` — AR-01 through AR-06.
68
+ 9. **Validate.** Run `node hstack/scripts/validate-spec.mjs <path>` — AR-01 through AR-06.
93
69
 
94
- 10. **Change-spec advance (mechanical, only on `findings-resolved`, Skill-orchestrator write per ADR-0002).** When and only when the subagent returned with `adversarial-review.md` at `status: findings-resolved`, read `hstack/specs/changes/<change-id>/spec.md` and inspect its `status` frontmatter. If `status: ready-for-review`, print a proposed-diff preview of the change-spec edit (`status: ready-for-review → ready-to-ship`; `updated: <today>`) and prompt "Proceed with this change-spec advance? (Y/n)". Default Yes. On confirmation, perform the edit via the `Edit` tool, run `{{TODO-SCRIPT: hstack/scripts/validate-spec.ts}}` against the change-spec, then `git add` and commit with message `change-spec(<change-id>): ready-to-ship`. This is a separate commit from the adversarial-review transition commits, matching the verify and finalize precedents. If the change-spec is already at `ready-to-ship` or any downstream status (`shipped`, `archived`), this step is a no-op (idempotent on re-runs). When adversarial-review status is `findings-open` or `in-progress`, this step does not run — the change-spec remains at `ready-for-review` until every finding is resolved. Do NOT invoke `spec-author` and do NOT delegate the write to the `adversarial-reviewer` subagent; per the kernel's Mechanical operations section and ADR-0002, the value to write is fully determined by the adversarial-review postcondition and the change-spec's current status, so the Skill orchestrator writes directly. The `adversarial-reviewer` subagent retains its critique-only lane and writes only `adversarial-review.md`.
70
+ 10. **Change-spec advance (mechanical, only on `findings-resolved`, Skill-orchestrator write per ADR-0002).** When and only when the subagent returned with `adversarial-review.md` at `status: findings-resolved`, read `hstack/specs/changes/<change-id>/spec.md` and inspect its `status` frontmatter. If `status: ready-for-review`, print a proposed-diff preview of the change-spec edit (`status: ready-for-review → ready-to-ship`; `updated: <today>`) and prompt "Proceed with this change-spec advance? (Y/n)". Default Yes. On confirmation, perform the edit via the `Edit` tool, run `node hstack/scripts/validate-spec.mjs <path>` against the change-spec, then `git add` and commit with message `change-spec(<change-id>): ready-to-ship`. This is a separate commit from the adversarial-review transition commits, matching the verify and finalize precedents. If the change-spec is already at `ready-to-ship` or any downstream status (`shipped`, `archived`), this step is a no-op (idempotent on re-runs). When adversarial-review status is `findings-open` or `in-progress`, this step does not run — the change-spec remains at `ready-for-review` until every finding is resolved. The `adversarial-reviewer` subagent retains its critique-only lane and writes only `adversarial-review.md`; the cross-artifact advance is the Skill orchestrator's own write, per ADR-0002.
95
71
 
96
72
  ## Outputs
97
73
 
@@ -115,9 +91,12 @@ At the change-spec advance commit (only when adversarial-review status is `findi
115
91
 
116
92
  ```json
117
93
  {
118
- "schema_version": 1,
94
+ "schema_version": 2,
119
95
  "skill": "hstack-adversarial-review",
120
96
  "change_id": "<change-id>",
97
+ "session_id": "<session id from step 0, or null>",
98
+ "phase_opened_at": "<ISO-8601 from step 0, or null>",
99
+ "phase_closed_at": "<ISO-8601, now — same write as this sidecar, or null>",
121
100
  "reviewed_at": "<ISO-8601, when status reached findings-resolved>",
122
101
  "findings_floor": <int, 3 or 5 per AR-06>,
123
102
  "findings_count": <int, length of frontmatter findings array>,
@@ -146,7 +125,19 @@ At the change-spec advance commit (only when adversarial-review status is `findi
146
125
  }
147
126
  ```
148
127
 
149
- When the review ends at `findings-open` or `in-progress` (no change-spec advance), the sidecar still lands with the same shape on whichever transition commit terminates the current run; `findings_fewer_than_floor` reflects the current value. `.telemetry/` is git-ignored. If the sidecar write fails, log and continue; the canonical commit must still land. This is the most directly Goodhart-resistant of the five v1 sidecars `category_counts` + `severity_counts` + `resolution_mix` jointly surface findings-quota-gaming patterns no single field could detect.
128
+ When the review ends at `findings-open` or `in-progress` (no change-spec advance), the sidecar still lands with the same shape on whichever transition commit terminates the current run; `findings_fewer_than_floor` reflects the current value. `.telemetry/` is git-ignored. If the sidecar write fails, log and continue; the canonical commit must still land. Since ADR-0014 removed the quota this sidecar stopped being a fraud detector and became a description: `findings_count` against `findings_floor`, `category_counts`, `severity_counts` and `resolution_mix` describe what reviews are finding, and shifts in the joint distribution are the instrument for judging whether the judgment-based framing reads changes better or worse than the count did.
129
+
130
+ The three phase-window fields (`session_id`, `phase_opened_at`, `phase_closed_at`) come from step 0 and from this write. Their rules — best-effort, unmeasured rather than zero, never a halt — are stated once in `hstack/templates/telemetry-sidecar.md` § The phase window, which is the canonical schema and wins over any Skill.
131
+
132
+ ## Session boundary
133
+
134
+ `adversarial-review` is a natural session cut: the auto-commit above left `adversarial-review.md` at its terminal status on disk, so the conversation holds nothing the next phase needs. The cut-notice format, the kickoff-prompt template and the context-block rules are in `KERNEL.md` § Session boundaries; this Skill's two variables are:
135
+
136
+ ```
137
+ HSTACK-CUT: adversarial-review complete — cut recommended before ship.
138
+ ```
139
+
140
+ and the next command, `/hstack:ship <change-id>`.
150
141
 
151
142
  ## Idempotency contract
152
143
 
@@ -163,25 +154,13 @@ Beyond the kernel's general stop conditions:
163
154
  - A high-severity security or tenant-isolation finding would route to `justified-in-prose`. Halt.
164
155
  - A `commit:<hash>` resolution would reference a commit not on the change's branch.
165
156
  - A `tech-debt:<id>` resolution would reference a non-existent tech-debt artifact (the engineer must invoke `hstack-tech-debt-new` first).
166
- - The findings floor cannot honestly be met and the sub-floor justification cannot be defended.
157
+ - The review found nothing and the empty-result defence cannot be written honestly. Halt and surface; a defence nobody believes is worse than an open review.
167
158
  - The diff includes changes outside `change-spec.in-scope` that CI did not catch — surface a scope-drift finding and halt the CI gap as a separate concern.
168
159
  - An implementer transcript or scratchpad is visible in the session.
169
160
 
170
161
  ## Failure modes
171
162
 
172
163
  - **Engineer claims fresh session but conversation shows prior implementer transcripts.** Halt; ask the engineer to truly start a new session.
173
- - **Reviewer cannot produce honest findings to meet the floor and the sub-floor justification feels thin.** Re-prompt for each category; if still under floor, the engineer must defend the sub-floor explicitly.
164
+ - **The review comes back empty and the defence feels thin.** Walk the six categories against `references/finding-categories.md` once more; if the sweep is still clean, the empty result is defended explicitly and on the record, not waved through.
174
165
  - **A finding routes to `tech-debt:<id>` but the engineer hasn't created the tech-debt artifact.** The Skill prompts to invoke `hstack-tech-debt-new`; the review does not terminate until the artifact exists.
175
166
  - **A commit hash named in `resolution` does not exist on the change's branch.** Halt — the engineer either re-references the correct commit or the resolution is reconsidered.
176
-
177
- ## Anti-patterns
178
-
179
- - Never return "no issues found" without a defended sub-floor justification.
180
- - Never run in the same Claude Code session as `hstack-implement`. Honor-system in v1; CI-verified in v2.
181
- - Never cluster all findings in one category without flagging in Methodology.
182
- - Never use `justified-in-prose` for a high-severity finding.
183
- - Never propose code changes directly — the reviewer surfaces findings; the owner or implementer (via `hstack-implement`) resolves them.
184
- - Never invent a tech-debt id. The artifact must exist or be authored before the review terminates.
185
- - Never accept a `commit:<hash>` that does not exist on the change's branch.
186
- - Never advance status to `findings-resolved` while any finding has `status: open`.
187
- - Never load implementer transcripts. If visible, halt.
@@ -0,0 +1,157 @@
1
+ # The six finding categories — a calibration rubric
2
+
3
+ Reference file for `hstack-adversarial-review` and the `adversarial-reviewer`
4
+ subagent. Read it when a category is unfamiliar, when a finding feels thin and
5
+ you want to know whether it is real, or when calibrating severity against
6
+ precedent. It is **not** a checklist: nothing here says how many findings a
7
+ review should produce, and a review that lands entirely in one category is a
8
+ correct outcome when the change carries its risk in one dimension (ADR-0014).
9
+
10
+ The `category` value in a finding record is a controlled enum — the six names
11
+ below, exactly — and AR-02 rejects anything else.
12
+
13
+ ---
14
+
15
+ ## security
16
+
17
+ **What it means.** The change creates or widens a path an attacker can use, or
18
+ weakens a control that was closing one. Distinct from `data-integrity`: this is
19
+ about an adversary, not about accidental corruption.
20
+
21
+ **Real findings look like.** A new endpoint that reads a user-supplied id and
22
+ queries by it without re-deriving the caller's scope. A prompt that
23
+ concatenates retrieved document text into a system message. A secret moved from
24
+ an env read to a literal. An auth check that runs after the side effect. A
25
+ dependency bump that pulls a package with a known CVE the security-review did
26
+ not see because the lockfile changed outside `in-scope`.
27
+
28
+ **Filler looks like.** "Consider adding rate limiting" on a change that touches
29
+ no network surface. Restating a hardening-checklist item the security-review
30
+ already scored `pass`, with no evidence from the diff that the score is wrong.
31
+
32
+ **Severity.** Anything exploitable by a non-authenticated caller, or that
33
+ crosses a tenant boundary, is `critical` or `high` — never `medium` because it
34
+ is unlikely. Likelihood belongs in the severity rationale, not in the level.
35
+
36
+ ---
37
+
38
+ ## scope-drift
39
+
40
+ **What it means.** The diff touches things `change-spec.in-scope` does not
41
+ name, or the change quietly grew a second purpose.
42
+
43
+ **Real findings look like.** A refactor of a shared util that no phase in
44
+ `plan.md` asked for. A migration that alters a table outside the change's
45
+ module. A "while I was in there" rename that makes the diff unreviewable. A new
46
+ dependency added to satisfy a convenience the spec never asked for.
47
+
48
+ **Filler looks like.** Flagging a file the change-spec's globs do cover on a
49
+ literal reading. Flagging generated files, lockfiles, or formatter output that
50
+ the repo's conventions produce automatically.
51
+
52
+ **Severity.** Usually `medium`. It escalates when the drift lands in a module
53
+ with its own module-spec invariants, because then it is also `invariant-breach`
54
+ and CI's scope gate did not catch it.
55
+
56
+ ---
57
+
58
+ ## invariant-breach
59
+
60
+ **What it means.** Something the change-spec or the relevant module-spec
61
+ declares as always-true is no longer always-true after this diff.
62
+
63
+ **Real findings look like.** A module-spec that says "no direct SQL outside this
64
+ module" and a new query in an adjacent file. A change-spec invariant about
65
+ ordering that the new code path can violate under concurrency. An invariant
66
+ that is still true but is now enforced by convention where it used to be
67
+ enforced by a type or a constraint.
68
+
69
+ **Filler looks like.** Paraphrasing an invariant back and calling it a risk.
70
+ Naming an invariant the diff does not touch.
71
+
72
+ **Severity.** `high` by default — an invariant that can break silently is worse
73
+ than a bug that throws. Drop to `medium` only when the breach is caught by an
74
+ existing test that will fail loudly.
75
+
76
+ ---
77
+
78
+ ## spec-compliance
79
+
80
+ **What it means.** The diff and the artifacts disagree. This is the category
81
+ with the widest surface and the one most often under-used.
82
+
83
+ **Real findings look like.** An acceptance criterion in the change-spec with no
84
+ corresponding behaviour in the diff. An edge case the `test-plan` promised and
85
+ the diff did not land. A tenant-isolation test named in the test-plan and
86
+ absent from the branch. An invariant in the change-spec that
87
+ `verification.test-plan-coverage` maps to no observed test. Behaviour the
88
+ implementation added that no artifact anticipated — undeclared scope is a
89
+ compliance gap in the other direction. **Any test-file modification without its
90
+ canonical authorization echo** (see `KERNEL.md` § Test immutability); this one
91
+ is mandatory and never subject to judgment about whether it is worth filing.
92
+ When `resolves-tech-debt` is non-empty, any Acceptance bullet the diff leaves
93
+ `partial` or `not-satisfied` (AR-07).
94
+
95
+ **Filler looks like.** "The spec could have been clearer." Style disagreements
96
+ with the plan's phase decomposition.
97
+
98
+ **Severity.** Unauthorized test changes: `high` minimum, `critical` for bulk
99
+ snapshot updates. Unmet Acceptance bullets: `high`, `critical` once the change
100
+ is at `ready-to-ship`. Everything else: judgment.
101
+
102
+ ---
103
+
104
+ ## data-integrity
105
+
106
+ **What it means.** Data can end up wrong, lost, or visible to the wrong tenant
107
+ without anybody attacking anything.
108
+
109
+ **Real findings look like.** A migration that adds a NOT NULL column without a
110
+ default or a backfill. A destructive DDL with no stated rollback. An RLS policy
111
+ whose predicate does not match the tenancy model in `data-architecture.md`. A
112
+ tenant-scoped RPC missing its `tenant_id` filter. A write path that is
113
+ not idempotent under retry. A pgvector retrieval that filters after the
114
+ similarity search rather than inside it.
115
+
116
+ **Filler looks like.** Speculating about scale on a table with a bounded row
117
+ count. Recommending an index with no query to justify it.
118
+
119
+ **Severity.** Cross-tenant leakage is `critical` — always, including when the
120
+ leak needs an unlikely sequence to trigger. Irreversible data loss is
121
+ `critical`. Recoverable inconsistency is `high`.
122
+
123
+ ---
124
+
125
+ ## code-quality
126
+
127
+ **What it means.** The change works and will cost more than it should to live
128
+ with. The weakest category and the easiest to pad, which is why it is worth
129
+ being strict about what belongs here.
130
+
131
+ **Real findings look like.** A third copy of logic that already exists twice,
132
+ where the copies have started to disagree. Error handling that swallows a
133
+ failure the caller needs. A function whose behaviour depends on call order with
134
+ nothing that documents or enforces it. A performance budget in the test-plan
135
+ that the implementation cannot meet as written.
136
+
137
+ **Filler looks like.** Naming preferences. Suggested comments. Requests to
138
+ extract a function that is used once. Anything a formatter or a linter would
139
+ have said, since `run-gates.sh` already ran.
140
+
141
+ **Severity.** `low` or `medium`. A `code-quality` finding at `high` is usually
142
+ mis-categorized — look again at whether it is really `invariant-breach` or
143
+ `data-integrity`.
144
+
145
+ ---
146
+
147
+ ## Resolution routing
148
+
149
+ Independent of category:
150
+
151
+ - `commit:<hash>` — fixed on the branch. The hash must exist there (AR-04).
152
+ - `tech-debt:<id>` — acknowledged and deferred, against a tech-debt artifact
153
+ that exists at `open` or `in-progress` at write time (AR-05).
154
+ - `justified-in-prose` — the finding is real and the right answer is to explain
155
+ why nothing changes. Reserved for `low` severity. A `high`-severity finding
156
+ routed here is a halt, and a security or tenant-isolation finding routed here
157
+ is a halt with escalation.