Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

hstack

Package Overview
Dependencies
Maintainers
1
Versions
6
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

hstack - npm Package Compare versions

Comparing version
0.1.0
to
0.2.0
+15
-9
CHANGELOG.md

@@ -5,5 +5,17 @@ # Changelog

## [Unreleased] — 0.1.0
## [Unreleased]
_Nothing yet._
## [0.2.0] - 2026-05-22
### Added
- **Category-B `enables` carve-out on change-spec.** New frontmatter fields `enables: []` and `enabled-by: []` separate foundational-prerequisite production code (Category B) from engineering-only `internal-tooling: true` (Category A). SP-09 expands to `user-stories` non-empty UNLESS `internal-tooling: true` UNLESS `enables` non-empty. New validator rules SP-13 (mutual exclusion of A and B) and SP-14 (`enables ↔ enabled-by` reciprocity). `/hstack:change-new` reconciles forward references at downstream scaffold time; `/hstack:help` renders the audit chain; `/hstack:ship` gains GT-12. Backwards-compatible — no schema-version bump; existing specs without the fields validate unchanged. See [ADR-0003](adr/ADR-0003-category-b-enables-field.md).
### Infrastructure
- **GitHub Action `publish.yml`.** Pushes to npm on `v*` tags. Verifies tag matches `package.json` version, publishes with `--provenance` and `--access public`. Requires repo secret `NPM_TOKEN`.
## [0.1.0] - 2026-05-22
### Added
- **CLI installer.** Three commands shipped on the `hstack` npm package:

@@ -22,12 +34,6 @@ - `hstack init` — copies `template/` into `<consumer>/hstack/`, wires `.claude/agents` (dir-level symlink) and `.claude/skills/hstack-*` (per-skill symlinks), appends the kernel-import line to `<consumer>/CLAUDE.md`, adds `**/.telemetry/` to `<consumer>/.gitignore`, stamps `<consumer>/hstack/VERSION`. Flags: `--yes`, `--force`, `--dry-run`.

