+15
-9
@@ -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. |
+1
-1
| { | ||
| "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
@@ -1,1 +0,1 @@ | ||
| 0.1.0 | ||
| 0.2.0 |
673379
1.36%