### Known limitations (v0.1)
### Notes
- 16 Skills, 10 subagents, 25 templates, kernel — first published npm release.
- No local-edit detection: `hstack update` overwrites consumer hand-edits without warning; the diff preview is the only signal. Hash-manifest mode is planned for v0.2.
- No CI for the CLI itself; coverage is manual smoke tests across happy / negative paths plus one real consumer (moso-app).
- No migration scripts: template schema changes between versions need CHANGELOG-driven manual action.
## [0.1.0] - 2026-05-22
Initial pre-release. Vendored / symlinked distribution only; npm CLI in progress.
- 16 Skills, 10 subagents, 25 templates, kernel.
- See repo history prior to this changelog for detail.
{
"name": "hstack",
"version": "0.1.0",
"version": "0.2.0",
"description": "A spec-driven engineering workflow that ships as Claude Code Skills and subagents.",

@@ -5,0 +5,0 @@ "keywords": [

@@ -80,3 +80,8 @@ ---

- Reference, do not duplicate. When a change-spec needs to cite a persona, story, or ADR, write the id, not the prose.
- Maintain reciprocity. When `tech-debt.origin` is a change-spec id, ensure that change-spec's `creates-tech-debt` array includes the new tech-debt id (TD-01). When writing `tech-debt.resolved-by`, ensure that change-spec's `resolves-tech-debt` array includes this tech-debt id (TD-04). Same for ADR `supersedes` / `superseded-by`. The reciprocal pair always lands in a single auto-commit; one-sided writes are not permitted.
- Maintain reciprocity. When `tech-debt.origin` is a change-spec id, ensure that change-spec's `creates-tech-debt` array includes the new tech-debt id (TD-01). When writing `tech-debt.resolved-by`, ensure that change-spec's `resolves-tech-debt` array includes this tech-debt id (TD-04). Same for ADR `supersedes` / `superseded-by`. For `change-spec.enables` (Category B foundational-prerequisite linkage), when writing or editing the `enables` array, also write the reciprocal `enabled-by` entry on each downstream change-spec named in the array (SP-14). Forward references — `enables` entries pointing at a not-yet-scaffolded id — are permitted; `/hstack:change-new` reconciles the reciprocal `enabled-by` when the downstream is later scaffolded. The reciprocal pair always lands in a single auto-commit; one-sided writes are not permitted.
- **The no-story interview branch.** When a change-spec's `user-stories` array would be empty, do not silently set `internal-tooling: true` (the old default). Ask the engineer: "This change has no linked user story. Which category applies?
- **(A) Internal tooling** — engineering-only code that never ships on a user path (scripts, dev dashboards, CI tooling). Sets `internal-tooling: true`.
- **(B) Foundational prerequisite** — production code that ships, but user value lives in a named downstream change-spec that consumes this one's output (schema before UI, plumbing before consumer). Sets `enables: [<downstream-id>, ...]` and writes the reciprocal `enabled-by` on each downstream spec.
- **(C) Neither** — there is actually a user story; let's draft it via `/hstack:story-draft`."
Categories A and B are mutually exclusive (SP-13). If the engineer is uncertain, walk the audit-query test: "After this ships, if someone asks 'what's the user value of this change?', is the honest answer (A) 'none, it's internal', (B) 'it teed up change-spec X', or (C) 'this user-facing thing'?"
- **Mechanical operations are not your job.** Per the kernel's Mechanical operations section, status flips, reciprocal writes, Resolution Log appends, and `updated:` date bumps are performed by Skills directly in the main Claude Code session, not by this subagent. The four resolution Skills (`/hstack:tech-debt-resolve`, `/hstack:tech-debt-wontfix`, `/hstack:tech-debt-stale`, `/hstack:finalize`) own those writes themselves. If you are invoked for a mechanical operation, refuse and direct the engineer to run the appropriate Skill — the invocation is a workflow error, not a request to fulfil.

@@ -100,3 +105,3 @@ - ADR ids are sequential. Read the highest existing `ADR-NNNN` and increment by one. No gaps, no reuse.

- All universal frontmatter (id, type, status, owner, created, updated, schema-version), all change-spec-specific fields (area, surfaces, user-stories, related-spec, in-scope, out-of-scope), and any conditional fields populated.
- All universal frontmatter (id, type, status, owner, created, updated, schema-version), all change-spec-specific fields (area, surfaces, user-stories, related-spec, in-scope, out-of-scope, internal-tooling, enables, enabled-by), and any conditional fields populated. Exactly one of {`user-stories` non-empty, `internal-tooling: true`, `enables` non-empty} must hold (SP-09); `internal-tooling: true` and `enables` non-empty must not both hold (SP-13).
- All ten sections from the schema, with Invariants holding three or more bullets and Open Questions either resolved or explicitly punted.

@@ -103,0 +108,0 @@ - A passing validator run.

@@ -63,12 +63,14 @@ ---

3. **Seed `spec.md`.** Read `hstack/templates/change-spec.md`, instantiate the frontmatter with `id`, `type: change-spec`, `status: draft`, `owner` (from `git config user.name` or `hstack/config.yaml`'s default owner), `area: <area>`, `related-spec: <area>`, `created` and `updated` set to today, `schema-version: 1`. Leave `surfaces`, `user-stories`, `in-scope`, `out-of-scope`, `related-adrs`, `creates-tech-debt`, `parent-change`, `threat-model-delta`, `internal-tooling`, `trivial` as their template defaults (typically empty arrays or `null`). Leave every prose section empty, with the template's interview-prompt comments intact for `spec-author` to consume.
3. **Seed `spec.md`.** Read `hstack/templates/change-spec.md`, instantiate the frontmatter with `id`, `type: change-spec`, `status: draft`, `owner` (from `git config user.name` or `hstack/config.yaml`'s default owner), `area: <area>`, `related-spec: <area>`, `created` and `updated` set to today, `schema-version: 1`. Leave `surfaces`, `user-stories`, `in-scope`, `out-of-scope`, `related-adrs`, `creates-tech-debt`, `parent-change`, `threat-model-delta`, `internal-tooling`, `enables`, `enabled-by`, `trivial` as their template defaults (typically empty arrays or `null`). Leave every prose section empty, with the template's interview-prompt comments intact for `spec-author` to consume.
4. **Validate.** Run `{{TODO-SCRIPT: hstack/scripts/validate-spec.ts}}` against the seeded file. Validation at `status: draft` is permissive — empty arrays and empty prose are allowed at draft — but the universal floor (FM-01) must pass.
4. **Forward-reference reconciliation for `enables` chains.** Grep every existing `hstack/specs/changes/*/spec.md` for the new `<id>` in `enables:` arrays. For each match: the matched (upstream) spec already declares this new (downstream) spec as a Category-B enabler. Per SP-14, write the reciprocal `enabled-by: [<upstream-id>, ...]` array on the newly-seeded spec in the same scaffold commit (this is the atomic-pair guarantee — both halves land together). When no match exists, leave `enabled-by: []`. Read-only on the upstream spec — its `enables` array was already written when the upstream was authored; no edit there. This is a mechanical operation per the kernel's Mechanical-operations section; no subagent is invoked. Surface the reconciliation to the engineer: "Detected upstream spec(s) declaring `enables: [<id>]` — populating `enabled-by` reciprocally."
5. **Offer branch creation.** Run `git branch --show-current`. If the current branch is `main` (or the configured default), ask the engineer: "You're on `<current-branch>`. Convention is one branch per change-spec — create `change/<id>` from here and check out before the scaffold commits? [Y/n/type-different-name]". Default Yes. On confirmation, run `git checkout -b change/<id>` BEFORE step 6 so the scaffold commit lands on the correct branch from the start. If the engineer declines or names a different branch, honor the choice and continue on the chosen branch. If the current branch is anything other than the configured default (i.e., already on a feature branch), do nothing — the engineer has a deliberate branching strategy and the Skill respects it.
5. **Validate.** Run `{{TODO-SCRIPT: hstack/scripts/validate-spec.ts}}` against the seeded file. Validation at `status: draft` is permissive — empty arrays and empty prose are allowed at draft — but the universal floor (FM-01) must pass.
6. **Auto-commit.** Commit message: `chore(change-new): scaffold <id>`.
6. **Offer branch creation.** Run `git branch --show-current`. If the current branch is `main` (or the configured default), ask the engineer: "You're on `<current-branch>`. Convention is one branch per change-spec — create `change/<id>` from here and check out before the scaffold commits? [Y/n/type-different-name]". Default Yes. On confirmation, run `git checkout -b change/<id>` BEFORE step 7 so the scaffold commit lands on the correct branch from the start. If the engineer declines or names a different branch, honor the choice and continue on the chosen branch. If the current branch is anything other than the configured default (i.e., already on a feature branch), do nothing — the engineer has a deliberate branching strategy and the Skill respects it.
7. **Surface next steps.** Print a one-line instruction directing the engineer to invoke `spec-author` (or `hstack-story-draft` first if the change is user-facing and no story exists yet).
7. **Auto-commit.** Commit message: `chore(change-new): scaffold <id>`.
8. **Surface next steps.** Print a one-line instruction directing the engineer to invoke `spec-author` (or `hstack-story-draft` first if the change is user-facing and no story exists yet). If the change has Category-B `enabled-by` populated from step 4, also note the upstream linkage so the engineer is reminded which prerequisite this realizes.
The Skill does not invoke any subagent. Scaffolding is mechanical and the engineer's subsequent moves vary by change.

@@ -75,0 +77,0 @@

@@ -69,3 +69,8 @@ ---

- Read `hstack/config.yaml` for `init-status` and the active MCP set.
- Glob `hstack/specs/changes/*/spec.md`. For each, read frontmatter (`id`, `status`, `surfaces`, `owner`, `internal-tooling`, `trivial`, `parent-change`). Filter to non-terminal status (anything before `shipped`, `archived`).
- Glob `hstack/specs/changes/*/spec.md`. For each, read frontmatter (`id`, `status`, `surfaces`, `owner`, `internal-tooling`, `enables`, `enabled-by`, `trivial`, `parent-change`). Filter to non-terminal status (anything before `shipped`, `archived`).
- For each in-flight change, classify and surface the no-story carve-out when present:
- Category A (`internal-tooling: true`) → annotate "[Category A — internal tooling]".
- Category B (`enables` non-empty) → annotate "[Category B — enables → <comma-separated downstream ids>]". For each downstream id, also note its on-disk status (or "not yet scaffolded" — informational, not a blocker; reconciliation happens at downstream `/hstack:change-new` time).
- `enabled-by` non-empty → annotate "[Realizes ← <comma-separated upstream ids>]" so the reverse direction of the chain is visible.
- SP-13 violation (both `internal-tooling: true` AND `enables` non-empty) → flag explicitly as an error.
- For each in-flight change, compute the **next blocking action**:

@@ -72,0 +77,0 @@ - `status: draft` → "Author via `spec-author` directly (or run `/hstack:story-draft` first if user-facing)."

@@ -50,3 +50,3 @@ ---

Invoke once the change-spec is at `status: ready-for-implementation` (which means every upstream gate is terminal: test-plan at `passed` or `concerns-acknowledged`, plan at `ready`, security-review at `passed` or `concerns-acknowledged`, data-review at `passed` or `concerns-acknowledged` when applicable, ui-brief at `drafted` and figma-handoff at `ready` when applicable, user-stories non-empty unless internal-tooling). One invocation per phase. Re-invoke for each subsequent phase.
Invoke once the change-spec is at `status: ready-for-implementation` (which means every upstream gate is terminal: test-plan at `passed` or `concerns-acknowledged`, plan at `ready`, security-review at `passed` or `concerns-acknowledged`, data-review at `passed` or `concerns-acknowledged` when applicable, ui-brief at `drafted` and figma-handoff at `ready` when applicable, user-stories non-empty UNLESS `internal-tooling: true` UNLESS `enables` non-empty). One invocation per phase. Re-invoke for each subsequent phase.

@@ -68,3 +68,3 @@ ## Inputs

- ui-brief at `status: drafted` and figma-handoff at `status: ready` when `surfaces` includes `ui`.
- User-stories non-empty unless `internal-tooling: true`.
- User-stories non-empty UNLESS `internal-tooling: true` (Category A — engineering-only) UNLESS `enables` non-empty (Category B — foundational prerequisite; user value lives in the named downstream change-spec). SP-13: `internal-tooling: true` and `enables` non-empty are mutually exclusive; if both are set, halt with an SP-13 violation message and direct the engineer to `spec-author` to pick one.
- The relevant module-spec at `status: current`.

@@ -71,0 +71,0 @@ - **Branch state.** Run `git branch --show-current`. If the current branch is `main` (or the configured default) AND `change-spec.trivial` is not `true`, HARD HALT with: "Refusing to implement on `main` — change-spec `<id>` is not marked trivial. Check out `change/<id>` first, or run `/hstack:branch <id>` to create-and-switch." Trivial changes (`trivial: true`) may proceed on main per the kernel's trivial-changes carve-out. This check enforces the kernel's branch-hygiene contract at the last moment before code lands.

@@ -50,3 +50,3 @@ ---

- Verify the change folder `hstack/specs/changes/<change-id>/` exists.
- Verify the change-spec exists. Read its `surfaces`, `internal-tooling`, `trivial` flags to know which conditional gates apply.
- Verify the change-spec exists. Read its `surfaces`, `internal-tooling`, `enables`, `enabled-by`, `trivial` flags to know which conditional gates apply.

@@ -59,3 +59,3 @@ 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.

2. **Compute the eleven-gate scorecard.** Run `{{TODO-SCRIPT: hstack/scripts/compute-merge-readiness.ts}}` against the artifact set, or inline the equivalent logic:
2. **Compute the twelve-gate scorecard.** Run `{{TODO-SCRIPT: hstack/scripts/compute-merge-readiness.ts}}` against the artifact set, or inline the equivalent logic:
- GT-01: spec presence — change folder exists with non-draft change-spec, or PR carries `trivial: true`.

@@ -68,6 +68,7 @@ - GT-02: diff within scope — every file in the PR diff (against the merge target) is a subset of `change-spec.in-scope`.

- GT-07: ui-brief at `drafted` and figma-handoff at `ready` (when applicable).
- GT-08: `user-stories` non-empty (unless `internal-tooling: true`).
- 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.
- GT-09: every cross-reference rule (CG-01..CG-04) passes.
- 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.
- 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`.
- 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.

@@ -84,3 +85,3 @@ 3. **Frontmatter validation.** Run `{{TODO-SCRIPT: hstack/scripts/validate-spec.ts}}` across every artifact. Any FM-* or per-type validation failure blocks ship.

- 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`.
- Scorecard summary: the eleven-gate table from step 2.
- Scorecard summary: the twelve-gate table from step 2.

@@ -129,5 +130,5 @@ 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.

- Never silently pass a gate. Every FAIL names the artifact and field.
- Never collapse the eleven gates into a single PASS / FAIL. The scorecard is per-gate.
- Never collapse the twelve gates into a single PASS / FAIL. The scorecard is per-gate.
- 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.
- Never extend `change-spec.in-scope` to make GT-02 pass — the scope amendment goes through `spec-author`, not this Skill.
- 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.
---
name: hstack-story-draft
description: |
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 has `internal-tooling: true`. Examples:
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:

@@ -11,3 +11,3 @@ <example>

<commentary>
Stories are gated by SP-09 (`user-stories` non-empty unless `internal-tooling: true`). The Skill produces the story before the change-spec can advance, and writes the reciprocal `linked-change-specs` entry on the story.
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.
</commentary>

@@ -40,3 +40,3 @@ </example>

`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`.
`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.

@@ -61,3 +61,5 @@ ## When to invoke

- 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.
- If the parent change-spec carries `internal-tooling: true`, halt and surface a "story not required" message.
- 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)."
- 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."
- 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`."
- Read `hstack/context/vision.md`, `mvp-scope.md`, and the personas index (required by `product-manager`'s session-start protocol).

@@ -110,3 +112,3 @@

- **Parent change-spec is `internal-tooling: true`.** Halt early; the story is unnecessary.
- **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`.
- **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.

@@ -113,0 +115,0 @@ - **Validator fails.** Halt and surface; the engineer rewords the failing field.

@@ -55,3 +55,3 @@ ---

- Verify the `design-system-version` declared in `hstack/config.yaml` is current and is what the brief will reference. Halt on drift.
- 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.
- 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`.

@@ -58,0 +58,0 @@ ## Orchestration steps

@@ -142,2 +142,9 @@ ---

**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):
- **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.
- **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`.
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.
---

@@ -144,0 +151,0 @@

@@ -16,3 +16,5 @@ ---

revisits-change: [] # change-spec ids this change is filed to repair (defects, regressions, missed findings). Informational, not gating.
internal-tooling: false
internal-tooling: false # Category A — engineering-only, never on a user path
enables: [] # Category B — downstream change-spec ids that realize user value from this change
enabled-by: [] # reciprocal of upstream specs' `enables` arrays; written by /hstack:change-new at scaffold time
trivial: false

@@ -19,0 +21,0 @@ in-scope: [] # repo-relative globs; must be non-empty

@@ -1,1 +0,1 @@

0.1.0
0.2.0