heyspex 0.2.6 → 0.2.7

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 (85) hide show
  1. package/bin/hs.mjs +85 -25
  2. package/bin/spexcode.mjs +31 -0
  3. package/dev-factory/lib/local-firecrawl.mjs +294 -0
  4. package/dev-factory/lib/seat-context.mjs +20 -4
  5. package/dev-factory/model-catalog.json +222 -222
  6. package/dev-factory/spexcrawl/firecrawl-cli.mjs +755 -0
  7. package/dist/cli/hs-connect.mjs +20 -2
  8. package/dist/cli/hs-defer.mjs +19 -1
  9. package/dist/cli/hs-index-rebuild.mjs +19 -1
  10. package/dist/cli/hs-index-reconcile.mjs +19 -1
  11. package/dist/cli/hs-init.mjs +2 -2
  12. package/dist/cli/hs-install.mjs +2 -2
  13. package/dist/cli/hs-join.mjs +20 -2
  14. package/dist/cli/hs-next-launch.mjs +89 -10
  15. package/dist/cli/hs-spawn.mjs +27 -1
  16. package/dist/cli/hs-vault-pull.mjs +19 -1
  17. package/dist/cli/hs-vault-push.mjs +19 -1
  18. package/dist/cli/hs-vault.mjs +21 -7
  19. package/dist/cli/hs-workspace.mjs +19 -1
  20. package/dist/cli-templates/customer-vault/specs/entity-model.md +1728 -1728
  21. package/dist/cli-templates/customer-vault/specs/protocols/canonical-frontmatter-templates.md +464 -464
  22. package/dist/cli-templates/customer-vault/specs/protocols/destructive-action-checklist.md +122 -122
  23. package/dist/cli-templates/customer-vault/specs/protocols/memory-protocol.md +46 -46
  24. package/dist/cli-templates/customer-vault/specs/protocols/spec-hierarchy.md +530 -530
  25. package/dist/cli-templates/customer-vault/specs/protocols/spec-summary-authoring.md +83 -83
  26. package/dist/cli-templates/harness-commands/claude/hs-backfill.md +28 -28
  27. package/dist/cli-templates/harness-commands/claude/hs-capture.md +62 -62
  28. package/dist/cli-templates/harness-commands/claude/hs-defer.md +29 -29
  29. package/dist/cli-templates/harness-commands/claude/hs-ground.md +30 -30
  30. package/dist/cli-templates/harness-commands/claude/hs-next.md +29 -29
  31. package/dist/cli-templates/harness-commands/claude/hs-promote.md +70 -70
  32. package/dist/cli-templates/harness-commands/claude/hs-push.md +35 -35
  33. package/dist/cli-templates/harness-commands/claude/hs-qc.md +27 -27
  34. package/dist/cli-templates/harness-commands/claude/hs-res.md +20 -8
  35. package/dist/cli-templates/harness-commands/claude/hs-resolve.md +23 -23
  36. package/dist/cli-templates/harness-commands/claude/hs-view.md +28 -28
  37. package/dist/cli-templates/harness-commands/claude/kill-ports.md +32 -32
  38. package/dist/cli-templates/harness-commands/claude/prototype.md +91 -91
  39. package/dist/cli-templates/harness-commands/cursor/hs-backfill.md +22 -22
  40. package/dist/cli-templates/harness-commands/cursor/hs-capture.md +56 -56
  41. package/dist/cli-templates/harness-commands/cursor/hs-defer.md +23 -23
  42. package/dist/cli-templates/harness-commands/cursor/hs-ground.md +24 -24
  43. package/dist/cli-templates/harness-commands/cursor/hs-next.md +23 -23
  44. package/dist/cli-templates/harness-commands/cursor/hs-promote.md +64 -64
  45. package/dist/cli-templates/harness-commands/cursor/hs-push.md +29 -29
  46. package/dist/cli-templates/harness-commands/cursor/hs-qc.md +21 -21
  47. package/dist/cli-templates/harness-commands/cursor/hs-res.md +20 -8
  48. package/dist/cli-templates/harness-commands/cursor/hs-resolve.md +17 -17
  49. package/dist/cli-templates/harness-commands/cursor/hs-view.md +23 -23
  50. package/dist/cli-templates/harness-commands/cursor/kill-ports.md +26 -26
  51. package/dist/cli-templates/harness-commands/cursor/prototype.md +86 -86
  52. package/docs/copilot-plugin-install.md +7 -0
  53. package/docs/mcp-client-config.md +6 -0
  54. package/package.json +7 -2
  55. package/plugins/heyspex/README.md +6 -0
  56. package/plugins/heyspex/bin/launch-mcp.mjs +17 -0
  57. package/plugins/heyspex/hooks/hooks.json +418 -418
  58. package/scripts/claude-hooks/_intent-classifier-semantic.mjs +2 -2
  59. package/scripts/hs-companion.mjs +38 -8
  60. package/scripts/lib/companion-install.mjs +768 -0
  61. package/scripts/lib/customer-kit.mjs +5 -3
  62. package/scripts/lib/hs-companion-settings.mjs +1 -0
  63. package/scripts/lib/hs-source-push.mjs +386 -386
  64. package/spexharness/doctrine/baseline/SPEXCODE-DOCTRINE.baseline.json +29 -29
  65. package/spexharness/src/adapters/copilot-models.mjs +179 -0
  66. package/spexharness/src/adapters/harness-detect.mjs +148 -0
  67. package/spexharness/src/gates/directive-obligation-gate.decide.mjs +132 -132
  68. package/spexharness/src/gates/vault-write-tier-guard.decide.mjs +153 -125
  69. package/spexharness/src/seat-context.mjs +10 -5
  70. package/spexharness/src/session-bootstrap.mjs +6 -3
  71. package/vendor/heyspex-mcp/cli.mjs +6 -1
  72. package/vendor/heyspex-mcp/config-seed.yaml +15 -15
  73. package/vendor/heyspex-mcp/dev-factory-overlay.mjs +142 -0
  74. package/vendor/heyspex-mcp/memory-tools.mjs +523 -523
  75. package/vendor/heyspex-mcp/on-deck-scheduler.mjs +1708 -1708
  76. package/vendor/heyspex-mcp/procedure-handlers.mjs +327 -327
  77. package/vendor/heyspex-mcp/procedure-tools.mjs +1209 -1181
  78. package/vendor/heyspex-mcp/research-tools.mjs +22 -2
  79. package/vendor/heyspex-mcp/seat-assign-cli.mjs +242 -0
  80. package/vendor/heyspex-mcp/seat-assignment-rules.mjs +96 -0
  81. package/vendor/heyspex-mcp/seat-assignments-client.mjs +265 -0
  82. package/vendor/heyspex-mcp/seat-defaults-cli.mjs +45 -0
  83. package/vendor/heyspex-mcp/seat-defaults.mjs +232 -0
  84. package/vendor/heyspex-mcp/seat-identity.mjs +171 -0
  85. package/vendor/heyspex-mcp/vault-io.mjs +720 -720
@@ -1,1728 +1,1728 @@
1
- ---
2
- id: HS-L2-ENTITY
3
- name: HeySpex Entity Model
4
- type: l2
5
- status: draft
6
- priority: 0
7
- owner: TODO — your name/handle
8
- created: 2026-05-14
9
- updated: 2026-05-18
10
- tags: "[foundation, l2, entity-model, schema]"
11
- ---
12
-
13
-
14
-
15
-
16
-
17
-
18
- # HeySpex Entity Model
19
-
20
- > [!important] Constitutional dependency
21
- > Every constraint in this document derives from a numbered principle in heyspex-north-star. If a future change to this document contradicts the L1 spec, the change is wrong. When the L1 changes, this document updates downstream.
22
-
23
- This L2 spec defines the shape of every artifact that lives in a HeySpex vault: the entity types, their frontmatter schemas, their on-disk layout, the dependency relations between them, and the wikilink resolution rules. It is the contract that the console, the CLI (`hs`), and any AI agent must satisfy when reading or writing the vault.
24
-
25
- ## Vocabulary
26
-
27
- - **Vault.** A folder of plain-text files under git. The on-disk layout is canonical. A single Builder owns one or more vaults; this document specifies the structure of one vault.
28
- - **Spec.** A single markdown file in the vault. Every spec has YAML frontmatter and a markdown body. The frontmatter declares the spec's type and identity.
29
- - **Entity type.** The value of the `type:` field in frontmatter. Determines which folder the spec lives in, which other fields are required, and how the spec is rendered in the console.
30
- - **Hierarchy level.** L1 / L2 / L3. Vision → architecture → use-case. A different axis from entity type. Some entity types are level-tagged (`type: l1`, `type: l2`, `type: l3`); most are not.
31
- - **Wikilink.** A reference of the form `[[link-target]]`, resolved by filename match across the vault.
32
- - **Memory (conceptual layer).** Durable project knowledge surfaced together in the console: `memory-lesson`, `memory-decision`, `memory-reference`, `memory-ux-principle`, `memory-ui-pattern`, and `protocol`. Protocols are filed under `specs/protocols/` on disk but are a **subsection of Memory** in navigation and in `hs prime` — not a peer of Memory beside Epics/Tasks. See memory-protocols-subsection-of-memory. Work to *create* or change a protocol remains `type: epic` / `task`.
33
-
34
- ## Vault root layout
35
-
36
- ```
37
- specs/
38
- ├── heyspex-north-star.md # the L1; one file
39
- ├── entity-model.md # this document; L2
40
- ├── architecture-foundations.md # L2 sibling (renamed 2026-05-31 from architecture-pillars.md to disambiguate from the `type: pillar` entity)
41
- ├── principles.md # L2 (expansion of L1's principle list)
42
- ├── glossary.md # L2 (authoritative term dictionary) — North Star tier
43
- ├── adr/ # one ADR per file
44
- │ ├── adr-0001-vault-format.md
45
- │ ├── adr-0002-derived-index.md
46
- │ └── ...
47
- ├── use-cases/ # one L3 use-case spec per file
48
- │ ├── uc-capture-intent.md
49
- │ ├── uc-decompose-intent.md
50
- │ └── ...
51
- ├── tasks/ # task / feature / bug specs
52
- │ ├── HS-ZF0T-TASK-<slug>.md # legacy sequential, or HS-K7M2-<slug>.md (random)
53
- │ └── ...
54
- ├── milestones/ # milestone specs (the grouping above epic)
55
- │ └── HS-MIL-<suffix>-<slug>.md # e.g. HS-IART-MIL-console-ia-restructure.md
56
- ├── pillars/ # type: pillar — perpetual categorical containers (Scaling, Reliability, DX, etc.)
57
- │ └── HS-PILR-<slug>.md # canonical Pillars; customers may also create HS-PILR-<suffix>-<slug>.md
58
- ├── epics/ # epic specs
59
- │ └── HS-0001-EPIC-<slug>.md # legacy, or HS-EPIC-X9P4-<slug>.md (random)
60
- ├── branches/ # branch-brief specs (one per git branch)
61
- │ └── <branch-slug>.md
62
- ├── memory/
63
- │ ├── lessons/ # memory-lesson specs
64
- │ │ └── <slug>.md
65
- │ ├── decisions/ # memory-decision specs
66
- │ │ └── <slug>.md
67
- │ ├── references/ # memory-reference specs
68
- │ │ └── <slug>.md
69
- │ ├── ux-principles/ # memory-ux-principle specs (cross-brand interaction rules)
70
- │ │ └── <slug>.md
71
- │ └── ui-patterns/ # memory-ui-pattern specs (reusable interaction/visual rules)
72
- │ └── <slug>.md
73
- ├── protocols/ # type: protocol — Memory subsection on disk (how-we-work rules)
74
- │ └── <slug>.md # (not epic/task work; those stay in epics/ and tasks/)
75
- ├── reviews/ # type: review — HS-REV-<suffix>-<slug>.md companions for in_review parent specs
76
- │ └── HS-REV-<suffix>-<slug>.md
77
- ├── business/ # business model / marketing / positioning / pricing
78
- │ ├── positioning/ # business-positioning specs
79
- │ │ └── <slug>.md
80
- │ ├── pricing/ # business-pricing specs
81
- │ │ └── <slug>.md
82
- │ ├── marketing/ # business-marketing specs
83
- │ │ └── <slug>.md
84
- │ └── model/ # business-model specs
85
- │ └── <slug>.md
86
- ├── canvases/ # JSON Canvas design files + .heyspex.yaml sidecars ([[adr-0013-canvas-vault-format-and-obsidian-interop]])
87
- │ ├── system-design.canvas
88
- │ ├── system-design.heyspex.yaml
89
- │ ├── diagrams/ # per-spec editable design diagrams ([[spec-diagram-design-vs-as-built-canvas]])
90
- │ │ └── <spec-basename>-design.canvas
91
- │ ├── as-built/ # sealed shipped spec diagrams (read-only in HeySpex)
92
- │ │ └── <spec-basename>.canvas
93
- │ └── ...
94
- ├── roadmaps/ # phased roadmap specs (orchestration over tasks/epics)
95
- │ └── HS-ROADMAP-<slug>.md
96
- ├── roadmap-design-v2/ # Console Roadmap surface v2 design pack (protocol docs; not manifest entities)
97
- │ ├── roadmap-design-v2-index.md # hub
98
- │ ├── roadmap-v2-implementation-checklist.md # printable when coding
99
- │ ├── roadmap-v2-implementation-handoff.md # read order + context
100
- │ ├── roadmap-v2-tabextend-reference.md
101
- │ ├── roadmap-v2-narrative-board-ux.md
102
- │ ├── roadmap-v2-display-titles-and-summaries.md
103
- │ └── roadmap-v2-*.md # see index for full map
104
- ├── companion-research-board/ # Chrome + Companion mirrored research board (protocol docs)
105
- │ ├── companion-research-board-index.md
106
- │ ├── companion-research-board-dual-surface-mirror.md
107
- │ ├── companion-research-board-implementation-checklist.md
108
- │ └── companion-research-board-*.md
109
- ├── future-considerations/ # pre-task research (promote to tasks when scheduled)
110
- │ └── <slug>.md
111
- ├── research/ # audit/research/draft notes
112
- │ ├── HS-XBPJ-RES-bd-feature-inventory.md
113
- │ └── ...
114
- ├── captures/ # concise core capture entries — kind: idea | bug-fix | clip | reference | question | spec-import ([[capture-guardrails]])
115
- │ └── HS-CAP-<suffix>-<slug>.md
116
- ├── flows/ # planning-flow lifecycle diagrams ([[planning-flow-research-epic-lifecycle]])
117
- │ └── HS-FLOW-<NNN>-<slug>.md
118
- ├── drafts/ # ingested-but-untriaged
119
- │ └── <slug>.md
120
- ├── events/ # audit-export JSONL — one file per UTC month ([[HS-FD93-AEHR-EPIC-audit-export-heyspex-yaml-and-heyspex-retirement]])
121
- │ └── YYYY-MM.jsonl # committed snapshot of claim/focus/spec events (git-clone-is-backup)
122
- └── daily/ # optional daily notes
123
- └── 2026-05-14.md
124
- ```
125
-
126
- Two committed siblings live **outside** `specs/` at the repo root, written/owned by the control plane:
127
-
128
- - **`heyspex.yaml`** — the top-level vault marker (`schema_version`, `console_url`, `vault_root`) written by `hs init` (HS-FD93-AEHR-EPIC-audit-export-heyspex-yaml-and-heyspex-retirement §2). `schema_version: 2` = the post-SCAH Neon-control-plane layout; `1` was the implicit pre-SCAH `.heyspex/`-files layout. The web companion reads it to bootstrap a vault.
129
- - **`.heyspex/`** — per-vault config home (adr-0009-heyspex-directory-contract): tracked `config.yaml`, `.gitignore`, `*.example` scaffolding templates, and `focus.yaml` (cross-machine pin SSOT, focus-yaml-is-tracked-pin-ssot). On the **free tier** (no Neon) this directory ALSO holds the live runtime state (`active-claims.json`, `claim-history.jsonl`, `focus-history.jsonl`); on the **paid tier** that state lives in Neon and these files are the export's free-tier source only (HS-7FA9-FREE-EPIC-zero-neon-local-only-tier). The audit export (`specs/events/`) reads whichever store is live for the tier.
130
-
131
- The L1 lives at the vault root as `heyspex-north-star.md`. The other L2 specs (`entity-model.md`, `architecture-foundations.md`, `principles.md`, `glossary.md`) also live at the root because they are foundational — in the console they surface under the **North Star** rail group, not Business (which is scoped to `business-*` types) or Memory (scoped to `memory-*` types). The glossary carries a `marketing` *tag* because marketing copy consumes its definitions, but its `type: l2` keeps it in the foundational tier. Everything else is filed under a folder matching its entity type.
132
-
133
- > [!note] Why folders, not flat?
134
- > Obsidian works with either, and HeySpex's link resolution treats folders as opaque (see [Wikilink resolution rules](#wikilink-resolution-rules)). Folders exist purely for human navigation and for the indexer's "list all <type>" view. Files can be moved between folders without breaking links.
135
-
136
- ## Entity types
137
-
138
- The table below names every entity type in v1 and points at the frontmatter contract. Each type has its own subsection below with the full schema.
139
-
140
- | `type:` value | Folder | Hierarchy level | One-line description |
141
- | --- | --- | --- | --- |
142
- | `l1` | root | L1 | The North Star vision; one per vault. |
143
- | `l2` | root | L2 | Foundational architecture or model documents. |
144
- | `l3` | `use-cases/` | L3 | Use-case specs that elaborate L1 system use cases. |
145
- | `adr` | `adr/` | — | Architectural Decision Record. |
146
- | `task` | `tasks/` | — | The atomic unit of work. Subsumes bd's `task`, `feature`, and `bug` types. |
147
- | `epic` | `epics/` | — | A grouping of related tasks under a common goal. |
148
- | `milestone` | `milestones/` | — | The grouping **above** epic: a coherent multi-epic ship that closes when every child epic closes and the milestone's own acceptance gate is met. Distinct from the legacy `kind: milestone` task discriminator (see [`#### task`](#task)). |
149
- | `diagnose` | `diagnoses/` | — | A user-reported **bug fix** as first-class executable work (claimable, decomposable). Standalone by default — no epic/milestone parent required — with an optional `parent:` (epic or milestone) for organization. Carries `severity:`; surfaces on the Resolution surface as a derived `kind: "bug"` row that links back to the diagnose. Elevates the bug concept that `task` historically subsumed (see [`#### task`](#task)) into its own lane. Ratified by introduction lineage HS-OXBS-BFIX-MIL-bug-fix-diagnose-lane (milestone-precedent, like `milestone`/`pillar`). See HS-AO2F-1KZK-RES-bug-fix-resolution-task-entity-model-and. Proposed (not-yet-ratified) field extensions — a `problem_layer: symptom \| core \| unknown` classifier and an optional bidirectional `caused_by`/`symptoms` link pair — are designed in HS-4QOK-UBJM-RES-diagnose-problem-layer-classification-sy. End-to-end workflow procedure: resolution-diagnose-workflow. |
150
- | `pillar` | `pillars/` | — | Perpetual categorical container that never closes; cross-cutting axis for epics. Every epic carries `pillar:` (required, single primary) + optional `secondary_pillars:`. Canonical Pillars ship pre-loaded with HeySpex; customers may add scoped Pillars. Orthogonal to `milestone` (a milestone is a shippable increment; a Pillar is a perpetual concern). See HS-PILR-pillars (meta-Pillar) and HS-AWDI-PILR-RES-pm-perpetual-container-terminology (research + 2026-05-31 resolution). |
151
- | `agent` | `agents/` | — | A durable, user-authored **agent identity** (the "home where agents live"): name, role, preferred tools, model tier, procedures, and persona body. Compiles to the host-native agent formats (`.claude/agents/*.md`, `.cursor/agents/*.md`) — the vault spec is the SSOT, the generated files are derived. Carries HeySpex-only extras (`lane`, `color`, `owned_paths`, `golden_cases`, and `source_attribution` when imported) stripped on compile. `owned_paths` is the machine-checkable file-ownership set for parallel-agent stomp-detection; `lane` remains a human label. An **imported** agent (community-collection per HS-SURM-TMXZ-EPIC-default-agent-roster-and-import-path AC#2, or own-agent per HS-11ME-1NL4-EPIC-import-and-adopt-your-own-existing-agent) records its provenance in `source_attribution` (collection, url, path, commit, license, imported_at); authored agents omit it. Evergreen (does not close). See HS-XNX5-UQHA-MIL-agent-home. |
152
- | `golden-case` | `agents/golden-cases/` | — | A saved **eval fixture** for one agent identity: the prompt (and optional setup + deterministic graders) the test-before-publish harness runs against a draft agent (HS-R1JM-OAG3-EPIC-agent-testing-harness-try-a-draft-agent). Stored under the `agents/` tree so fixtures version beside the identities they exercise in the same git history. Evergreen (updated in place). Introduced by HS-8AQT-J6FU-TASK-golden-case-vault-artifact-format-and-st. |
153
- | `branch-brief` | `branches/` | — | "What this git branch is trying to do, and how it actually went." Plan-vs-actual + evidence for one branch. |
154
- | `memory-lesson` | `memory/lessons/` | — | "Here's what went wrong and how to avoid it." |
155
- | `memory-decision` | `memory/decisions/` | — | "Here's what we chose and why." |
156
- | `memory-reference` | `memory/references/` | — | "Here's where to look for X." |
157
- | `memory-ui-pattern` | `memory/ui-patterns/` | — | "Here's a reusable console interaction/visual pattern to copy." |
158
- | `memory-ux-principle` | `memory/ux-principles/` | — | "Here's a cross-brand UX rule (what/why) before implementation." |
159
- | `protocol` | `protocols/` | Memory subsection | Repeatable how-we-work rules for agents/humans (session-close, memory capture, MCP usage). Browsed under Memory → Protocols in console; not a peer nav item beside Memory. |
160
- | `review` | `reviews/` | — | Companion fill-in workspace for an `in_review` parent spec — acceptance verification, reviewer notes, memory candidates, decision. One per parent, suffix-matched 1:1. |
161
- | `business-positioning` | `business/positioning/` | — | Brand H1, taglines, audience qualifier, message hierarchy. |
162
- | `business-pricing` | `business/pricing/` | — | Tier tables, AI add-on terms, discount/refund policy. |
163
- | `business-marketing` | `business/marketing/` | — | Channels, launches, campaigns, copy decks. |
164
- | `business-model` | `business/model/` | — | Unit economics, cost structure, monetization decisions. |
165
- | `roadmap` | `roadmaps/` | — | Phased plan orchestrating tasks/epics; compact `roadmap_manifest` for indexer/CLI (adr-0012-roadmap-storage). |
166
- | `capture` | `captures/` | — | Concise capture entry; `kind:` field discriminates idea / bug-fix / clip / reference / question / spec-import. Replaces the legacy `type: idea` entity (renamed 2026-05-22 per HS-B2B8-DLQI-EPIC-capture-rename-migration-idea-to-capture). `spec-import` is the **import-origin** kind — one capture per imported foreign doc, created by the bulk spec-import feature (HS-8V8T-8SBG-EPIC-first-connect-rough-import / HS-ROIU-9P4B-EPIC-import-preview-and-confirm); grounding HS-J7T5-VEW8-RES-import-specs-one-click-feature-pipeline. |
167
- | `research` | `research/` | — | Audit reports, feature inventories, exploratory notes. |
168
- | `planning-flow` | `flows/` | — | Epic lifecycle orchestration diagram (mermaid source + canvas projection). |
169
- | `draft` | `drafts/` | — | Ingested content awaiting triage. |
170
- | `daily` | `daily/` | — | Optional daily-note scratchpad. |
171
- | `run-retrospective` | `dev-factory/reports/retrospectives/` (outside `specs/`, sibling to `heyspex.yaml`/`.heyspex/`) | — | A **generated** run-close record — never hand-authored, not scheduled/claimable, not a wikilink-addressable spec. One file per COO-dispatcher run, `<runId>.md` (`runId` = the run's milestone id), produced deterministically by `dev-factory/lib/retrospective.mjs` from `run-events.jsonl` + the gate ledger + `coo-dispatcher-state.json`. Frontmatter: `runId`, `generatedAt`, `shippedUnits[]` (gate-verified units), `autoAppliedDecisions[]` (dispatcher auto-applies), `stoppedReason`, `restartCondition`, `ownerGates[]` (pending owner-only gates). Body: the three Retrospective sections (what went well / what could be improved / actionable next steps). Read by What's Next's Retrospective hero (Assisted/Autonomous fronts) — see HS-4HI5-JEPA-EPIC-what-s-next-multi-run-mode-run-dashboard and the-retrospective-is-a-generated-run-rec.
172
-
173
- Adding a new entity type requires an ADR. `branch-brief` was added by adr-0008-branch-brief-and-evidence-schema; `roadmap` by adr-0012-roadmap-storage; all other core types ratified at adr-0001-vault-format. `milestone` is the exception that proves the rule: the type was used informally first — 33 `HS-MIL-*` files (e.g. HS-RM22-IART-MIL-console-ia-restructure) were hand-authored before formalization — so rather than a standalone ADR, the convention is ratified by its introduction lineage, HS-VK7C-MILE-EPIC-milestone-entity-type-and-surfacing (this schema row, the validator, MCP support) followed by HS-OFR5-M2J3-EPIC-introduce-milestones-as-highest-entity (required-parent-on-epics + the roadmap drilldown). The canonical hierarchy these two epics serve is documented in [[spec-hierarchy]].
174
-
175
- `pillar` follows the same milestone-precedent (no standalone "introduce the entity type" ADR): ratified by its introduction lineage HS-EBIL-0011-EPIC-goal-hierarchy-l2-strategic-pillars-l3 (the L2 perpetual-Pillars epic, sibling of HS-OR8V-0010-EPIC-north-star-vision-first-class-entity in HS-J6K8-NSPL-MIL-north-star-and-strategic-pillars-hierarchy) plus the design captured in HS-AWDI-PILR-RES-pm-perpetual-container-terminology; the privacy-boundary side of the work (aggregate-learning telemetry on the Pillar Library) is in adr-0016-pillar-library-privacy-boundary. The 2026-05-31 resolution that produced the Pillar tier also retired the previously planned L3 Themes tier (per-epic teleological anchoring collapsed into perpetual Pillars). First canonical Pillar filed: HS-PILR-pillars (the meta-Pillar; dogfooding instance).
176
-
177
- `agent` follows the same milestone/pillar-precedent (no standalone "introduce the entity type" ADR): ratified by its introduction lineage HS-XNX5-UQHA-MIL-agent-home (this schema row, the frontmatter-schema branch, the `KIND_RULES`/`TYPE_TO_FOLDER` registration, and the compiler that exports identities to host-native `.claude/agents`/`.cursor/agents` formats). The agent identity is the durable, user-authored counterpart to the ephemeral control-plane presence (`active_claims`) — the vault holds *who an agent is*, the control plane holds *what it is doing now*. Positioning context: HS-O46R-R30X-RES-agent-layer-as-the-demand-side-twin-of-t.
178
-
179
- `golden-case` follows the same introduction-lineage precedent under HS-R1JM-OAG3-EPIC-agent-testing-harness-try-a-draft-agent / HS-8AQT-J6FU-TASK-golden-case-vault-artifact-format-and-st: a first-class vault artifact for agent eval fixtures (not JSON sidecars or console-only blobs). Bodies follow a fixed section contract; optional deterministic graders validate against `specs/schemas/golden-case-body.schema.json`.
180
-
181
- ## Lifecycle classes
182
-
183
- Entity types fall into four lifecycle classes. The class determines which subset of the [universal status enum](#frontmatter-schemas) a type uses, when its `canonical:` flag flips, and what the close-out ritual is. **A reader asking *"can I flip this file's status to `in_review`?"* should consult this section first — the answer depends entirely on which class the file's type belongs to.**
184
-
185
- ### Evergreen (living reference substrate)
186
-
187
- `l1` · `l2` · `l3` · `adr` · `pillar` · `agent` · `golden-case` · `protocol` · `memory-lesson` · `memory-decision` · `memory-reference` · `memory-ui-pattern` · `memory-ux-principle` · `research` · `business-positioning` · `business-pricing` · `business-marketing` · `business-model`
188
-
189
- These are **standalone reference artifacts**. `status: open` is the resting state and stays the resting state for the doc's entire life. There are no `in_review` or `closed` transitions — the close-as-historical-record ritual does not apply.
190
-
191
- - **Updates** happen in-place: amend the body, bump `updated:`, append a change-log row.
192
- - **Supersession** is the only "lifecycle event" — record it via relational fields (`supersedes:` / `superseded_by:` on ADRs, `superseded_by:` on memories and research, `canonical: false` as the universal drift tombstone per canonicalization-arbiter-is-link-graph-not-policy). The original stays as the historical record at `status: open`.
193
- - **Research lineage** to downstream artifacts is recorded via `enhances:`, `spawned_epics:`, `bound_epic:`, `incorporated_into:` — these are relational edges, **not** status transitions. See research-is-evergreen for the full rule and the deferred MCP-tool / UI-epic / validator surfaces still being remediated.
194
-
195
- Session-close protocol carve-out: an agent who touched an evergreen doc this session **must not** flip its status. See [[session-close-protocol]] step 1.
196
-
197
- ### Trackable plan-side (open → in_progress → in_review → closed)
198
-
199
- `task` · `epic` · `milestone` · `diagnose` · `review` · `branch-brief` · `planning-flow`
200
-
201
- These are **work containers** with a definite scope and a moment at which they're done. They follow the full status flow and are **frozen at close** per closed-trackable-specs-are-historical-record-not-living-docs — the body becomes historical record of the plan as it was at the moment of close.
202
-
203
- - **Session close** flips completed-this-session specs to `status: in_review` and creates an `HS-REV-<suffix>` companion ([[session-close-protocol]]).
204
- - **Reviewer close** flips `in_review → closed`, flips `canonical: true → false`, optionally sets the `outcome:`/`outcome_note:` verdict (`met` | `partially-met` | `not-met` — reviewer judgment, never inferred; see §Iteration lineage), and freezes the frontmatter forever after.
205
- - **`branch-brief`** closes when the branch ships; **`planning-flow`** closes with its bound epic.
206
-
207
- ### Single-shot ingest (triage-driven; promotion replaces closure)
208
-
209
- `capture` · `draft` · `daily`
210
-
211
- These are **inbound surfaces** scoped to one input session. They don't close in the lifecycle sense — they get **promoted** (capture → research / task / epic; draft → some other type) or remain as a dated scratchpad (daily).
212
-
213
- - **`capture`** carries `promoted_to:` as a binary pointer to the first downstream artifact and `informs_research:` as the M:N lineage list (see the `capture` schema below).
214
- - **`draft`** is triaged by editing the `type:` field in place and moving the file to the destination folder.
215
- - **`daily`** is a single-date scratchpad — no status semantics; the date IS the lifecycle.
216
-
217
- ### Per-instance (class declared at filing time)
218
-
219
- `roadmap`
220
-
221
- `roadmap` is the one type whose lifecycle class depends on the specific roadmap. A perpetual product roadmap is evergreen; a per-quarter ship plan is trackable. Each `HS-ROADMAP-*` declares its class via its own `status:` value at filing: roadmaps that never close stay `open`; roadmaps with a defined ship horizon use the trackable flow. See adr-0012-roadmap-storage for the compact-manifest design that supports both shapes.
222
-
223
- ### Cross-reference summary
224
-
225
- | Class | Status values used | Close-as-historical-record? | `canonical: false` trigger |
226
- |---|---|---|---|
227
- | Evergreen | `open` (resting state) | No | Supersession (relational edge) |
228
- | Trackable plan-side | `open` → `in_progress` → `in_review` → `closed` (plus `deferred`) | Yes — frozen at close | Close-out write |
229
- | Single-shot ingest | `open` (capture/draft pre-promotion) / N/A (daily) | No — promoted or archived by date | Not used |
230
- | Per-instance | Declared at filing | Per-instance | Per-instance |
231
-
232
- ## Identity, IDs, and filenames
233
-
234
- > [!important] Identifier format migration in progress (updated 2026-08-11)
235
- > The vault is migrating to **file-key-first** (era 3) ordering: `HS-<file key>-<family key>-<type label>-<title slug>` — the leading token after `HS-` is the **unique per-file ID**, not the family (HS-6B0S-Q9YG-RES-naming-system-v3-file-key-first, rulings naming-v3-7-rulings-hs-namespace-kept; owner re-confirmed the token order 2026-08-11). On disk today most files are still **family-first** (era 2, `HS-<family key>-<type label>-…`) or legacy type-first (era 1); the vault-wide rename is built and **pending owner review/execution** (HS-L6Y4-01D7-EPIC-deterministic-rename-with-link-rewrite). The type label's position (1st/2nd/3rd token) tells you a filename's era — never guess which token is the file ID by position alone; read frontmatter `file_id:`/`id:`. The ID-format and filename tables below still show older orderings pending the flip; the token **names** are canonical in every era. Plain-language explainer: how-spec-ids-and-naming-work.
236
-
237
- ### Identifier tokens (file key · family key · type label · title slug)
238
-
239
- A managed spec's basename decomposes into named tokens. Canonical names per glossary §Identifier anatomy and HS-37O7-T9KP-RES-identifier-token-terminology-family-key (external-validated; supersedes the "pipeline ID / type token / file ID" names in HS-MF8M-CCVE-RES-family-first-spec-identifier-format-hs-i).
240
-
241
- | Token | Example | Role |
242
- | --- | --- | --- |
243
- | **File key** | `L6Y4` | The per-file 4+ char uppercase-alphanumeric key (at least one letter), **unique vault-wide** — the identity and one-token citation handle for exactly one file. Recorded in frontmatter `file_id:` (SSOT) and embedded in the filename; becomes the `id:` itself (`id: HS-L6Y4`) under era 3. Server-minted, never hand-picked. |
244
- | **Family key** | `P2TR` | The shared 4+ char alphanumeric code that groups one initiative's specs — an epic and all its child tasks, research, and reviews — into one greppable **family** (`grep HS-P2TR` returns the whole family). Shared across the family; uniqueness is enforced on the full **basename** per kind, never on the family key alone (see vault-id-assignment-protocol §Shared-suffix families). |
245
- | **Type label** | `TASK` | The entity-kind token (`TASK`, `EPIC`, `MILESTONE`, `RESEARCH`, `REVIEW`, …). A **human-readable hint only**. |
246
- | **Title slug** | `verification-state-schema` | The lowercased-hyphenated descriptive tail derived from `title:` — the part that makes each basename unique within a family. |
247
-
248
- **Filename shapes (era = type-label position):**
249
- - **File-key-first (era 3, ratified target):** `HS-<file key>-<family key>-<type label>-<title slug>.md` → `HS-L6Y4-01D7-EPIC-deterministic-rename-with-link-rewrite.md` — lands vault-wide with HS-L6Y4-01D7-EPIC-deterministic-rename-with-link-rewrite.
250
- - **Family-first (era 2, current on disk / current minting):** `HS-<family key>-<type label>-[<file key>-]<title slug>.md` → `HS-P2TR-TASK-verification-state-schema.md`, `HS-0G910-EPIC-G3D3-api-foundation-keys-scopes-error.md`
251
- - **Type-first (era 1, legacy):** `HS-<type label>-<family key>-<title slug>.md` → `HS-TASK-P2TR-verification-state-schema.md`
252
-
253
- **Routing rule (SSOT for entity type):** code MUST determine a spec's entity type from the frontmatter **`type:`** field (and its directory), **never** by parsing the type label out of the filename. The type label is a convenience hint that survives out of directory context (wikilinks, search results, clipboard, terminal output); it is **not** authoritative and must never drive a mutation, a route, or a reclassification on its own. This is the semantic-identifier guardrail from HS-MF8M-CCVE-RES-family-first-spec-identifier-format-hs-i (don't route on parsed-ID semantics).
254
-
255
- **Retired / avoid:** **"suffix"** (the legacy name for the family key — self-contradictory once the family key leads), **"pipeline ID"** (overloaded with CI/CD + the feature-pipeline graph), **"prefix" / "type prefix"** for the family key (industry convention encodes *type*, not a family). The legacy term `<suffix>` still appears in the tables below and in the `legacy_slug_only` naming atoms until the Phase 3 codegen flip.
256
-
257
- ### ID format
258
-
259
- | Entity type | ID format | Example |
260
- | --- | --- | --- |
261
- | `l1` | `HS-L1` | `HS-L1` |
262
- | `l2` | `HS-L2-<UPPER>` | `HS-L2-ENTITY`, `HS-L2-FOUNDATIONS` |
263
- | `l3` | `HS-UC-<short>` | `HS-UC-CAPTURE` |
264
- | `adr` | `HS-ADR-NNNN` (zero-padded sequence) | `HS-ADR-0001` |
265
- | `task` | `HS-TASK-<suffix>` — `<suffix>` is 4+ random A-Z0-9 (vault-id-assignment-protocol). Pre-2026-05-22 entries (`HS-NNNN`, `HS-<4+ A-Z0-9>`) were migrated to this prefix. | `HS-TASK-K7M2` |
266
- | `epic` | `HS-EPIC-<suffix>` — same rules | `HS-DE8L`, `HS-V888` |
267
- | `milestone` | `HS-MIL-<suffix>` — `<suffix>` is 4+ uppercase `A–Z0–9` (vault-id-assignment-protocol); legacy 4-character mnemonic IDs (e.g. `HS-RM22`) remain valid | `HS-RM22`, `HS-1JC8` |
268
- | `diagnose` | `HS-DIAG-<suffix>` — `<suffix>` is 4+ random A-Z0-9 (vault-id-assignment-protocol) | `HS-DIAG-K7M2` |
269
- | `pillar` | `HS-PILR-<slug>` (canonical; slug uniquely identifies the concept; no random suffix) or `HS-PILR-<suffix>-<slug>` (customer-scoped; random 4+ A-Z0-9) | `HS-PILR-pillars`, `HS-PILR-scaling`, `HS-PILR-K7M2-team-onboarding` |
270
- | `branch-brief` | `HS-BB-<slug>` | `HS-BB-feature-checkout-redesign` |
271
- | `memory-lesson` | `HS-LES-<slug>` | `HS-LES-bd-process-leak` |
272
- | `memory-decision` | `HS-DEC-<slug>` | `HS-DEC-dep-relate-for-epic-children` |
273
- | `memory-reference` | `HS-REF-<slug>` | `HS-REF-dolt-time-travel-docs` |
274
- | `memory-ui-pattern` | `HS-UIP-<slug>` | `HS-UIP-rail-active-vs-keyboard-cursor` |
275
- | `memory-ux-principle` | `HS-UXP-<slug>` | `HS-UXP-toggle-current-state-semantics` |
276
- | `protocol` | `HS-PROTO-<slug>` | `HS-PROTO-session-close` |
277
- | `review` | `HS-REV-<suffix>` — `<suffix>` matches the parent task/epic id 1:1 (e.g. parent `HS-76RX` → review `HS-QMHO`). Detection: [`extractSpecSuffix` in `console/src/lib/vault.ts:155`](../console/src/lib/vault.ts). | `HS-QMHO`, `HS-R3HO`, `HS-CCEQ` |
278
- | `business-positioning` | `HS-BIZ-POS-<slug>` | `HS-BIZ-POS-2nd-brain-harness-for-specs` |
279
- | `business-pricing` | `HS-BIZ-PRI-<slug>` | `HS-BIZ-PRI-tiers-and-byok-2026-05` |
280
- | `business-marketing` | `HS-BIZ-MKT-<slug>` | `HS-BIZ-MKT-launch-playbook-companion` |
281
- | `business-model` | `HS-BIZ-MOD-<slug>` | `HS-BIZ-MOD-flat-per-vault-economics` |
282
- | `roadmap` | `HS-ROADMAP-<SLUG>` | `HS-ROADMAP-PRODUCT` |
283
- | `research` | `HS-RES-<suffix>` — legacy `HS-RES-NNN` or random `HS-RES-<4+ A-Z0-9>` (vault-id-assignment-protocol) | `HS-RES-001`, `HS-RES-K7M2` |
284
- | `capture` | `HS-CAP-<suffix>` — same rules; legacy `HS-IDEA-<suffix>` preserved in `legacy_idea_id:` for archaeology | `HS-CAP-JKLN`, `HS-CAP-OAP3` |
285
- | `agent` | `HS-AGENT-<suffix>` — `<suffix>` is 4+ random A-Z0-9 (vault-id-assignment-protocol) | `HS-634I` |
286
- | `golden-case` | `HS-GCASE-<suffix>` — same rules | `HS-HPIO` |
287
- | `planning-flow` | `HS-FLOW-NNN` | `HS-FLOW-001` |
288
- | `draft` | `HS-DRAFT-<slug>` | `HS-DRAFT-2026-05-14-1130-article-link` |
289
- | `daily` | `HS-DAILY-YYYY-MM-DD` | `HS-DAILY-2026-05-14` |
290
-
291
- **New task/epic/research/capture IDs** (console `POST /api/specs`, MCP `heyspex_save_research`, agents): allocate a random 4-character suffix from `A–Z` and `0–9`, checking the full vault for collisions (frontmatter `id` and filename prefix). When every combination at the current length is taken, the suffix grows to 5 characters, then 6, and so on. Legacy sequential IDs (`HS-NNNN`, `HS-RES-NNN`, `HS-IDEA-*`, slug-only research filenames) remain valid in the read-path until HS-23PN-9HVY-TASK-capture-rename-repo-wide-wikilink-rewrite retires them. Validate with `npm run validate:vault-ids` at the repo root.
292
-
293
- ### Filename derivation
294
-
295
- The filename is **derived from the ID + an optional slug**, lowercased, with the same prefix the ID uses minus the `HS-` prefix (kept lowercase in filenames for readability):
296
-
297
- | Entity | Filename pattern | Example |
298
- | --- | --- | --- |
299
- | `l1` | `heyspex-north-star.md` (literal, only one) | `heyspex-north-star.md` |
300
- | `l2` | `<topic>.md` (literal slug) | `entity-model.md` |
301
- | `l3` | `uc-<short>.md` | `uc-capture-intent.md` |
302
- | `adr` | `adr-NNNN-<slug>.md` | `adr-0001-vault-format.md` |
303
- | `task` | `HS-TASK-<suffix>-<slug>.md` | `HS-TASK-K7M2-fix-foo.md` |
304
- | `epic` | `HS-EPIC-<suffix>-<slug>.md` | `HS-0007-EPIC-console-resilience.md`, `HS-W9ZE-EPIC-design-system-enforcement-auto-surface-u.md` |
305
- | `milestone` | `HS-MIL-<suffix>-<slug>.md` | `HS-IART-MIL-console-ia-restructure.md` |
306
- | `diagnose` | `HS-DIAG-<suffix>-<slug>.md` | `HS-DIAG-K7M2-console-startup-lag.md` |
307
- | `pillar` | `HS-PILR-<slug>.md` (canonical; slug IS the ID with no random suffix) or `HS-PILR-<suffix>-<slug>.md` (customer-scoped) | `HS-PILR-pillars.md`, `HS-PILR-K7M2-team-onboarding.md` |
308
- | `branch-brief` | `<branch-slug>.md` | `feature-checkout-redesign.md` |
309
- | `memory-lesson` | `<slug>.md` | `bd-process-leak.md` |
310
- | `memory-decision` | `<slug>.md` | `dep-relate-for-epic-children.md` |
311
- | `memory-reference` | `<slug>.md` | `dolt-time-travel-docs.md` |
312
- | `memory-ui-pattern` | `<slug>.md` | `rail-active-vs-keyboard-cursor.md` |
313
- | `memory-ux-principle` | `<slug>.md` | `toggle-current-state-semantics.md` |
314
- | `protocol` | `<slug>.md` | `session-close-protocol.md` |
315
- | `review` | `HS-REV-<suffix>-<slug>.md` (slug truncated from parent's slug, ≤40 chars) | `HS-SK1L-REV-hs-idea-skill-four-beats-layered-confirms.md` |
316
- | `research` | `HS-RES-<suffix>-<slug>.md` (legacy: `<slug>.md` only) | `HS-RES-K7M2-agent-governance.md` |
317
- | `capture` | `HS-CAP-<suffix>-<slug>.md` | `HS-CAP-JKLN-vault-triage.md` |
318
- | `agent` | `HS-AGENT-<suffix>-<slug>.md` | `HS-9TNK-AGENT-code-reviewer.md` |
319
- | `golden-case` | `HS-GCASE-<suffix>-<slug>.md` | `HS-R4VW-GCASE-review-missing-ac.md` |
320
- | `planning-flow` | `HS-FLOW-<NNN>-<slug>.md` | `HS-FLOW-001-epic-planning-flow-studio.md` |
321
- | `draft` | `<slug>.md` | `2026-05-14-1130-article-link.md` |
322
- | `daily` | `YYYY-MM-DD.md` | `2026-05-14.md` |
323
-
324
- Slug rules: lowercase, ASCII, dash-separated, no trailing punctuation, no leading number for non-task types.
325
-
326
- > [!note] Branch-brief slug
327
- > For `branch-brief` the slug is **derived from the git branch name**: lowercased, every run of non-`[a-z0-9]` characters collapsed to a single `-`, trailing `-` stripped. `feature/checkout-redesign` → `feature-checkout-redesign`. The raw, unsanitized branch name is preserved in the required `branch:` field — the slug is for the filesystem and wikilink resolution only; git lookups use `branch:`. Ratified in adr-0008-branch-brief-and-evidence-schema.
328
-
329
- > [!note] Why not put the ID in the filename for memories?
330
- > bd memory keys are content-derived (`lessons/bd-process-leak`), and the Builder already thinks in those terms. The filename `bd-process-leak.md` IS the key, the ID is the metadata. Wikilink resolution finds the file by name; the `HS-LES-` prefix in the `id:` field is for the index and for stable cross-references.
331
-
332
- ### ID + filename for the migration
333
-
334
- The migration plan (migration-from-bd) maps each bd issue to a new HeySpex task ID by sorting bd issues by `created_at` and assigning `HS-U2K9`, `HS-FMWM`, etc., in that order. The original bd ID is preserved as `legacy_bd_id` in frontmatter so cross-references in old commits/logs remain resolvable.
335
-
336
- ## Frontmatter schemas
337
-
338
- Every spec opens with a YAML frontmatter block. The schemas below define **required** fields (must be present and non-empty) and **optional** fields (may be omitted).
339
-
340
- Universal fields (every spec has these):
341
-
342
- ```yaml
343
- id: # required, string, matches the entity type's ID format
344
- name: # required, string, human-readable title
345
- type: # required, one of the entity type values above
346
- status: # required, see status enum below
347
- priority: # required, integer 0–4 (0 = highest)
348
- owner: # required, string (email or handle) — the ORIGINATING + accountable human (doubles as author).
349
- # See §Authorship provenance below.
350
- created: # required, ISO-8601 date (YYYY-MM-DD)
351
- updated: # required, ISO-8601 date
352
- contributors: # optional, list of "Name <email>" — OTHER humans who later enhanced the doc (append-only;
353
- # OMIT when empty). Auto-stamped, never hand-maintained. See §Authorship provenance.
354
- assisted_by: # optional, list of "AGENT" or "AGENT:MODEL" — agents/IDEs + model that assisted (append-only;
355
- # OMIT when empty). Auto-stamped, never hand-maintained. See §Authorship provenance.
356
- tags: # optional, list of strings
357
- related: # optional, list of wikilinks (alternative to inline body links)
358
- what_matters: # optional, enum: star | heart — per-spec user-priority signal
359
- ```
360
-
361
- **`what_matters`** is the per-spec user-priority signal that replaced the retired What Matters column (scheduler-and-whats-next-what-matters-column-mechanics, HS-78E7-Y9OJ-EPIC-deterministic-priority-scheduler). It is optional and two-level:
362
-
363
- - `star` (★) — *priority*: "consider this please."
364
- - `heart` (♥) — *very important*: "appreciate this being next if at all possible." A stronger signal than `star`.
365
- - absent — unflagged (no default is injected).
366
-
367
- The value is held verbatim; an unrecognized value is tolerated by the parser and emits a warn-only `INVALID_WHAT_MATTERS` validator notice rather than an error. A Phase 7b scheduler stage consumes it for ordering.
368
-
369
- ### Authorship provenance
370
-
371
- Three universal fields carry GitHub-faithful provenance, following the cross-foundation `Assisted-by:` convention (Linux Kernel, Fedora, LLVM, OpenTelemetry) that keeps the human as the sole accountable author and records AI as a tool that *assisted*, not a co-author. Origin: HS-N1WG-W48S-RES-git-github-authorship-attribution-best-p → HS-Y4JH-IKIM-EPIC-authorship-provenance-frontmatter-owner. The SSOT question (frontmatter vs git history) is resolved in provenance-frontmatter-is-ssot-not-git-echo.
372
-
373
- | Field | Who | Item format | Cardinality |
374
- | --- | --- | --- | --- |
375
- | `owner` | the **originating + accountable** human (doubles as author) | handle or email (e.g. `pwher`) | single scalar |
376
- | `contributors` | **other** humans who later enhanced the doc | `Name <email>` — GitHub form, round-trips with the `Co-authored-by:` trailer | append-only list |
377
- | `assisted_by` | agents/IDEs + model that assisted | `AGENT` or `AGENT:MODEL` — e.g. `Claude Opus 4.8`, `Cursor` | append-only list |
378
-
379
- Semantics (apply to every entity kind):
380
-
381
- - **`owner` is universally required** and unchanged in type — only its meaning is enriched here (originator + accountable, not merely a pointer). A later ownership *transfer* is not preserved in frontmatter; git history (first-commit author) retains the original creator. Acceptable trade per HS-N1WG-W48S-RES-git-github-authorship-attribution-best-p.
382
- - **`contributors` and `assisted_by` are universal-but-optional and omit-when-empty** — they are not emitted as empty lists, so a solo-vault doc stays clean and only grows a list once a second human or an agent touches the doc.
383
- - **Append-only + de-dup, never reorder/remove.** A new acting human ≠ `owner` is appended to `contributors`; an acting agent is appended to `assisted_by`; an entry already present is not duplicated.
384
- - **Auto-stamped, never hand-maintained.** Seeded at creation (`owner` from the resolved user, `assisted_by` from the acting agent) and appended on edit, sourced from the existing claim/session trail (Neon `active_claims`). Authoring/wiring lives in HS-Y4JH-IKIM-EPIC-authorship-provenance-frontmatter-owner.
385
-
386
- > [!note] Schema addition self-gating
387
- > Per entity-model-self-gates-schema-changes-via-adr, new reserved frontmatter keys are normally gated behind an ADR. `contributors`/`assisted_by` are admitted here **without a standalone ADR** under the same milestone-precedent the `milestone` and `pillar` entity types used: they are additive, optional, omit-when-empty enrichments of the existing universal `owner` field (not a new entity type or a new lifecycle), ratified by their introduction lineage HS-N1WG-W48S-RES-git-github-authorship-attribution-best-p → HS-Y4JH-IKIM-EPIC-authorship-provenance-frontmatter-owner. The decision rationale is recorded in provenance-frontmatter-is-ssot-not-git-echo.
388
-
389
- ### Artifact linkage
390
-
391
- An optional `artifact:` mapping records a published Claude Artifact that this spec/doc is the vault source for, so the artifact is never the only copy of its content — HS-6YIS-5GSW-EPIC-artifacts-as-vault-documents. **Universal** — unlike the type-specific schemas below, it attaches to ANY entity kind (a task, an epic, a research doc, a memory doc, or a non-frontmatter file like a roadmap page — see the HTML variant below), because a published artifact can belong to a doc at any level, not one lifecycle stage.
392
-
393
- ```yaml
394
- artifact: { title: "…", url: "https://claude.ai/code/artifact/…", version: "…", updated: "2026-08-23" }
395
- # title: required when artifact: is present — the artifact's display title (matches its published <title>/heading).
396
- # url: required — the published URL.
397
- # version: optional, string — the last-published version stamp this vault file was confirmed against. This is
398
- # a LOCAL claim, not a live read: it is the freshness anchor for the push-first stale-guard
399
- # (HS-6YIS-5GSW-EPIC part 2) — before publishing this file over the artifact, the publishing session
400
- # reads the LIVE page and confirms it is not ahead of this stamp, THEN writes the new version back
401
- # here after a successful publish. Never hand-edited to "catch up" without actually reading the live
402
- # page first (the incident this field exists to prevent: [[a-published-artifact-and-its-vault-copy]]).
403
- # updated: optional, ISO-8601 date — when this vault file was last published/stamped against the artifact.
404
- ```
405
-
406
- Either a single-line flow mapping (above) or an equivalent nested block mapping is valid YAML; the artifact-publish tooling (HS-6YIS-5GSW-EPIC-artifacts-as-vault-documents part 2, `scripts/hs-artifact.mjs`) reads and writes the flow-mapping form. Omit the whole `artifact:` key when the doc has no published artifact (omit-when-empty, same posture as `contributors`/`assisted_by`).
407
-
408
- **Non-frontmatter vault files** (a raw HTML page under `specs/roadmap/`, for example, has no YAML frontmatter at all): the same four fields are carried as a single machine-readable `ARTIFACT-STAMP: {...}` JSON line inside the file's existing leading `<!-- -->` source-of-truth comment, read/written by the same tooling. This is the shape `specs/roadmap/heyspex-sprint-roadmap0829.html` uses.
409
-
410
- > [!note] Schema addition self-gating
411
- > Per entity-model-self-gates-schema-changes-via-adr, `artifact:` is admitted here **without a standalone ADR** under the same milestone-precedent as `contributors`/`assisted_by` above: additive, optional, omit-when-empty, not a new entity type or lifecycle — ratified by its introduction lineage, the owner's immediate order following the incident recorded in a-published-artifact-and-its-vault-copy → HS-6YIS-5GSW-EPIC-artifacts-as-vault-documents.
412
-
413
- Status enum (universal):
414
-
415
- - `draft` — under active authorship, not yet ratified.
416
- - `open` — ratified and available to work against (for tasks, epics) or stable (for L1/L2/L3/ADR).
417
- - `in_progress` — claimed and being executed (tasks only).
418
- - `in_review` — implementation complete, awaiting human verification before closure (tasks only).
419
- - `closed` — done (tasks) or superseded (ADRs).
420
- - `deferred` — **deprecated** (2026-06-24 per parked-is-the-single-hold-status-deferre). Use `status: parked` for indefinite hold or `defer_until:` for dated snooze instead. Kept for read-back-compat only.
421
-
422
- ### Type-specific schemas
423
-
424
- #### `l1`
425
-
426
- ```yaml
427
- type: l1
428
- aliases: # optional, list of alternative names
429
- brand: # optional, mapping with canonical/variants/pronunciation/comma
430
- domains: # optional, list of relevant domains
431
- version: # optional, monotonic integer — bumped on every material edit to the record.
432
- # The alignment-drift detector ([[HS-E3D3-RDFT-EPIC-alignment-drift-detector-into-resolution]])
433
- # compares the version an epic was last aligned against (its `alignment_baseline`)
434
- # to this current version; a bump past the baseline surfaces a `drift` resolution item.
435
- last_edited_at: # optional, ISO-8601 timestamp of the last edit to the record. Timestamp FALLBACK for
436
- # drift detection when an epic carries no per-layer `alignment_baseline`: the layer's
437
- # `last_edited_at` newer than the epic's `alignment_reviewed_at` means drift.
438
- ```
439
-
440
- Only one `l1` exists per vault. The `version` / `last_edited_at` fields are the versioning prerequisites carried from HS-OR8V-0010-EPIC-north-star-vision-first-class-entity; they make the L1 record a drift baseline anchor for the alignment-drift detector.
441
-
442
- #### `l2`
443
-
444
- Universal fields only. No required L2-specific fields.
445
-
446
- #### `l3` (use case)
447
-
448
- ```yaml
449
- type: l3
450
- actors: # required, list of actor names from L1
451
- trigger: # required, string — what initiates this use case
452
- outcomes: # required, list — observable end states
453
- depends_on: # optional, list of wikilinks to other use cases
454
- ```
455
-
456
- #### `adr`
457
-
458
- ```yaml
459
- type: adr
460
- decision_date: # required, ISO-8601 date
461
- deciders: # required, list of strings
462
- supersedes: # optional, wikilink to ADR this replaces
463
- superseded_by: # optional, wikilink to ADR replacing this
464
- status: # adr-status enum: proposed | accepted | deprecated
465
- # `superseded` is DEPRECATED 2026-06-03 per [[superseded-enum-values-deprecated-use-canonical-false-and-superseded-by]]
466
- # New writes: set `canonical: false` + `superseded_by: '[[<successor>]]'` instead. Existing
467
- # ADRs that already carry `status: superseded` stay as historical record per the
468
- # immutability principle ([[closed-trackable-specs-are-historical-record-not-living-docs]]) —
469
- # readers infer `canonical: false` from the legacy value.
470
- canonical: # universal flag — true (or absent) = current/authoritative; false = historical record
471
- # (current canonical is at `superseded_by:`). Flipped at supersession-time via
472
- # [[supersession-protocol]]; frozen thereafter.
473
- ```
474
-
475
- The ADR-specific `status` enum overrides the universal status enum for ADRs. The `canonical:` field applies to ADRs the same way it does to every spec type.
476
-
477
- #### `task`
478
-
479
- ```yaml
480
- type: task
481
- kind: # one of: infra | foundation | feature | polish (scheduler tier — SSOT is
482
- # SCHEDULER_KIND_VOCAB in console/src/lib/indexer/vault-rules.mjs). Legacy bd
483
- # kinds (bug | spike | story | milestone) are retired; migrate via
484
- # scripts/migrate-to-scheduler-metadata.mjs. Bugs now have their own lane
485
- # (kind: bug-fix capture → HS-DIAG-* diagnose spec → Resolution surface).
486
- assignee: # optional, string
487
- started_at: # optional, ISO-8601 datetime — set on first transition to in_progress
488
- closed_at: # optional, ISO-8601 datetime
489
- close_reason: # optional, enum: shipped | deprecated | obsolete | wont_fix | duplicate
490
- # shipped → "Done" archive view; deprecated* → "Deprecated" archive view
491
- # `superseded` is DEPRECATED 2026-06-03 per [[superseded-enum-values-deprecated-use-canonical-false-and-superseded-by]]
492
- # New writes that involve supersession: omit `close_reason` (or use another value
493
- # like `deprecated`) and set `canonical: false` + `superseded_by:` instead.
494
- # Existing closed specs with `close_reason: superseded` stay as historical record
495
- # per immutability ([[closed-trackable-specs-are-historical-record-not-living-docs]]).
496
- defer_until: # optional, ISO-8601 date — dated snooze evaluated by isDeferredNow(); scheduler-invisible
497
- # until the date, then auto-returns with no status change ([[parked-is-the-single-hold-status-deferre]]).
498
- # Distinct from `status: parked` (indefinite hold). Do NOT pair with deprecated `status: deferred`.
499
- target: # optional, string — repo-relative path(s) of the file(s) an autonomous builder edits
500
- # for this task (comma-separated for multi-file). DISPATCH METADATA for the dev-factory
501
- # overnight loop: its backlog selector only dispatches an open, dependency-ready task
502
- # that carries target:; without it the task is skipped with a warning. Hand-editing this
503
- # field is allowed (it is not status). Owner-approved 2026-07-04 (HS-BMAW go-live);
504
- # interim docs lived in dev-factory/README.md §Vault-task target: frontmatter.
505
- test: # optional, string — the test file that gates the autonomous build of this task
506
- # (scoped vitest run / self-test). When omitted the gate falls back to tsc --noEmit
507
- # plus the module's own self-test conventions. Pairs with target: above.
508
- suggested_model: # optional, string — breakdown-authored Ollama model id for local builder dispatch
509
- # (e.g. qwen3-coder:30b). Honored as first candidate in scheduler/backlog-selector;
510
- # falls back through routing-table build chain when unavailable.
511
- suggested_machine:# optional, string — machine key from routing-table (specops-gpu0, basement-geek, …).
512
- # Scheduler tries this machine first when reachable + capable.
513
- recommended_num_ctx: # optional, integer — Ollama num_ctx bucket (16384 | 32768 | 65536 | 131072).
514
- # pick-num-ctx.mjs honors when ≤ machine ceiling (spec-ops 65536, basement 131072).
515
- estimated_context_tokens: # optional, integer — breakdown estimate of prompt tokens (target + deps + spec).
516
- context_files: # optional, list of repo-relative paths — passed to builder.mjs as --extra (types, deps).
517
- # Omit the target file (builder always reads it). Body duplicate: ## Builder brief.
518
- fit_assessment: # optional, enum: safe | tight | overflow — breakdown confidence in context fit.
519
- parallel_safe: # optional, boolean — false when task must not run concurrently (shared config/schema).
520
- affects_paths: # optional, list of repo-relative paths — read/write set for stomp detection at dispatch.
521
- parallel_group: # optional, string — wave id; tasks in same group may run together when paths disjoint.
522
- builder_profile: # optional on child tasks only when inherited from epic; see builder-fleet-capability-reference.
523
- canonical: # universal flag — true (or absent) = current/authoritative; false = historical record.
524
- # Flipped to false during the in_review → closed transition for plan-side specs per
525
- # [[session-close-protocol]] §Reviewer responsibilities step 4, or at supersession per
526
- # [[supersession-protocol]]. `status: parked` keeps `canonical: true` (paused, not historical).
527
- superseded_by: # optional, wikilink — pointer to the current canonical when canonical: false.
528
- # Required when canonical: false AND there is a successor (per [[supersession-protocol]]).
529
- # When close_reason was historically deprecated|duplicate, this field can still point
530
- # at the successor; the legacy enum value stays as historical record.
531
-
532
- # Dependency edges (see "Dependencies" section below)
533
- blocks: # optional, list of wikilinks — this task blocks these
534
- blocked_by: # optional, list of wikilinks — these tasks block this one
535
- related: # optional, list of wikilinks — soft, navigational
536
- parent: # optional, single wikilink — hierarchical parent
537
- children: # optional, list of wikilinks — hierarchical children
538
- discovered_from: # optional, single wikilink — provenance edge ("this task was born while
539
- # working on X"). Also the code-audit **cleanup→task exit**: a task filed
540
- # from a `research_type: code-audit` finding row (Action: task) sets this to
541
- # the source `[[HS-RES-…]]` (see §research "Code-audit findings table" /
542
- # [[HS-7D0I-WL88-TASK-code-audit-research-type-with-a-defined]]).
543
-
544
- # Delegation / task-split lineage ([[HS-HXE2-YQ5A-EPIC-delegation-defer-work-from-one-agent-to]],
545
- # [[HS-DTL1-CX5K-CAP-task-split-handoff-when-context-runs-low]], [[HS-PD6K-NCH5-TASK-handoff-envelope-schema-and-split-child]])
546
- split_of: # optional, single wikilink — parent task or epic this spec continues (option B:
547
- # random child id + frontmatter linkage). Must pair with split_index.
548
- split_index: # optional, positive integer — 1-based sequence among siblings split from the
549
- # same split_of parent. Required when split_of is set.
550
- handoff_id: # optional, string — dedup key for idempotent defer re-posts
551
- # ([[HS-PJN4-W146-TASK-defer-idempotency-handoff-id-and-cross-a]]).
552
-
553
- # Ingest + roadmap admission ([[plan-ingest-review-roadmap-workflow]], [[adr-0012-roadmap-storage]])
554
- ingest_draft: # optional, wikilink to draft this spec was promoted from
555
- forked_from: # optional, wikilink — source spec when forked from sealed as-built diagram ([[HS-G8JS-F6P1-TASK-fork-spec-from-as-built-canvas]])
556
- forked_from_canvas: # optional, string — vault-relative as-built canvas path
557
- has_diagram: # optional, boolean — spec contains architecture mermaid; indexes Shipped gallery
558
- source_intent_hash: # optional, copied from draft at promote time
559
- roadmap_admission: # optional, enum: pending | scheduled | horizon | declined
560
- roadmap_phase: # optional, string — phase id from a HS-ROADMAP-* manifest (e.g. M3)
561
- # horizon backlog: set priority: 4 and/or tag horizon; roadmap_admission: horizon
562
-
563
- # Optional structured content; preferred form is markdown sections in body
564
- acceptance: # optional, list of acceptance criteria. Each entry is EITHER a plain string
565
- # (prose criterion — stays fully valid) OR an object carrying a runnable check:
566
- # - text: "<the criterion>"
567
- # verification: # optional — how a machine proves this criterion
568
- # kind: test | lint | types | command | guard | evidence
569
- # run: "<command or test path>" # required for machine kinds
570
- # expect: exit-0 | no-net-new # default exit-0
571
- # `kind: evidence` names an evidence type a human/receipt records instead of a
572
- # command. Prose-only criteria are receipt-marked "human-verified" honestly.
573
- # Backward compatible by construction ([[HS-MNW5-IU2S-EPIC-executable-acceptance-criteria-and]]);
574
- # kinds/expects mirror the checks-manifest contract ([[checks-manifest-authoring]],
575
- # parser: spex-console/src/lib/cli/checks-manifest.ts).
576
- skills: # optional, list of strings
577
- context: # optional, list of strings
578
- evidence: # optional, evidence array — sources proving acceptance met
579
- # (see "Evidence linking" below)
580
-
581
- # Council session output ([[adr-0015-council-as-cross-cutting-capability]])
582
- deliberation_record: # optional; multi-LLM debate output, append-only `runs[]`. Permitted on task/epic/review
583
- # in addition to draft. Schema lives under `draft` §`deliberation_record` above. Attaches
584
- # on promoted-spec recheck (invocation #3) via `/hs-next` claim path; never gates work.
585
-
586
- # Parallel-agent module hints ([[HS-ZMAQ-M15C-TASK-declare-owned-path-globs-on-agent-identi]])
587
- affects_paths: # optional, list of repo-relative glob strings — coarse module hints for
588
- # overlap detection when multiple agents work in parallel; advisory in v1
589
- # (same posture as the advisory `lane:` label on agent identities).
590
-
591
- # Migration provenance
592
- legacy_bd_id: # optional, string — original bd id for migrated entries
593
- ```
594
-
595
- ##### Handoff envelope (body)
596
-
597
- When an agent defers or splits remaining work into a child task, the **handoff envelope** carries the next agent's starting context. SSOT is the **body**, not frontmatter — a `## Handoff envelope` heading followed by a fenced ` ```yaml ` block matching `schemas/handoff-envelope.schema.json`:
598
-
599
- ```yaml
600
- reason: "Why the defer/split happened (context budget, blocked, lane hand-off)"
601
- summary_done:
602
- - "Work completed before the handoff"
603
- remaining:
604
- - "Actionable items for the picking-up agent"
605
- success_criteria:
606
- - "How the reviewer knows the deferred work is done"
607
- ```
608
-
609
- Field semantics align with the WIP ledger (`remaining` / verified-style `summary_done`) plus delegation-specific `reason` and `success_criteria` per HS-SUMD-4T35-RES-parallel-agent-coordination-handoff-and. A task with `split_of:` **must** include this section; `scripts/validate-vault-ids.mjs` enforces shape. The defer command (HS-MS3T-1U0Z-TASK-hs-defer-mcp-tool-and-slash-command) authors both the lineage frontmatter and this block.
610
-
611
- #### `agent`
612
-
613
- Durable agent identity (HS-XNX5-UQHA-MIL-agent-home). Evergreen — does not close. Compiles to `.claude/agents/<name>.md` and `.cursor/agents/<name>.md`; the vault spec is the SSOT.
614
-
615
- ```yaml
616
- type: agent
617
- role: # required, string — human-readable role title (also used as description fallback on compile)
618
- model: # optional, enum: opus | sonnet | haiku
619
- description: # optional, string — host-native description on compile
620
- goal: # optional, string — HeySpex-resident intent label (not compiled)
621
- allowed_tools: # optional, list of strings — compiled to host `tools:` when present
622
- disallowed_tools: # optional, list of strings — compiled to host `disallowedTools:` when present
623
- procedures: # optional, list of wikilinks — HeySpex-resident procedure refs
624
- handoffs: # optional, list — HeySpex-resident handoff targets
625
- guardrails: # optional, list of strings — HeySpex-resident; persona body carries compiled guardrails
626
- lane: # optional, string — human label for parallel work (advisory in v1)
627
- isolation: # optional, enum: default | worktree — operator guidance for checkout
628
- # isolation when parallel agents each need a dev server; advisory in v1
629
- # ([[parallel-agent-isolation-guidance]]). `worktree` = use a dedicated git
630
- # worktree before implementation. HeySpex-only — stripped on compile (like `lane`).
631
- color: # optional, string — console roster accent (HeySpex-only)
632
- owned_paths: # optional, list of repo-relative glob strings — machine-checkable file/module
633
- # ownership for stomp-detection ([[HS-8PNX-Q1KC-EPIC-lanes-and-stomp-detection-for-parallel-a]]).
634
- # Example: spex-console/src/lib/scheduler/**, vendor/heyspex-mcp/**. HeySpex-only — stripped on compile.
635
- golden_cases: # optional, list of wikilinks to HS-GCASE-* fixtures owned by this agent.
636
- # Reverse of each case's `agent:` pointer. HeySpex-only — stripped on compile
637
- # ([[HS-8AQT-J6FU-TASK-golden-case-vault-artifact-format-and-st]]).
638
- source_attribution: # optional object — import provenance for community/own-agent imports only
639
- tags: # required for roster grouping — MUST include `agent` plus at least one
640
- # `purpose/*` tag (see Purpose tags below). Plain frontmatter array;
641
- # no agent-only grouping engine ([[HS-IAYA-5345-TASK-lock-agent-purpose-taxonomy-and-tag-conv]]).
642
- ```
643
-
644
- **Purpose tags** (HS-E2VS-LBUS-EPIC-roster-organization-and-discovery-for-a, HS-WDHW-0009-EPIC-tag-taxonomy-tag-browser-obsidian-style): agent identities reuse the vault's nested-tag namespaces — a dedicated **`purpose/`** namespace (seventh namespace alongside 0009's `area/`, `type/`, `surface/`, `theme/`, `dep/`, `stage/`) for roster discovery. Values are multi-select tags in `tags:`; grouping UI and `GET /api/agents` read them later (HS-NRND-UF9J-TASK-expose-agent-tags-in-get-api-agents-rost).
645
-
646
- | Tag | When to use |
647
- |-----|-------------|
648
- | `purpose/reviewer` | Verify acceptance criteria, review diffs, read-only audit before close-out |
649
- | `purpose/researcher` | Synthesize cited research into durable `HS-RES-*` specs |
650
- | `purpose/planner` | Ground plans/epics, stress-test assumptions at the plan→execute boundary |
651
- | `purpose/triager` | Intake routing — captures, resolution items, smallest-fix triage |
652
- | `purpose/implementer` | Build, edit, deploy — agents that modify code or infra |
653
- | `purpose/writer` | Prose/docs authoring (reserved; no starter roster member yet) |
654
-
655
- Rules:
656
-
657
- 1. **Required:** every shipped `type: agent` spec carries **at least one** `purpose/*` tag in `tags:` (in addition to the `agent` marker).
658
- 2. **Primary purpose:** the **first** `purpose/*` tag in `tags:` array order is the roster section key. Downstream surfaces expose it as `primaryPurpose` (computed server-side — not a separate frontmatter field). When an agent legitimately spans multiple purposes, list the primary group first and secondary `purpose/*` tags after.
659
- 3. **`lane:` is not a substitute:** `lane` is parallel-work coordination (advisory, HeySpex-only, stripped on compile). `purpose/*` is vault-resident discovery metadata that flows to the API. They often align but are independent fields.
660
-
661
- #### `golden-case`
662
-
663
- Saved eval fixture for the agent testing harness (HS-R1JM-OAG3-EPIC-agent-testing-harness-try-a-draft-agent). Evergreen — updated in place as the agent evolves; does not close. One golden case exercises exactly one `type: agent` identity. The eval spawn (HS-YNIE-ZDQ7-TASK-transient-eval-spawn-and-trajectory-capt) reads the body; the verdict engine (HS-ZOHQ-MOOE-TASK-verdict-engine-deterministic-graders-plu) consumes optional deterministic graders. Expected trajectories (regression baselines) are **run artifacts**, not stored in the fixture at v1 — they land in run history later (HS-ZR0Z-TS11-TASK-run-history-storage).
664
-
665
- ```yaml
666
- type: golden-case
667
- agent: # required, wikilink to HS-AGENT-* — the identity under test
668
- tags: # should include golden-case, agents, testing
669
- related: # optional wikilinks — e.g. the task spec used as setup context
670
- ```
671
-
672
- **Bidirectional linkage:** each golden case carries `agent:`; the agent MAY carry `golden_cases:` (list of wikilinks). Filing via `createSpec` / `heyspex_file_spec` with `frontmatter.agent` auto-appends to the agent's `golden_cases:` array (same pattern as task→epic `children:`).
673
-
674
- **Required body sections:**
675
-
676
- | Section | Required | Purpose |
677
- | --- | --- | --- |
678
- | `## Prompt` | yes | The user message the eval spawn sends to the draft agent (≥12 characters). |
679
- | `## Setup` | no | Freeform context — wikilinks to task specs, files in scope, env hints. |
680
- | `## Deterministic graders` | no | Fenced `yaml` block matching `specs/schemas/golden-case-body.schema.json`. Keys the reproducible pass/fail signal; LLM-judge critique remains advisory per OAG3 AC#4. |
681
-
682
- **On-disk layout:** `specs/agents/golden-cases/HS-GCASE-<suffix>-<slug>.md` — nested under `agents/` so fixtures travel in the same git subtree as agent identities.
683
-
684
- #### `epic`
685
-
686
- Same as `task` schema (including optional `affects_paths:`). The `type: epic` is what differentiates. An epic typically has many `children:` and few `blocks:`/`blocked_by:`.
687
-
688
- **Planning flow requirements** (planning-flow-research-epic-lifecycle): every epic MUST include `research:` and `planning_flow:` wikilinks. Console Epic Studio and `POST /api/specs` enforce this for newly filed epics.
689
-
690
- ```yaml
691
- research: # required for new epics — wikilink to HS-RES-*
692
- planning_flow: # required for new epics — wikilink to HS-FLOW-*
693
- milestone: # REQUIRED — single wikilink to the parent HS-MIL-* this epic belongs to.
694
- # The inverse of the milestone's `epics:` array (write both sides;
695
- # `scripts/validate-vault-ids.mjs` rejects one-sided drift).
696
- # Grace window: missing `milestone:` is a validator warning until
697
- # 2026-06-26, then escalates to an error
698
- # ([[HS-V5UA-T5PX-TASK-required-milestone-parent-on-epic-validator-and-grace-window]],
699
- # part of [[HS-OFR5-M2J3-EPIC-introduce-milestones-as-highest-entity]]).
700
- # Pre-rule orphan epics live at
701
- # [[HS-3DJS-ORPH-MIL-uncategorized-pre-2026-05-26-epics]]
702
- # pending walk-the-floor reassignment.
703
- derived_from: # optional, single wikilink to HS-RES-* — the research that spawned this epic.
704
- # Reverse of `research.spawned_epics:` (1:N). See §Provenance edges.
705
- ```
706
-
707
- **Alignment fields** (drift-detection prerequisites; HS-E3D3-RDFT-EPIC-alignment-drift-detector-into-resolution, owned by HS-KA1E-0012-EPIC-creation-guardrails-agent-pushback for the vault-wide backfill):
708
-
709
- ```yaml
710
- alignment_rationale: # optional (backfill in progress), string — a wikilink to the Pillar / L1 principle
711
- # this epic advances PLUS a one-line "why", e.g.
712
- # "[[HS-PILR-architecture]] — advances the foundations concern by converging detection paths".
713
- # Distinct from the membership `pillar:` field: `pillar:` categorizes; `alignment_rationale`
714
- # carries the teleological "why" the drift detector reasons over. The wikilink target
715
- # SHOULD match the epic's `pillar:`. An `alignment_rationale` that resolves to no layer
716
- # record surfaces as an `orphan` resolution item.
717
- alignment_baseline: # optional, mapping { <layer-id>: <int version>, anti_goals?: <fingerprint> } — the layer
718
- # versions this epic was last aligned against. PRIMARY drift baseline: when a referenced
719
- # layer's current `version` exceeds the baseline here, a `drift` item is emitted. The
720
- # `anti_goals` key stamps the L1 anti-goal fingerprint so a changed anti-goal set re-checks
721
- # every active + recently-closed epic. Absent → the detector falls back to
722
- # `alignment_reviewed_at` vs the layer's `last_edited_at`; absent both → no drift (never a
723
- # false positive). Stamped by the alignment checkpoint on re-alignment.
724
- ```
725
-
726
- #### `milestone`
727
-
728
- The grouping **above** epic: a coherent multi-epic ship (a restructure or release) that closes when every child epic closes and the milestone's own acceptance gate is met. Formalized by HS-VK7C-MILE-EPIC-milestone-entity-type-and-surfacing; the canonical hierarchy and filing rules live in [[spec-hierarchy]]. The 33 `HS-MIL-*` files on disk (seed: HS-RM22-IART-MIL-console-ia-restructure) predate this schema row and are described by it retroactively.
729
-
730
- ```yaml
731
- type: milestone
732
- status: # one of: open | in_progress | closed (subset of the universal enum)
733
- epics: # list of wikilinks to every child epic — the mirror of each child epic's
734
- # `milestone:` pointer. Write both sides (bidirectional linkage; see [[spec-hierarchy]]).
735
- informed_by: # optional, list of wikilinks to HS-RES-* — research docs that enhance this milestone.
736
- # Reverse of `research.enhances:` (each research enhances exactly one milestone, M:1;
737
- # one milestone may be informed by many research docs over time). Bidirectional
738
- # consistency enforced by `scripts/validate-vault-ids.mjs`. See §Provenance edges.
739
- target_date: # optional, ISO-8601 date or empty string "" — the intended ship date
740
- tags: # optional, list of strings
741
- related: # optional, list of wikilinks
742
- ```
743
-
744
- **No scalar `priority:` field.** Milestone ordering is the **output** of the deterministic priority scheduler, never a stored scalar (deterministic-priority-scheduler-supersedes-scored-priority, HS-78E7-Y9OJ-EPIC-deterministic-priority-scheduler). This deliberately diverges from the universal "`priority:` required" rule above — the same way current epics and tasks omit the scalar. Do not add a `priority:` to a milestone.
745
-
746
- Sub-milestones are **out of scope**: large milestones are split horizontally (more milestones at the same level), not nested ([[spec-hierarchy]] §Open questions). There is no `milestone:` (parent-milestone) field on a milestone.
747
-
748
- Required body sections ([[spec-hierarchy]] §Frontmatter requirements per level): rationale ("why this is a milestone, not loose epics"), an acceptance gate (numbered closure conditions), suggested landing order, and out-of-scope.
749
-
750
- #### `diagnose`
751
-
752
- A user-reported **bug fix** as first-class executable work — claimable and decomposable like a task, but typed so it reads as "a reported bug to fix," not planned roadmap work. Introduced by HS-OXBS-BFIX-MIL-bug-fix-diagnose-lane (milestone-precedent, no standalone ADR). Standalone by default; an optional `parent:` (epic OR milestone) groups related fixes. Surfaces on the Resolution surface as a derived `kind: "bug"` row (the row is derived; this spec is the work — see blockers-are-gating-subset-of-resolution and HS-AO2F-1KZK-RES-bug-fix-resolution-task-entity-model-and).
753
-
754
- ```yaml
755
- type: diagnose
756
- status: # universal enum: open | in_progress | in_review | closed | …
757
- severity: # REQUIRED — one of: error | warning | info. Maps onto the scheduler's
758
- # existing priority signals (channel B): a higher-severity diagnose
759
- # competes higher for on-deck. Drives the derived bug row's color.
760
- parent: # OPTIONAL — single wikilink to an HS-EPIC-* or HS-MIL-* parent.
761
- # Standalone (absent) is the DEFAULT and is exempt from the
762
- # epic→milestone mandatory-parent rule. One-way pointer (no auto back-link).
763
- depends_on: # optional, list of wikilinks — tasks/specs that must close first.
764
- blocks: # optional, list of wikilinks to HS-EPIC-*/HS-TASK-* this fix gates.
765
- # Surfaces as a gating item in that spec's On-Deck overlay (channel A).
766
- promoted_from: # optional, single wikilink to HS-CAP-* — set when this diagnose was promoted
767
- # from a bug-report capture via `heyspex_promote_capture_to_diagnose`.
768
- # Reverse of `capture.promoted_to:` (first-promotion).
769
- derived_from: # optional, single wikilink to HS-RES-* — the research that surfaced this bug,
770
- # set when this diagnose was promoted from a research investigation via
771
- # `heyspex_promote_research_to_diagnose` (mirrors `epic.derived_from`).
772
- # Reverse of `research.produces_diagnose:` (1:N).
773
- tags: # optional, list of strings
774
- related: # optional, list of wikilinks
775
- ```
776
-
777
- **No scalar `priority:`** — like task/epic/milestone, ordering is the scheduler's output; `severity:` is the diagnose's contribution to that ranking, not a stored priority.
778
-
779
- Required body sections: Symptom, Reproduction, Root cause, Fix, Acceptance criteria.
780
-
781
- #### `pillar`
782
-
783
- Perpetual categorical container that never closes — the cross-cutting axis to which every epic associates via a primary `pillar:` field. Pillars are the structural primitive that makes HeySpex's "preserve context across time" claim true rather than aspirational ("Linear and Jira are queues; HeySpex is context" — see heyspex-market-positioning). Introduced 2026-05-31 per HS-AWDI-PILR-RES-pm-perpetual-container-terminology; ratified by introduction lineage HS-EBIL-0011-EPIC-goal-hierarchy-l2-strategic-pillars-l3 (milestone precedent, no standalone ADR for the entity itself). The meta-Pillar — the Pillar about Pillars themselves, dogfooding instance — is HS-PILR-pillars.
784
-
785
- ```yaml
786
- type: pillar
787
- status: # `perpetual` — non-terminal. Pillars never transition to closed/in_review/etc.
788
- # Universal status enum does NOT apply.
789
- canonical: # required, boolean
790
- # true → curated by HeySpex team; ships pre-loaded in customer vaults; read-only (fork to customize)
791
- # false → customer-scoped Pillar (filed by the customer with high friction)
792
- library_tier: # optional enum: meta | foundational | discretionary (placeholder; library taxonomy TBD)
793
- secondary_pillars: # always present; usually empty []. Reserved for future cross-Pillar linking.
794
- version: # optional, monotonic integer — same semantics as the `l1` `version` field above.
795
- # A Pillar edit that materially changes scope bumps this; epics aligned against the
796
- # prior version surface as `drift` items ([[HS-E3D3-RDFT-EPIC-alignment-drift-detector-into-resolution]]).
797
- last_edited_at: # optional, ISO-8601 timestamp of the last edit — drift timestamp fallback (see `l1`).
798
- related: # optional, list of wikilinks
799
- tags: # optional, list of strings
800
- ```
801
-
802
- The `version` / `last_edited_at` fields are the versioning prerequisites carried from HS-EBIL-0011-EPIC-goal-hierarchy-l2-strategic-pillars-l3; together with the L1 fields they let the alignment-drift detector tell a material Pillar revision apart from a no-op touch.
803
-
804
- **Per-epic membership** (new fields on every epic, added 2026-05-31 by HS-EBIL-0011-EPIC-goal-hierarchy-l2-strategic-pillars-l3):
805
-
806
- ```yaml
807
- pillar: # required on every epic, single wikilink to a Pillar.
808
- # The home for library/learning. Forces "what perpetual concern does this advance?"
809
- # at filing time. HARD-BLOCKED at the filing boundary (owner decision 2026-06-18):
810
- # POST /api/specs / heyspex_file_spec refuse an epic with no resolvable HS-PILR pillar
811
- # (400 pillar_required). Legacy pillar-less epics are caught by the EPIC_MISSING_PILLAR
812
- # vault-rule — warning (Resolution backfill queue) until 2026-07-31, then error.
813
- # Full rules: [[pillar-assignment-protocol]].
814
- secondary_pillars: # optional, list of wikilinks to Pillars.
815
- # For genuinely cross-cutting work ("Add caching layer" is primarily Scaling but also Reliability).
816
- # Tasks inherit their epic's primary pillar; do not set secondary on tasks.
817
- ```
818
-
819
- **Authored as the work surfaces themes.** Pillars are created over time, not enumerated up front — there are deliberately only a few today and many more are expected. When filing an epic surfaces a theme with no pillar yet, the agent is prompted to author one (`heyspex_file_spec({ type: "pillar", name })`); the owner may also author a pillar standalone whenever a theme is foreseen. The procedure is pillar-assignment-protocol.
820
-
821
- **Milestones surface a derived pillar roll-up.** A milestone has **no authored `pillar:` field** — it is a dated ship that groups epics, which may belong to different pillars. Instead a milestone *surfaces* the de-duped set of pillars its child epics carry (computed in `getSpec`, rendered on the milestone detail drawer). The roll-up is derived and may list multiple pillars, so it never drifts from the epics' `pillar:` fields.
822
-
823
- **Status semantics.** Pillars carry `status: perpetual` as a single non-terminal value, distinct from the universal enum. The close-out protocol skips Pillars; the scheduler skips Pillars as on-deck candidates (only their epics are scheduled); review-doc flow does not apply. A Pillar's body grows over time as epics close and lessons accumulate against the concern — accumulation IS the point.
824
-
825
- **Body sections.** Required: a one-line "why this Pillar exists" hook; `## In scope` / `## Out of scope`; `## Anticipated epics`; `## Library status` (canonical | customer-scoped). The body is a living document, not a frozen artifact.
826
-
827
- **Disambiguation — three distinct uses of "pillar" in this vault:**
828
-
829
- 1. **`type: pillar` entities (`HS-PILR-*`)** — this section. Perpetual product concerns (Scaling, Reliability, DX, etc.). Queryable, schematized.
830
- 2. **HS-DOC-architecture-foundations** (renamed 2026-05-31 from `architecture-pillars`) — L2 doc that names the five technical foundations of the system (vault, indexer, console, CLI, sync mesh) and refers to them as "Pillars 0–5" in internal section headers. Unrelated to (1); the section names are preserved for now and may be renamed in a future cosmetic pass.
831
- 3. **HS-OR8V-0010-EPIC-north-star-vision-first-class-entity's FIVE COMPONENTS section** — five implementation parts of that epic. Previously named "FIVE PILLARS"; renamed 2026-05-31 to remove the overload.
832
-
833
- Only sense (1) is a queryable entity. (2) and (3) use the word as a non-schematized noun within their own documents.
834
-
835
- #### `branch-brief`
836
-
837
- A durable "plan receipt" for one git branch. Ratified by adr-0008-branch-brief-and-evidence-schema; closes the Continuity Layer schema gaps (HS-7KOO-0022-EPIC-continuity-layer-gaps). One brief per branch; re-generation overwrites the file in place (git history is the version trail). Generated by HS-838Y-ALDY-TASK-branch-brief-generation.
838
-
839
- ```yaml
840
- type: branch-brief
841
- branch: # required, string — the raw (unsanitized) git branch name
842
- repo: # optional, string — repo name if multi-repo
843
- goal: # required, string — what this branch is trying to accomplish
844
- implements: # optional, list of wikilinks — tasks/epics this branch implements
845
- plan: # optional, list of { step: int, description: string }
846
- # the ORIGINAL intent. Sourced (priority order) from:
847
- # 1. the linked implementing spec's acceptance criteria
848
- # (its `acceptance:` array, else its `## Acceptance` body)
849
- # 2. a manual plan authored here (exploratory branches)
850
- # Commit messages / PR bodies are NOT v1 plan sources.
851
- actual: # optional, list of { step: int, description: string,
852
- # status: <drift enum>, evidence: [<evidence ref>] }
853
- # what was implemented, captured at brief-generation time.
854
- risks: # optional, list of strings — identified risks/concerns
855
- open_questions: # optional, list of strings — unresolved questions
856
- next_steps: # optional, list of strings — recommended next actions
857
- decisions: # optional, list of wikilinks — memory-decisions made on this branch
858
- evidence: # optional, evidence array (see "Evidence linking" below)
859
- ```
860
-
861
- **Drift enum** (the `status:` of each `actual` step) — defined once here, reused by HS-838Y-ALDY-TASK-branch-brief-generation and any drift tooling (HS-CM22-0019-EPIC-post-edit-alignment-drift-detection):
862
-
863
- | Value | Meaning |
864
- | --- | --- |
865
- | `match` | Implementation achieves the planned step. |
866
- | `drift` | Implementation differs from the plan in a material way. |
867
- | `missing` | Planned step not implemented. |
868
- | `extra` | Implemented change that was not in the plan. |
869
-
870
- Required body sections: `## Goal`, `## Plan vs Actual` (a table rendering `plan`/`actual` with an `Evidence` column), and — when any step is `drift`/`missing`/`extra` — a `## Drift Analysis` section explaining each, with risk and a recommendation. Optional: `## Decisions`, `## Risks`, `## Open Questions`, `## Next Steps`, `## Evidence`. The frontmatter `plan`/`actual` arrays are canonical for queries; the body table is the human view (same canonical-frontmatter rule as dependencies and tags).
871
-
872
- #### `memory-lesson`
873
-
874
- ```yaml
875
- type: memory-lesson
876
- topic: # required, short string — the subject
877
- observed: # required, ISO-8601 date — when the lesson was learned
878
- related: # optional, list of wikilinks
879
- evidence: # optional, evidence array — sources grounding the problem/fix
880
- # (see "Evidence linking" below)
881
- ```
882
-
883
- The body of a memory-lesson spec has two required sections: `## Problem` and `## Fix`.
884
-
885
- #### `memory-decision`
886
-
887
- ```yaml
888
- type: memory-decision
889
- topic: # required, short string
890
- decided: # required, ISO-8601 date
891
- deciders: # required, list of strings
892
- supersedes: # optional, wikilink to a prior decision
893
- evidence: # optional, evidence array — sources grounding the rationale
894
- # (see "Evidence linking" below)
895
- ```
896
-
897
- The body has two required sections: `## Decision` and `## Rationale`. (An ADR is a heavier-weight decision; a `memory-decision` is the day-to-day choice that doesn't warrant an ADR but should still be persisted.)
898
-
899
- #### `memory-reference`
900
-
901
- ```yaml
902
- type: memory-reference
903
- topic: # required, short string
904
- url: # optional, URL the reference points at
905
- evidence: # optional, evidence array — additional grounded sources
906
- # (see "Evidence linking" below)
907
- ```
908
-
909
- Body is the brief context for why this reference matters.
910
-
911
- #### `memory-ui-pattern`
912
-
913
- ```yaml
914
- type: memory-ui-pattern
915
- topic: # required, short string — the pattern name/slug
916
- documented: # required, ISO-8601 date — when the pattern was captured
917
- related: # optional, list of wikilinks
918
- evidence: # optional, evidence array
919
- ```
920
-
921
- Reusable **console interaction and visual patterns** (keyboard ownership, two-layer highlights, drill-in between panes). Distinct from `memory-ux-principle` (cross-brand rules) and `memory-decision` (a one-time choice). Body sections: `## Pattern`, `## When to apply`, `## Rules`, `## Implementation` (file map + copy-paste shape). See rail-active-vs-keyboard-cursor.
922
-
923
- #### `memory-ux-principle`
924
-
925
- ```yaml
926
- type: memory-ux-principle
927
- topic: # required, short string — the principle name/slug
928
- documented: # required, ISO-8601 date — when the principle was captured
929
- related: # optional, list of wikilinks
930
- evidence: # optional, evidence array
931
- ```
932
-
933
- Cross-brand **interaction rules** (Apple HIG, Material, Linear, Nielsen) — product-agnostic **what/why**. Distinct from `memory-ui-pattern` (HeySpex **how**). Body sections: `## Principle`, `## When to apply`, `## Rules`, `## Related UI patterns`, `## Sources`. See toggle-current-state-semantics and ux-principles-vs-ui-patterns.
934
-
935
- #### `protocol`
936
-
937
- Part of the **Memory** layer (memory-protocols-subsection-of-memory). Epics/tasks that *implement* or *revise* a protocol stay in `epics/` / `tasks/`; only the finished operational doc is `type: protocol`.
938
-
939
- ```yaml
940
- type: protocol
941
- scope: # required, one of: session | project | vault | tool
942
- version: # optional, semver-ish version of the protocol
943
- ```
944
-
945
- Body describes the protocol with numbered steps.
946
-
947
- #### `review`
948
-
949
- Companion fill-in workspace for a parent spec at `status: in_review`. One review doc per parent spec; created by the agent at the moment of flipping the parent to `in_review`. The reviewer (human) walks acceptance criteria, reads the agent's session observations, scores memory candidates, and records pass/reopen. Naming: `HS-REV-<suffix>-<slug>.md` where `<suffix>` matches the parent's id 1:1 (`HS-76RX` → `HS-SK1L-REV-…`, `HS-KQ5O` → `HS-IDFV-REV-…`).
950
-
951
- ```yaml
952
- type: review
953
- parent: # required, single wikilink — the spec under review
954
- reviews: # optional, single wikilink — same as parent (legacy alias retained for back-compat reads)
955
- # parent: is the canonical edge; reviews: is kept for older docs
956
- deliberation_record: # optional; Council session output for invocation #4 (review close-out)
957
- # ([[adr-0015-council-as-cross-cutting-capability]]). Schema lives under `draft`
958
- # §`deliberation_record` above. Attached when the human triggers council during
959
- # `/hs-review` close-out; never gates the human's pass/reopen decision.
960
- ```
961
-
962
- Body has four required sections — the unified workspace:
963
-
964
- - `## Acceptance verification` — checkboxes mirroring the parent spec's acceptance criteria; reviewer ticks them during verification and adds inline deviation notes. For epic-level reviews, expand into a manual testing walkthrough (fast-fail commands, click-by-click steps, expected behavior the reviewer can follow top-to-bottom).
965
- - `## Reviewer notes` — free-form workspace. **The agent writes implementation observations here during the work session** (architecture choices, judgment calls, surprises, gotchas). The reviewer adds deviation/surprise notes during the testing pass. This prose is the raw material the close-out memory-extraction pass reads.
966
- - `## Memory candidates` — table of proposed lessons / decisions / UI patterns / references derived from the Reviewer notes. Each row scored against the four-axis rubric (non-derivability / reusability / surprise-cost / concreteness, 0–3 each, threshold ≥ 7/12) and **MUST include a "Conflict check" column** noting `New`, `Refines [[existing-memory]]`, or `Conflict [[existing-memory]]`. A `Conflict` row is a red flag and must be resolved before accepting. Owned by HS-35JN-REVD-TASK-review-doc-fill-in-template-and-memory-extraction.
967
- - `## Decision: close or reopen` — explicit pass/fail outcome with reasoning, plus a checkbox indicating whether to materialize accepted memory candidates.
968
-
969
- Cross-link from the parent spec: every parent spec at `in_review` MUST add a `## Review` section pointing to its companion: `Reviewer workspace for this spec lives at [[HS-REV-<suffix>-<slug>]] — pre-review checklist, manual verification steps, reviewer notes, and memory candidates extracted at close-out.`
970
-
971
- The console shows a "Review" button on cards only when the parent spec is `status: in_review` and an `HS-REV-<suffix>-*` companion exists (detection: `console/src/lib/vault.ts:155 extractSpecSuffix`). Once the parent moves to `closed`, the button disappears but the wikilink in the parent body remains.
972
-
973
- The four-section unified-workspace template and the close-out flow are specified in HS-35JN-REVD-TASK-review-doc-fill-in-template-and-memory-extraction and CLAUDE.md §Session Completion. The MCP tool that loads a review doc into agent context (`heyspex_review_on_deck` + `/hs-review`) is specified in HS-XRO1-RVDC-TASK-heyspex-review-on-deck-mcp-and-hs-review. The legacy `{basename}.review.md` sibling naming was migrated by `scripts/migrate-review-doc-naming.mjs` — do not use it for new docs.
974
-
975
- #### `business-positioning` / `business-pricing` / `business-marketing` / `business-model`
976
-
977
- Parallel layer to Memory for **business-side** knowledge: how the product is positioned, priced, marketed, and the business model itself. Like Memory, browsed in the console on its own surface (**Business**) with category filters.
978
-
979
- ```yaml
980
- type: business-positioning # or business-pricing | business-marketing | business-model
981
- topic: # required, short string — the subject (e.g. "homepage-h1", "tier-table")
982
- status: # universal status enum — draft | open | in_progress | in_review | closed | deferred
983
- # `active` is allowed as a synonym for `open` on migrated docs.
984
- related: # optional, list of wikilinks
985
- evidence: # optional, evidence array — sources grounding the claim/decision
986
- ```
987
-
988
- Body is free-form prose; the recommended shape is a one-line H1 frame, then sections that match the doc's purpose (e.g. **Decision** / **Why each word is load-bearing** for positioning; **Tier table** / **AI add-on** / **What this also unlocks** for pricing).
989
-
990
- Distinct from `memory-decision` — memory captures *how-we-work* decisions for agents/humans inside the project; business docs capture *what-we-sell-and-how* decisions for the outside world. A pricing change is a `business-pricing` doc; a "don't mock the database in tests" rule is a `memory-decision`.
991
-
992
- #### `capture`
993
-
994
- Concise core-intent capture **before** research (capture-guardrails). Replaces the legacy `type: idea` entity (renamed 2026-05-22 per HS-B2B8-DLQI-EPIC-capture-rename-migration-idea-to-capture, source rationale HS-ZKFN-1FXG-CAP-capture-as-universal-companion-verb-and-parent-term). Not a draft inbox item and not a research doc.
995
-
996
- ```yaml
997
- type: capture
998
- kind: # required, one of: idea | bug-fix | clip | reference | question | spec-import | feedback
999
- # idea → speculative concept worth exploring (default — migrated entries land here)
1000
- # bug-fix → defect observation paired with proposed fix
1001
- # clip → web/document excerpt saved verbatim for later use
1002
- # reference → a pointer to an external resource worth keeping
1003
- # question → an open question to revisit when more context lands
1004
- # spec-import→ an imported foreign doc (README / top-level project doc) landed as a RAW
1005
- # capture by the bulk spec-import feature ([[HS-8V8T-8SBG-EPIC-first-connect-rough-import]]);
1006
- # source doc in ## Source Material; carries source_repo/source_path for
1007
- # (repo, vault) idempotency; stays RAW for manual triage. The
1008
- # "pin → run feature pipeline" exit ([[HS-J7T5-VEW8-RES-import-specs-one-click-feature-pipeline]])
1009
- # is a deferred follow-on — NOT wired in this wave.
1010
- # feedback → app feedback / bug report / product idea / support question captured through
1011
- # a deterministic (no-AI) intake channel ([[HS-KQEW-I5DV-EPIC-feedback-capture-type-deterministic]]);
1012
- # classified by `routeFeedback()` ([[HS-FUII-EI9R-TASK-deterministic-feedback-router-module-no]]) toward diagnose/research/
1013
- # answered, or stays `feedback` when unmatched. Optional structured fields below
1014
- # (declared alongside `source:` further down this block — see `example`/
1015
- # `user_story`/`situation`).
1016
- example: # optional, string — a concrete example/repro illustrating the feedback (e.g. steps to reproduce a bug).
1017
- # Only ever populated on `kind: feedback` captures; never forced, never required by the schema.
1018
- user_story: # optional, string — "as a user, I want to… so that…" framing for a feedback-sourced product idea.
1019
- # Same optionality as `example`.
1020
- situation: # optional, string — the situational context the user was in when the feedback arose.
1021
- # Same optionality as `example`.
1022
- capture_status: # required, one of: raw | expanded — default raw
1023
- # raw → no Core Idea content yet (single textarea / quick capture)
1024
- # expanded → Core Idea / Concept / Details has been written (modal v3 or agent rewrite)
1025
- # Flips raw → expanded automatically when an agent writes to core_idea,
1026
- # concept, or agent_expansion via executeUpdateCapture (vendor/heyspex-mcp).
1027
- # Console save-time linter force-corrects raw → expanded when `# Core Idea`
1028
- # body has content. See [[HS-BDDT-Q1B7-EPIC-idea-capture-modal-v3-single-textbox-cle]].
1029
- promoted_to: # binary: null (not promoted) or "[[HS-RES-<suffix>-<slug>]]" (promoted to research).
1030
- # Records the FIRST research doc this capture was promoted into; not the full set.
1031
- # For the M:N "every research doc this capture informs" view, see `informs_research:`.
1032
- informs_research: # optional, list of wikilinks to HS-RES-* — every research doc this capture informs (M:N).
1033
- # Reverse of `research.sources_captures:`. Distinct from `promoted_to:` (binary
1034
- # first-promotion pointer); a capture may appear in multiple research docs over time.
1035
- # Bidirectional consistency enforced by `scripts/validate-vault-ids.mjs`. See §Provenance edges.
1036
- source: # optional capture channel (console, cli, companion, agent, …). On `kind: feedback` captures this
1037
- # also doubles as the free-text originating-channel/page-context signal `routeFeedback()`
1038
- # reads ([[HS-FUII-EI9R-TASK-deterministic-feedback-router-module-no]]) — still optional, never forced.
1039
- related: # optional wikilinks
1040
- legacy_idea_id: # optional, string — original HS-IDEA-* id for migrated entries (archaeology)
1041
- ```
1042
-
1043
- `promoted_to` is the **single binary promotion field** (HS-KQ5O-IDFV-EPIC-unified-idea-workflow-v2 / HS-QOAC-IDFL-RES-unified-idea-workflow-v2). `research_stage` was removed 2026-05-22; intermediate "promoted-but-research-not-written-yet" state lives in `.heyspex/research-focus.yaml`, not in the spec.
1044
-
1045
- `kind:` is the taxonomy field locked in by HS-B2B8-DLQI-EPIC-capture-rename-migration-idea-to-capture Milestone A. Migration of pre-2026-05-22 `type: idea` entries set `kind: idea` for every record; new captures pick the kind at creation time. The enum was extended from 5 to 6 values on 2026-07-10 — `spec-import` added as the **import-origin** kind for the bulk spec-import feature (HS-8V8T-8SBG-EPIC-first-connect-rough-import / HS-ROIU-9P4B-EPIC-import-preview-and-confirm, grounding HS-J7T5-VEW8-RES-import-specs-one-click-feature-pipeline). `spec-import` was added as a documentation/protocol convention only, not to the hard validator enum — `scripts/validate-vault-schema` (`schemas/heyspex-frontmatter.schema.json`) only required that a capture carry a `kind:` and a `capture_status:`, so `spec-import` slipped past AJV. `feedback` (7th value, HS-KQEW-I5DV-EPIC-feedback-capture-type-deterministic / HS-PUK1-48ZZ-TASK-add-feedback-capture-kind-optional) was added to BOTH the doc above and the hard AJV `kind` enum in `schemas/heyspex-frontmatter.schema.json` — a `kind: feedback` capture passes `scripts/validate-vault-schema.mjs` the same way the original five values do. Further consolidation (getting `spec-import` onto the hard enum too) is still deferred.
1046
-
1047
- Body **must** use four named sections — `# Core Idea` / `## Notes` (verbatim human words inside `"quotes"`) / `## Concept` (AI brief summary, ≤1200 chars hard / ≤800 soft) / `<details>Details</details>` (AI synthesis / deep dive, collapsed by default). Core Idea ≤280 chars soft / ≤500 hard. (The body section names retain the legacy "Idea" / "Notes" / "Concept" / "Details" wording even for captures whose `kind:` is not `idea`; the headings are a fixed scaffold, not a taxonomy field — agents and humans use the same four-section shape for every kind.) Legacy `# Core idea` / `## Notes` / `<details>Expanded idea (agent)</details>` is read back-compat during the deprecation window; writes always emit v2. AI may write to any section but: (a) never alters words inside a quote, (b) `## Notes` format-wrap is confirm-free, (c) `# Core Idea` / `## Concept` / `## Details` writes require user confirm. Agents must not add research sections (Executive Summary, Findings, Sources) to capture files — only the separate research doc after `/hs-res` (capture-guardrails).
1048
-
1049
- #### `research`
1050
-
1051
- Research is **evergreen** per the lifecycle-class taxonomy above and the canonical decision research-is-evergreen. `status: open` is the resting state for the doc's entire life — there are no `in_review` or `closed` transitions. Lineage to downstream artifacts is recorded through the relational fields below (`enhances:`, `spawned_epics:`, `bound_epic:`, `incorporated_into:`, `superseded_by:`), **never** through a status flip.
1052
-
1053
- ```yaml
1054
- type: research
1055
- research_type: # REQUIRED, enum — the declared purpose of this research doc, set at creation
1056
- # ([[HS-45YJ-120I-EPIC-research-type-field-the-declared-purpose]]). Coherent with the
1057
- # capture `kind:` vocabulary (`idea | bug-fix | clip | reference | question`) one
1058
- # level up the lifecycle, but not identical — research_type describes what the
1059
- # research is FOR, not what kind of capture seeded it. Five values:
1060
- # new-feature — grounds a not-yet-built capability or product surface
1061
- # enhancement — improves or extends something already shipped
1062
- # diagnose — investigates a bug, defect, or quality problem
1063
- # code-audit — reviews existing code/architecture with no specific bug in hand
1064
- # reference — captures external/background knowledge for reuse (the default)
1065
- # Enforced by `schemas/heyspex-frontmatter.schema.json`. Missing on a pre-existing
1066
- # doc is a WARNING during the grace window (`RESEARCH_TYPE_GRACE_END` in
1067
- # `spex-console/src/lib/indexer/vault-rules.mjs`, mirroring `EPIC_PILLAR_GRACE_END`),
1068
- # then a hard error. Deterministic kind→research_type suggestion is out of scope
1069
- # here — owned by [[HS-6CFV-MVC1-EPIC-deterministic-type-assist-suggest-resear]].
1070
- scope: # optional, enum — how broad a search context this research was run at:
1071
- # internal — specs/, memories, ADRs only
1072
- # codebase — the repo (console/, vendor/, scripts/, etc.)
1073
- # external — web search
1074
- # all — vault + codebase + web
1075
- # Default "internal" when omitted. This is a CONSISTENCY fix, not a new
1076
- # requirement — scope is NOT required and has no grace window
1077
- # ([[HS-NGD1-TASK-consolidate-and-fix-the-research-scope-field]]). Written the
1078
- # same way (same enum, same default) by `heyspex_save_research`,
1079
- # `heyspex_promote_capture`, and `heyspex_complete_research`. Validated as a
1080
- # string enum by `schemas/heyspex-frontmatter.schema.json` (distinct from the
1081
- # unrelated freetext `scope:` on `type: protocol`).
1082
- enhances: # REQUIRED when set — bidirectional consistency rule. If `enhances:` exists, the
1083
- # target milestone MUST list this research in `informed_by:` (see §Provenance edges).
1084
- # Single wikilink to the HS-MIL-* this research enhances. Not an array (M:1 rule).
1085
- # Open research may omit during exploration; the validator enforces bidirectional
1086
- # consistency whenever the field IS set. A research doc that spans multiple
1087
- # milestones must be split first ([[HS-TQQS-GTH9-TASK-hs-split-research-mcp-tool]]).
1088
- # NOTE: `scripts/validate-vault-ids.mjs` still gates on the legacy
1089
- # `status: closed` framing — validator catch-up to this "when set" framing is on
1090
- # the deferred list in [[research-is-evergreen]].
1091
- sources_captures: # optional, list of wikilinks to HS-CAP-* — captures this research aggregates (M:N).
1092
- # Reverse of `capture.informs_research:`. Bidirectional consistency enforced.
1093
- spawned_epics: # optional, list of wikilinks to HS-EPIC-* — epics this research spawned (1:N).
1094
- # Reverse of `epic.derived_from:`.
1095
- enhances_epics: # optional, list of wikilinks to HS-EPIC-* — already-existing epics this research
1096
- # augmented (M:N, rare). One-sided in v1 — no reverse field on epic.
1097
- produces_diagnose: # optional, list of wikilinks to HS-DIAG-* — bugs found during this research and
1098
- # promoted to a diagnose (1:N, mirrors `spawned_epics`). Reverse of
1099
- # `diagnose.derived_from:`. Written by `heyspex_promote_research_to_diagnose`
1100
- # ([[HS-W5VP-MSHY-TASK-research-diagnose-link-send-bugs-to-the]]); reuses the existing
1101
- # diagnose machinery — see [[resolution-diagnose-workflow]].
1102
- split_into: # optional, list of wikilinks — on the original (archive) research when split;
1103
- # names the child research docs that carry forward.
1104
- split_from: # optional, single wikilink — on a research created by splitting from an ancestor;
1105
- # mirror of `split_into:` on the ancestor.
1106
- source_capture: # optional wikilink to HS-CAP-* — legacy single-capture seed pointer; retained for
1107
- # read-side back-compat. New research uses `sources_captures:` (M:N) as canonical.
1108
- planning_flow: # optional until flow filed; then wikilink to HS-FLOW-*
1109
- bound_epic: # optional; set when epic filed — locks findings to append-only ([[planning-flow-research-epic-lifecycle]])
1110
- forked_from: # optional, wikilink — source HS-RES when forked for a new epic
1111
- ```
1112
-
1113
- Body is the research note. After `bound_epic:` is set, findings sections are append-only; new epic scope uses `## Scope for <epic name>` addendum sections.
1114
-
1115
- ##### Code-audit findings table (`research_type: code-audit`)
1116
-
1117
- A `research_type: code-audit` doc's body **must** carry a findings table — the authored standard for "reviewed the code, found N things, here's what happens to each" (HS-7D0I-WL88-TASK-code-audit-research-type-with-a-defined). Four columns, in this order:
1118
-
1119
- | Finding | Severity | Action (diagnose\|task\|reference) | Exit (open\|closed) |
1120
- | --- | --- | --- | --- |
1121
- | One-line description of what was found | freetext (e.g. high/med/low) | `diagnose` \| `task` \| `reference` | `open` \| `closed` |
1122
-
1123
- Every row's `Action` maps to exactly one of three defined exits — no fourth "just leave it" option:
1124
-
1125
- - **`diagnose`** — a real bug. Promote it with the existing `heyspex_promote_research_to_diagnose` tool (HS-W5VP-MSHY-TASK-research-diagnose-link-send-bugs-to-the), which writes `research.produces_diagnose:` / `diagnose.derived_from:` (see §Provenance edges). Not new machinery — reuse it.
1126
- - **`task`** — a cleanup/follow-up with no bug attached. File a task and set its `discovered_from: [[HS-RES-…]]` back to this research doc (see `task.discovered_from` above and §Dependencies "Discovered-from").
1127
- - **`reference`** — non-actionable; the row itself IS the exit. Set `Exit: closed` directly, no further linkage required.
1128
-
1129
- **Enforcement** (`spex-console/src/lib/indexer/vault-rules.mjs`):
1130
-
1131
- - `CODE_AUDIT_FINDINGS_MALFORMED` — no findings table, or one that doesn't match the 4-column header shape, or a data row with an unrecognized `Action`/`Exit` value. Warning during the `CODE_AUDIT_GRACE_END` grace window (mirrors `RESEARCH_TYPE_GRACE_END`'s pattern — pre-existing code-audit docs like `HS-ZHAB-RES-doc-authoring-contract-audit-required-st` predate this format and are exempt while the window is open), then a hard error.
1132
- - `CODE_AUDIT_FINDINGS_OPEN_ON_CLOSE` — the **closure gate**: a code-audit doc at `status: closed` or `review_pending` with any finding row whose `Exit` is `open`. Warning during the same grace window, then error — blocks closing a code-audit doc until every finding has exited.
1133
-
1134
- #### `planning-flow`
1135
-
1136
- Orchestration artifact for epic filing (planning-flow-research-epic-lifecycle). Body contains a `## Planning flow` section with fenced `mermaid` (semantic source for canvas projection per spec-diagram-design-vs-as-built-canvas).
1137
-
1138
- ```yaml
1139
- type: planning-flow
1140
- research: # required, wikilink to HS-RES-*
1141
- decisions: # optional, list of wikilinks — memory-decision or adr
1142
- epic: # optional until epic filed; then wikilink to HS-EPIC-*
1143
- has_diagram: # required, true — indexes canvas bridge + Shipped gallery when closed
1144
- related: # optional, wikilinks to tasks and supporting specs
1145
- ```
1146
-
1147
- #### `roadmap`
1148
-
1149
- ```yaml
1150
- type: roadmap
1151
- roadmap_manifest: # required, machine-readable phase index (see below)
1152
- related_roadmaps: # optional, wikilinks to other HS-ROADMAP-* specs
1153
- ```
1154
-
1155
- `roadmap_manifest` shape (indexer validates; agents request via `--compact`):
1156
-
1157
- ```yaml
1158
- roadmap_manifest:
1159
- version: 1
1160
- phases:
1161
- - id: M2 # short stable id
1162
- name: Indexer MVP
1163
- tasks: [HS-V3YS, HS-1BML] # HS-NNNN or HS-EPIC-NNNN ids
1164
- horizon: # may not ship — priority-4 / tag:horizon backlog
1165
- - HS-WYHZ
1166
- initiatives: # optional links to other roadmap docs
1167
- - HS-ROADMAP-VOICE-CHROME
1168
- ```
1169
-
1170
- Body is the human narrative (goals, exit criteria, risks). **Do not duplicate** the task list in prose without mirroring ids in `roadmap_manifest` — the manifest is the console/CLI source of truth for ordering.
1171
-
1172
- Horizon items use native `HS-NNNN` ids (or wikilinks to `future-considerations/` research until promoted). No separate FC id namespace (adr-0012-roadmap-storage).
1173
-
1174
- #### `draft`
1175
-
1176
- ```yaml
1177
- type: draft
1178
- source: # optional, where this draft came from (URL, "phone capture", "screenshot", etc.)
1179
- captured_at: # required, ISO-8601 datetime
1180
- source_intent_hash: # optional but required on Agent ingest; SHA-256 of canonical capture blob ([[plan-ingest-review-roadmap-workflow]])
1181
- source_intent: # optional verbatim excerpt (≤2 KB) of human inputs at capture time
1182
- agent_output_hash: # optional; SHA-256 of structured Agent preview at Save-to-drafts
1183
- review_stage: # optional, enum: inbox | deliberating | staging | review | promoted | rejected
1184
- council_requested: # optional bool | null — per-submit override; null = apply deliberation.triggers policy ([[council-opt-in-pre-review-conditioning]])
1185
- triage_record: # optional; scout pre-pass ([[council-triage-scout-optional-llm]], [[review-inbox-workshop-scout-overview]])
1186
- deliberation_record: # optional; Gate 0 output ([[ai-deliberation-gate]]) — append-only runs
1187
- suggestions: # optional; staged Gate A records ([[HS-EU98-GCGE-TASK-staged-draft-suggestion-schema]]) — omit when empty
1188
- review_record: # optional; Gate A commit audit — who/when/per-suggestion outcomes
1189
- ingest_mode: # optional, enum: manual | agent_llm — default manual when omitted
1190
- capture_scope: # optional, enum: selection | full — did a highlight scope the body? (HS-M54O); absent ⇒ full
1191
- purpose: # optional free string — the user's stated "why" for capturing (HS-7S40)
1192
- context_blocks: # optional list of { source: typed | voice, text, at: ISO-8601 } — enrichment
1193
- # added on the intent card (HS-JQ7O/0077), each bound to this draft's trace_id.
1194
- # Append-only within a trace_id; ordered by `at`. Absent ⇒ no extra context.
1195
- llm_route: # optional, required when ingest_mode is agent_llm — builder-selected route from console picker:
1196
- # chatgpt | gemini | claude | auto | ... (enum grows with integrations; must stay in sync with UI labels)
1197
- llm_resolved_at: # optional, ISO-8601 datetime when ingest_mode agent_llm — when the routed model finished
1198
- resolved_model: # optional free string or structured id — actual provider/model id after routing (meaningful when llm_route: auto or when aliases map to concrete models)
1199
- submission_kind: # optional, enum: external_media | refined_intent — Gate 0 epistemic mode ([[companion-external-media-vs-refined-intent-council]]); companion defaults external_media
1200
- capture_channel: # optional, enum: manual_ui | cli | voice | extension | companion | ...
1201
- companion_dialogue: # optional append-only [{ role: user|assistant, text, at }] — pre-send chat about capture ([[HS-67TW-2966-TASK-companion-pre-council-context-dialogue]]); omit when empty
1202
- capture_group_id: # optional string — shared id linking multiple captures made in one multi-capture
1203
- # companion session; drafts sharing this id render as a connected set on the
1204
- # Captures surface, ordered by capture_group_seq. Distinct from capture_bundle_id
1205
- # below (one composite draft with multiple sources) — this links multiple SEPARATE
1206
- # draft files. Omit for standalone single captures.
1207
- capture_group_seq: # optional positive integer — 1-based position within capture_group_id's group.
1208
- # Only meaningful when capture_group_id is set.
1209
- capture_bundle_id: # optional UUID — companion-local bundle id when submitted from Capture inbox ([[companion-capture-inbox-and-bundles]])
1210
- source_set: # optional — multi-source bundle; list of members, each with { url | file_path, title, member_trace_id, member_note, file_size?, file_type? }
1211
- # For web captures: url (required), title, member_trace_id, member_note ([[HS-D1YU-OVFO-TASK-bundle-submit-composite-draft-source-set]])
1212
- # For file captures: file_path (required), title (filename), file_size, file_type, member_trace_id, member_note ([[HS-5FI0-0036-EPIC-file-system-selection-voice-capture]])
1213
- # Omit for single-source captures
1214
- wake_used: # optional bool — companion wake phrase invoked (vs shortcut-only capture)
1215
- foreground_app: # optional string — OS-visible focused app title/name at capture time
1216
- spoken_command_tail: # optional sanitized transcript excerpt when capture_channel includes voice (see [[uc-voice-ingest-hey-spex]])
1217
- shortcut_binding_id: # optional stable identifier for whichever hotkey initiated capture
1218
- voice_companion_semver: # optional — installed companion semantic version emitting the draft
1219
- pointer_screen_x: # optional — mouse pointer X in screen px, sampled once at ingest trigger
1220
- pointer_screen_y: # mouse pointer Y (screen px)
1221
- pointer_dpi_scale:
1222
- pointer_sampled_at: # ISO-8601
1223
- last_click_screen_x:
1224
- last_click_screen_y:
1225
- last_click_at:
1226
- spatial_hint_ttl_ms:
1227
- chrome_viewport_ratio_x:
1228
- chrome_viewport_ratio_y:
1229
- pointer_dom_hint: # optional heuristic from Chrome elementFromPoint; non-authoritative
1230
-
1231
- # File capture provenance (when source_type: local_file) — [[HS-5FI0-0036-EPIC-file-system-selection-voice-capture]]
1232
- source_type: # optional enum: local_file | url — discriminator; url is default/implicit for browser captures
1233
- file_path: # optional string — absolute path (single file or root folder); present when source_type: local_file
1234
- file_size: # optional int — file size in bytes
1235
- file_type: # optional string — MIME type or file extension
1236
- file_modified_at: # optional ISO-8601 — file mtime at capture
1237
- folder_depth: # optional int — recursion depth when folder captured
1238
- folder_file_count: # optional int — total files in folder/zip capture
1239
- zip_extracted: # optional bool — true if zip was extracted to temp for ingestion
1240
- zip_source_path: # optional string — original zip path when zip_extracted: true
1241
- sensitive_files_included: # optional list of { path, pattern_matched, user_confirmed_at } — audit when user overrode sensitive file warning
1242
- ```
1243
-
1244
- Body is the ingested content; triage promotes a draft into a task, memory, or research file by editing the frontmatter `type:` and moving the file.
1245
-
1246
- Agent-assisted drafts **should** end the markdown body with a short human-readable provenance line so readers skimming raw files see how the draft was produced; **frontmatter carries the authoritative fields.**
1247
-
1248
- Example footer (console-rendered; values must mirror frontmatter):
1249
-
1250
- ```markdown
1251
-
1252
- ---
1253
-
1254
- _Ingest provenance:_ route `claude` → resolved `anthropic/claude-3-7-sonnet` · _Captured:_ 2026-05-16T12:00:00Z
1255
- ```
1256
-
1257
- See uc-ingest-anywhere §Console toggle: Manual vs Agent ingest, HS-DOC-architecture-foundations §What it exposes (API), companion capture + optional spatial hints in uc-voice-ingest-hey-spex §Persistence / §Spatial pointers when `capture_channel: voice`.
1258
-
1259
- **`deliberation_record`** (when present):
1260
-
1261
- Permitted on `draft`, `task`, `epic`, and `review` types — Council session output, append-only across all four ai-deliberation-gate invocation points per adr-0015-council-as-cross-cutting-capability. The host spec retains its existing type; the record is purely advisory and never gates anything (human disposition stays in Gates A/B/C per heyspex-north-star Principle 3).
1262
-
1263
- ```yaml
1264
- deliberation_record:
1265
- latest_run_id: "<uuid>"
1266
- runs:
1267
- - run_id: "<uuid>"
1268
- at: "<ISO-8601>"
1269
- invocation_point: pre_review | canvas_live | promoted_recheck | review_close
1270
- # which of the four Council invocation points produced this run
1271
- # (per [[adr-0015-council-as-cross-cutting-capability]])
1272
- submission_lens: refined_intent | external_media | promoted_spec_recheck
1273
- # role objective discriminator — lifts the draft body's
1274
- # `submission_kind` to the run so promoted-spec contexts carry it
1275
- parent_run_id: "<uuid>" | null # loop-suppression input; null on first run for a spec
1276
- test_preset: free | cheap | production | null # when [[ai-deliberation-gate]] test_mode was active
1277
- api_calls: 4 # optional audit — varies by invocation: 1 (canvas_live),
1278
- # 3 (review_close, skip rebuttal), 4 (pre_review skip_rebuttal),
1279
- # 7 (full pre_review or promoted_recheck)
1280
- roles:
1281
- - id: tech_lead | program_lead | ux_lead
1282
- provider: "<string>"
1283
- model: "<string>"
1284
- position:
1285
- disposition: schedule_now | schedule_later | horizon | defer | deprecated | merge | reject | split
1286
- priority: 0-4
1287
- roadmap_phase: "<id>"
1288
- merge_into: "[[HS-NNNN]]"
1289
- north_star_alignment: aligned | tension | conflict
1290
- arguments: ["<bullet>"]
1291
- facilitator: # absent on `canvas_live` (single-role) and on
1292
- # `review_close` when skip_rebuttal is set
1293
- recommended_disposition: "<enum>"
1294
- confidence: 0.0-1.0
1295
- disagreements: ["<role_a vs role_b on topic>"]
1296
- north_star_conflict: # present when ≥2 roles report conflict
1297
- agreed: true
1298
- summary: "<string>"
1299
- path_a_change_l1: { summary, typical: false }
1300
- path_b_reshape_plan: { summary, suggested_edits: [] }
1301
- ```
1302
-
1303
- **Storage bound:** `runs[]` is capped at the last 10 entries in frontmatter; older runs archive to `specs/.deliberation-archive/<spec-id>.yaml` (gitignored, audit-only) per adr-0015-council-as-cross-cutting-capability §Consequences.
1304
-
1305
- **`triage_record`** (when present; scout only — not council):
1306
-
1307
- ```yaml
1308
- triage_record:
1309
- at: "<ISO-8601>"
1310
- model: "<slug>"
1311
- title_line: "<≤80 chars>"
1312
- memory_jogger: "<2-3 sentences>"
1313
- review_category: feature_request | external_media | brainstorm | research | bug | mixed | unclear
1314
- recommend_council: true | false
1315
- confidence: 0.0-1.0
1316
- rationale: "<string>"
1317
- suggested_actions:
1318
- - id: run_council | add_context | gate_a_review | defer | promote_quick | discard
1319
- label: "<string>"
1320
- primary: true
1321
- quick_tags: ["<optional>"]
1322
- ```
1323
-
1324
- **`suggestions`** (when present; omit block entirely when empty — pre-HS-GCGE-TASK drafts unchanged):
1325
-
1326
- ```yaml
1327
- suggestions:
1328
- - id: "<stable-uuid-within-draft>"
1329
- kind: roadmap | priority | child_task | consideration
1330
- payload: {} # kind-specific proposed fields (epic stub, priority, etc.)
1331
- rationale: "<one line>"
1332
- disposition: pending | accepted | edited | declined
1333
- edited_payload: {} # optional; present when disposition: edited
1334
- provenance:
1335
- llm_route: "<enum>"
1336
- resolved_model: "<provider/model>"
1337
- at: "<ISO-8601>"
1338
- ```
1339
-
1340
- **`review_record`** (written on Gate A staging commit):
1341
-
1342
- ```yaml
1343
- review_record:
1344
- at: "<ISO-8601>"
1345
- by: pwher
1346
- outcomes:
1347
- - suggestion_id: "<uuid>"
1348
- disposition: accepted | edited | declined
1349
- note: "<optional>"
1350
- ```
1351
-
1352
- #### `daily`
1353
-
1354
- ```yaml
1355
- type: daily
1356
- date: # required, ISO-8601 date
1357
- ```
1358
-
1359
- Body is free-form notes for the day.
1360
-
1361
- ## Body structure conventions
1362
-
1363
- Beyond the type-specific required sections noted above, all specs follow these structural conventions:
1364
-
1365
- - The first heading is `# <Name>` (the human-readable title), matching `name:` in frontmatter.
1366
- - Optional first block: a single `> [!note]` / `> [!important]` callout if there's a one-line framing message.
1367
- - Sections use `##` headings. Sub-sections `###`.
1368
- - A spec ends with `## Related specs` (optional) and `## Change log` (required for L1, L2, L3, ADR, and protocol; optional but encouraged elsewhere).
1369
-
1370
- ### Recommended sections for long-lived specs
1371
-
1372
- Two SCEH-Pattern conventions (HS-U88E-SP34-EPIC-sceh-patterns-3-and-4-markdown-conventions) define optional `##` sections useful on specs that accumulate work across many sessions:
1373
-
1374
- | Section | Protocol | Use when | Placement |
1375
- |---|---|---|---|
1376
- | `## Session Boundaries` | session-boundary-marker | Multi-session epics, milestones, and HS-REV review docs benefit from a five-column session-by-session trail (`session_id` / `agent` / `started` / `ended` / `summary`). | Immediately above `## Change log`. |
1377
- | `## Conflict Resolution` | conflict-resolution-section | Two or more sessions left contradictory opinions in the body, change log, or HS-REV companions and a reviewer has had to pick. Three-row table (opinions + bold `**resolution**`) with optional memory-extraction routing into `memory-decision` specs. | Below `## Session Boundaries` if present, above `## Change log`. |
1378
-
1379
- Both are **recommended** — opt-in per spec, not enforced by the wikilink validator. They are content commentary about the spec; control-plane runtime state (claim/focus/spec events, handoff context) stays in Neon (control-plane-belongs-in-neon).
1380
-
1381
- ## Dependencies
1382
-
1383
- HeySpex inherits beads' four dependency types and adds a normalization choice: dependencies are expressed **both** as structured frontmatter arrays **and** as inline wikilinks in body prose. The frontmatter is canonical for queries; the body wikilinks are for reading flow.
1384
-
1385
- | Type | Frontmatter field | Inverse field | Semantics |
1386
- | --- | --- | --- | --- |
1387
- | **Blocks** | `blocks: [[...]]` | `blocked_by: [[...]]` | Hard. A blocked task is not ready until all `blocked_by` resolve. |
1388
- | **Related** | `related: [[...]]` | `related:` (symmetric) | Soft. Navigational only; no readiness implication. |
1389
- | **Parent-child** | `children: [[...]]` | `parent: [[...]]` | Hierarchical. Children roll up to parent for "completion of parent" calculations. |
1390
- | **Discovered-from** | `discovered_from: [[...]]` | `discovered:` (list) | Provenance. "This task was born while working on X." |
1391
- | **Iterates-on** | `iterates_on: [[...]]` | `iteratedBy:` (computed) | Re-attempt. "This initiative re-attempts an outcome the target shipped but did not fully deliver." No readiness implication; neither doc is demoted. See §Iteration lineage below. |
1392
-
1393
- Rules:
1394
-
1395
- - The indexer maintains both sides of every edge; the Builder only needs to write one side, and the console fills the other on save. (Implementation detail; specified in HS-DOC-architecture-foundations.)
1396
- - The indexer rejects edges that form a cycle in the `blocks` graph or the `parent`/`children` graph.
1397
- - `related` edges are symmetric; the indexer enforces symmetry on save.
1398
- - A task without a `parent:` and not referenced as a child by anyone is treated as a top-level task.
1399
- - `discovered_from` is single-valued; if a task has multiple origins, model the additional ones as `related:`.
1400
-
1401
- ## Provenance edges (capture → research → milestone)
1402
-
1403
- The promotion lifecycle — capture observation → research synthesis → milestone commitment — has cross-type provenance edges with strict cardinalities. Ratified by HS-OFR5-M2J3-EPIC-introduce-milestones-as-highest-entity §Architecture; enforced by `scripts/validate-vault-ids.mjs` (HS-HQRM-YJX6-TASK-research-enhances-exactly-one-milestone-validator-and-bidirectional-linkage).
1404
-
1405
- | Edge | Cardinality | Forward field | Reverse field |
1406
- | --- | --- | --- | --- |
1407
- | **Capture → Research** | M:N | `capture.informs_research: [[[HS-RES-…]]]` | `research.sources_captures: [[[HS-CAP-…]]]` |
1408
- | **Research → Milestone** | **M:1** (strict) | `research.enhances: [[HS-MIL-…]]` (single, not array) | `milestone.informed_by: [[[HS-RES-…]]]` |
1409
- | **Research → Epic (spawned)** | 1:N | `research.spawned_epics: [[[HS-EPIC-…]]]` | `epic.derived_from: [[HS-RES-…]]` (single) |
1410
- | **Research → Epic (augmented)** | M:N | `research.enhances_epics: [[[HS-EPIC-…]]]` | (one-sided — no reverse field on epic in v1) |
1411
- | **Research → Diagnose (bug found)** | 1:N | `research.produces_diagnose: [[[HS-DIAG-…]]]` | `diagnose.derived_from: [[HS-RES-…]]` (single) |
1412
-
1413
- Rules:
1414
-
1415
- - **Research is M:1 to milestone — strict.** A single research doc that spans multiple milestones must be split before promotion. The split-action protocol is owned by HS-TQQS-GTH9-TASK-hs-split-research-mcp-tool; the original research stays as the audit archive with `status: closed` and `split_into: [[HS-RES-A]], [[HS-RES-B]]`, while N child research docs each carry `split_from: [[HS-RES-original]]` and their own `enhances:` target.
1416
- - **Open research may omit `enhances:`.** The validator only requires it on `status: closed` research (the promotion-time gate); during exploration the target milestone can still be ambiguous.
1417
- - **`enhances:` is single-valued.** Not an array — the validator rejects array form and points the author at the split flow.
1418
- - **Bidirectional consistency is enforced.** If research X has `enhances: Y`, milestone Y MUST list X in `informed_by:`. If capture C has `informs_research: [X, Y]`, both X.sources_captures and Y.sources_captures MUST include C. The validator surfaces one-sided drift as a hard error on each side.
1419
- - **Captures M:N research is intentional.** A single capture may inform multiple research docs over time (e.g. a "voice capture works" observation that lands in both companion research and console-voice research). The first promotion writes `capture.promoted_to:` (binary pointer); subsequent re-uses extend `capture.informs_research:` without touching `promoted_to:`.
1420
- - **`spawned_epics` vs `enhances_epics`.** `spawned_epics` is the canonical 1:N edge — epics this research *created*, mirrored as `epic.derived_from:` on each child. `enhances_epics` is the rarer case where research *augmented* an already-existing epic; it's a one-sided list with no reverse field in v1.
1421
- - **`produces_diagnose` mirrors `spawned_epics`.** A research investigation may surface a bug, which is promoted into a first-class `HS-DIAG-*` (not new machinery — the existing diagnose entity per resolution-diagnose-workflow). `research.produces_diagnose:` is the canonical 1:N edge, mirrored as `diagnose.derived_from:` on each child, written by `heyspex_promote_research_to_diagnose`. This is the research-side sibling of the capture-side exit `heyspex_promote_capture_to_diagnose` (which writes `diagnose.promoted_from:` ⇔ `capture.promoted_to:`/`informs_diagnose:` instead).
1422
-
1423
- Promotion-action contract (`/hs-promote-research`, HS-2ERO-QQHP-TASK-hs-promote-research-mcp-tool): writes all four sides (research.enhances, research.spawned_epics, milestone.informed_by, epic.derived_from) atomically. The validator is the safety net for hand-edited drift, not the primary write path.
1424
-
1425
- ## Iteration lineage (round-to-round re-attempts)
1426
-
1427
- The **third doc-to-doc relation tier**, alongside supersession (terminal, whole-doc) and amends (partial, non-terminal) — ratified via HS-3TZX-T9T6-RES-iteration-loop-lineage-and-capture Part A, built by HS-O97P-T9T6-EPIC-iteration-loop-lineage-relation-tier. It expresses what neither existing tier can: *work shipped and closed, the outcome fell short of the goal, and a NEW family re-attempts the SAME goal.* Supersession would falsify round 1 (it wasn't wrong — it shipped, its review is accurate history); amends would misdescribe it (round 2 contradicts nothing).
1428
-
1429
- ### Fields
1430
-
1431
- ```yaml
1432
- iterates_on: # optional, YAML list of wikilinks — lives ONLY on the NEWER doc (one-edit-on-source,
1433
- # same principle as amends). Semantics: "this initiative re-attempts an outcome the
1434
- # target initiative shipped but did not fully deliver." Typically carried by the
1435
- # round-2 research doc pointing at the round-1 research doc — the research chain is
1436
- # the spine (research is evergreen, so the root research id is the durable handle
1437
- # spanning every round; no new entity type).
1438
- # iteratedBy — COMPUTED reverse map (indexer/validator output, like amendedBy) — never written
1439
- # by hand into frontmatter.
1440
- outcome: # optional, on TRACKABLE docs only, set by the HUMAN REVIEWER at close — never
1441
- # inferred, never agent-set. Enum: met | partially-met | not-met. Lives ONLY on
1442
- # the closed doc (not echoed onto the round-2 doc; discovery goes through the
1443
- # iteratedBy reverse map).
1444
- outcome_note: # optional, one-line reviewer prose qualifying the verdict.
1445
- ```
1446
-
1447
- ### Invariants
1448
-
1449
- - **Nothing is demoted.** Both docs stay `canonical: true`; round 1 keeps `status: closed`. `iterates_on:` is a lineage edge, not a supersession — demoting round 1 to `canonical: false` would falsify the record and hide shipped scope from round 2's breakdown.
1450
- - **`outcome:` is the reviewer's judgment alone.** `status: closed` without an `outcome:` says only "shipped"; it must never be read as "goal met." The verdict is written at reviewer close (the one mutable-at-close moment), per [[session-close-protocol]].
1451
- - **Distinct family keys are correct.** Round 2 is its own initiative with its own children and review, so it takes a new family key; `iterates_on:` is the cross-family edge that replaces the missing shared grep handle.
1452
-
1453
- ### Required `## Related / lineage` section on promoted docs
1454
-
1455
- Every promoted doc's outline ends with a resolved-wikilink table (immediately before `## Change log`), authored at promote-time and gated by `heyspex_validate_linkage` (every row must resolve under `specs/`):
1456
-
1457
- ```markdown
1458
- ## Related / lineage
1459
-
1460
- | Doc | Role |
1461
- |---|---|
1462
- | [[round-1-research]] | Iterates on |
1463
- | [[HS-REV-...]] | Prior attempt's review |
1464
- | [[HS-CAP-...]] | Feedback capture |
1465
- ```
1466
-
1467
- Role vocabulary: **Iterates on · Prior attempt's review · Feedback capture · Amends · Superseded by · Pillar · Spawned epics** (extend with prose roles when none fits — e.g. "Source capture", "Enhances").
1468
-
1469
- ## Evidence linking
1470
-
1471
- > [!important] "Summaries with receipts"
1472
- > Ratified by adr-0008-branch-brief-and-evidence-schema for HS-7KOO-0022-EPIC-continuity-layer-gaps. Every claim a memory or brief makes should be able to point at a verifiable source. Without evidence, a summary is unfalsifiable; with it, a reader can navigate from claim to proof.
1473
-
1474
- `evidence:` is an optional array on `branch-brief`, `memory-lesson`, `memory-decision`, `memory-reference`, `memory-ui-pattern`, and `task`. Each entry:
1475
-
1476
- ```yaml
1477
- evidence:
1478
- - type: commit # required — one of the evidence types below
1479
- ref: "abc123" # required — the reference, format per type
1480
- line: 42 # optional — line number (file/diff only)
1481
- description: "..." # optional — what this evidence shows
1482
- ```
1483
-
1484
- Evidence types:
1485
-
1486
- | Type | `ref` format | Example |
1487
- | --- | --- | --- |
1488
- | `commit` | short or full SHA | `abc123`, `abc123def…` |
1489
- | `file` | repo-relative path, optional `:line` | `src/foo.ts`, `src/foo.ts:42` |
1490
- | `pr` | PR number | `#123` |
1491
- | `diff` | `commit:path` | `abc123:src/pages/dashboard.tsx` |
1492
- | `issue` | issue number | `#42` |
1493
- | `note` | freeform text | `Builder confirmed via Slack 2026-05-15` |
1494
- | `test` | test/gate identifier | `vitest:src/lib/continuity/plan-vs-actual.test.ts`, `tsc:noEmit` |
1495
-
1496
- > [!note] `test` added 2026-08-11 (receipt-on-ship, HS-XGG4-IU2S-EPIC-receipt-on-ship-plan-vs-actual-proof-at). Additive extension per adr-0008-branch-brief-and-evidence-schema §Consequences ("a future change to evidence types … is a normal entity-model edit"); lets Plan Receipts cite test/gate results as typed evidence.
1497
-
1498
- ### Inline shorthand
1499
-
1500
- In body prose, evidence may be cited inline as `[evidence:<type>:<ref>]`:
1501
-
1502
- ```markdown
1503
- We chose Payment Elements over hosted checkout [evidence:commit:abc123]
1504
- because it gives better customization [evidence:pr:#123].
1505
- ```
1506
-
1507
- The indexer harvests inline shorthand and the frontmatter `evidence:` array into the same evidence index; **frontmatter is canonical** for "what grounds this spec?" queries (same rule as `tags:` and dependency edges). Console rendering of evidence references (commit → diff view, file → file viewer at line, pr/issue → git host) is an implementation concern specified downstream, not part of this schema contract.
1508
-
1509
- ## Delivery snapshot
1510
-
1511
- > [!note] User-persisted, not auto-maintained
1512
- > See HS-WB81-RBJ8-TASK-spec-delivery-frontmatter-snapshot. The live source of truth for "which commits touched this spec" is always `git log -- <spec_path>`; the `delivery:` block is an *optional persisted snapshot* the Builder takes from the console's On Deck row so the vault carries its own delivery trail.
1513
-
1514
- `delivery:` is an **optional, cross-type** key. It exists only after the Builder has clicked "snapshot to spec" in the console's delivery dropdown. Shape:
1515
-
1516
- ```yaml
1517
- delivery:
1518
- snapshot_at: 2026-05-21T07:00:00-05:00 # ISO timestamp of when the snapshot was taken
1519
- commits:
1520
- - sha: 8648ad5e3f... # full SHA
1521
- short_sha: 8648ad5 # 7-char SHA
1522
- branch: hs-mvp0520 # primary branch (local before remote)
1523
- pushed: true # boolean — reachable from origin/*
1524
- at: 2026-05-21T05:14:32-05:00 # commit author time
1525
- author: pwher
1526
- message: "feat(console): per-spec delivery dropdown on On Deck child rows"
1527
- ```
1528
-
1529
- Rules:
1530
-
1531
- - One primary branch per row, not the full branch list (live view stays authoritative for fan-out).
1532
- - `message` is the first commit-message line only; full commit bodies live in git, not the vault.
1533
- - `delivery:` may be present on **any** spec type; treat it as a presentational artifact, not a dependency edge — the indexer does not crawl it.
1534
- - A stale snapshot (live count ≠ snapshot count) is rendered as such in the console; the snapshot is never auto-refreshed.
1535
- - Persisted by `PUT /api/specs/[id]/delivery`; no git hook or agent workflow rewrites it.
1536
-
1537
- ## Wikilink resolution rules
1538
-
1539
- HeySpex uses Obsidian-compatible wikilinks: `[[target]]` or `[[target|display text]]`.
1540
-
1541
- Resolution algorithm:
1542
-
1543
- 1. **Strip extension.** `[[foo]]`, `[[foo.md]]`, and `[[foo|Foo]]` all target the file basename `foo`.
1544
- 2. **Case-insensitive match.** The resolver compares lowercased basenames. `[[Foo]]` and `[[foo]]` resolve to the same file.
1545
- 3. **No path qualifier needed.** Folders are not part of the resolution. `[[entity-model]]` finds `specs/entity-model.md` regardless of where the linking spec lives. (Consistent with Obsidian's "default" link mode.)
1546
- 4. **Aliases.** If a spec's frontmatter has `aliases: [foo, bar]`, both `[[foo]]` and `[[bar]]` also resolve to that spec.
1547
- 5. **Forward references are valid.** A wikilink to a non-existent spec is not an error; it is a signal that the linked spec should be written. The indexer records forward references in a `forward_refs` view; the console shows them inline so the Builder can see what's pending.
1548
- 6. **Duplicate basenames are forbidden.** Two specs may not share a basename. The indexer rejects writes that would create a duplicate.
1549
- 7. **Filename is canonical; do not author id-form links.** The resolver also matches a link target against a spec's frontmatter `id:` (it tries id → basename → alias, in that order). This is a **tolerant fallback** so that hand-typed or legacy `[[HS-DEC-…]]`/`[[HS-TASK-…]]` links still resolve — it is **not** the form to write. Author every link by **filename basename** so the vault stays Obsidian-portable (Obsidian does not resolve by the `id:` field). For memory specs (decisions, lessons, references, ui-patterns) the file is slug-named while its `id` carries a type prefix, so `[[HS-DEC-foo]]` is the id form and `[[foo]]` is the canonical filename form — prefer the latter. The vault was swept to filename form on 2026-05-28 (`scripts/sweep-hs-dec-links-to-basename.mjs`).
1550
-
1551
- Display text:
1552
-
1553
- - `[[entity-model|the entity model]]` renders "the entity model" as a link to `entity-model.md`.
1554
- - This is the same syntax Obsidian uses; HeySpex inherits it unchanged.
1555
-
1556
- ## Tags
1557
-
1558
- Tags use the `tags:` array in frontmatter. They are flat strings, no nesting (no `#foo/bar` semantics in v1 beyond convention).
1559
-
1560
- Body-inline `#tag` syntax is allowed for compatibility with Obsidian users; the indexer harvests both frontmatter tags and body inline tags into the same index, but frontmatter is canonical for "is this spec tagged X?" queries.
1561
-
1562
- ## Reserved frontmatter keys
1563
-
1564
- The following keys are reserved by HeySpex and must not be repurposed:
1565
-
1566
- - `id`, `name`, `type`, `status`, `priority`, `owner`, `created`, `updated`, `tags`, `related`, `aliases`
1567
- - `blocks`, `blocked_by`, `parent`, `children`, `discovered_from`, `discovered`
1568
- - `assignee`, `started_at`, `closed_at`, `close_reason`, `defer_until`
1569
- - `evidence` (cross-type; see [Evidence linking](#evidence-linking))
1570
- - `delivery` (cross-type; user-persisted git-log snapshot — see [Delivery snapshot](#delivery-snapshot))
1571
- - `legacy_bd_id`, `legacy_dolt_commit`
1572
- - Type-specific fields named above (including `branch-brief`'s `branch`, `goal`, `implements`, `plan`, `actual`, `risks`, `open_questions`, `next_steps`, `decisions`; `milestone`'s `epics`, `target_date`, `informed_by`; the `epic`'s `milestone` parent pointer and optional `derived_from`; `task`'s `split_of`, `split_index`, `handoff_id` (split lineage — envelope lives in body) and optional `affects_paths`; `research`'s `enhances`, `sources_captures`, `spawned_epics`, `enhances_epics`, `produces_diagnose`, `split_into`, `split_from`; `diagnose`'s optional `promoted_from` and `derived_from`; `capture`'s `informs_research`; the `agent` type's `role`, `model`, `allowed_tools`, `disallowed_tools`, `procedures`, `handoffs`, `guardrails`, `lane`, `color`, `owned_paths`, and `source_attribution` import-provenance object). Type-specific fields are additive and do **not** require an ADR (only a new *universal* reserved key does); `source_attribution` was added under HS-GMLJ-K3KR-TASK-lock-the-community-import-design-and-add; `owned_paths` / `affects_paths` under HS-ZMAQ-M15C-TASK-declare-owned-path-globs-on-agent-identi.
1573
- - `reviews` (review type — back-compat alias for `parent:` pointing at the spec under review)
1574
-
1575
- Adding a new universal reserved key requires an ADR.
1576
-
1577
- ## Validation
1578
-
1579
- The indexer (see HS-DOC-architecture-foundations §Indexer) runs frontmatter validation on every write:
1580
-
1581
- - All required fields present and of the expected type.
1582
- - `id` matches the entity type's pattern.
1583
- - `id` is unique across the vault.
1584
- - `status` is in the enum.
1585
- - All wikilinks in frontmatter resolve, or are tracked as forward references.
1586
- - No cycles in `blocks` or `parent`.
1587
- - No duplicate basenames.
1588
-
1589
- Validation failures are surfaced in the console and via `hs lint`. They do not block git writes (the vault is plain text and the Builder is trusted), but the indexer marks the affected spec as invalid until corrected.
1590
-
1591
- ## Examples
1592
-
1593
- ### Minimal task
1594
-
1595
- ```markdown
1596
- ---
1597
- id: HS-O3E0
1598
- name: Fix console keyboard navigation
1599
- type: task
1600
- status: open
1601
- priority: 2
1602
- owner: pwher
1603
- created: 2026-05-14
1604
- updated: 2026-05-14
1605
- kind: bug
1606
- blocked_by:
1607
- - "[[HS-IS1A-TASK-SpexConsole-routing-rewrite]]"
1608
- related:
1609
- - "[[HS-582Q-TASK-arrow-key-conflicts]]"
1610
- tags: [console, ux, keyboard]
1611
- ---
1612
-
1613
- # Fix console keyboard navigation
1614
-
1615
- Arrow keys do not move focus between issues in the list view; only Tab works. Compare to Linear, where ↑/↓ navigates the list.
1616
-
1617
- ## Acceptance
1618
-
1619
- - Up/Down arrow keys move focus between rows in `IssuesSurface`.
1620
- - Enter on a focused row opens the detail pane.
1621
- - No regression for input fields (arrow keys still move the caret inside text inputs).
1622
-
1623
- ## Change log
1624
-
1625
- | Date | Author | Change |
1626
- | --- | --- | --- |
1627
- | 2026-05-14 | pwher | Created from drafts/triage. |
1628
- ```
1629
-
1630
- ### Memory lesson
1631
-
1632
- ```markdown
1633
- ---
1634
- id: HS-LES-bd-process-leak
1635
- name: bd CLI process leak
1636
- type: memory-lesson
1637
- status: open
1638
- priority: 1
1639
- owner: pwher
1640
- created: 2026-05-14
1641
- updated: 2026-05-14
1642
- topic: bd-process-leak
1643
- observed: 2026-05-14
1644
- related:
1645
- - "[[HS-LES-dolt-embedded-windows]]"
1646
- - "[[adr-0006-replace-beads]]"
1647
- tags: [bd, dolt, windows, process-leak]
1648
- ---
1649
-
1650
- # bd CLI process leak
1651
-
1652
- ## Problem
1653
-
1654
- A single `bd create` issued the prior session sat alive consuming 107 GB working set, plus seven orphaned bd procs at ~67 MB each. RAM hit 80% of 128 GB.
1655
-
1656
- ## Fix
1657
-
1658
- At session start, run `Get-Process bd | Sort WorkingSet -Desc` on Windows and kill any orphans before issuing new bd commands. Be wary of invoking `bd create` with `run_in_background` — bd does not always exit cleanly when the parent harness moves on.
1659
-
1660
- ## Change log
1661
-
1662
- | Date | Author | Change |
1663
- | --- | --- | --- |
1664
- | 2026-05-14 | pwher | Migrated from bd memory `lessons/bd-process-leak`. |
1665
- ```
1666
-
1667
- ## Open questions
1668
-
1669
- These are intentionally left for the Builder's confirmation in Phase 5 (migration), to avoid pre-committing the migration translation rules:
1670
-
1671
- 1. **Resolved (2026-05-18):** Memory uses subfolders — `memory/lessons`, `memory/decisions`, `memory/references`, plus `protocols/` as a Memory subsection on disk (memory-protocols-subsection-of-memory). **2026-05-20:** added `memory/ui-patterns/` (`memory-ui-pattern`). **2026-05-21:** added `memory/ux-principles/` (`memory-ux-principle`). Console Memory surface: All / Lessons / Decisions / References / **UX Principles** / UI Patterns / Protocols.
1672
- 2. The id format for migrated tasks — `HS-NNNN-<slug>` with `legacy_bd_id` preserved. Confirm the slug source (proposal: from `title:` lowercased, truncated to ~40 chars, dash-separated).
1673
- 3. Whether to expose `body inline tags` (`#tag` inside prose) as queryable, or treat them as decorative-only. Proposal: queryable but frontmatter `tags:` wins on ties.
1674
- 4. Whether the `daily/` notes folder should exist at all in v1 (it's not in L1's use cases). Proposal: include it for Builder convenience; cost is one folder.
1675
-
1676
- ## Related specs
1677
-
1678
- - heyspex-north-star — the L1 this entity model serves.
1679
- - HS-DOC-architecture-foundations — the tech that implements the contract this document specifies.
1680
- - uc-capture-intent — first-class typed capture and the phone-to-draft path; promotes drafts per §`draft` below.
1681
- - uc-ingest-anywhere — Manual vs Agent ingest; drives optional `draft` LLM provenance fields and body footer conventions.
1682
- - uc-voice-ingest-hey-spex — Hey Spex wake companion + shortcuts; optional `capture_channel: voice` fields above.
1683
- - migration-from-bd — the L3 spec that uses the IDs/filenames defined here.
1684
- - adr-0001-vault-format — the ADR that locks in markdown + YAML + wikilinks as the file format.
1685
- - adr-0008-branch-brief-and-evidence-schema — the ADR that ratifies the `branch-brief` type and `evidence:` key.
1686
- - adr-0012-roadmap-storage — `roadmap` type, `roadmap_manifest`, horizon backlog, ingest provenance fields.
1687
- - plan-ingest-review-roadmap-workflow — default ingest → review → roadmap admission protocol.
1688
- - ai-deliberation-gate — Gate 0 multi-model debate on drafts.
1689
- - ingest-review-deliberation-roadmap-lifecycle — consolidated ingest/roadmap/council decision.
1690
- - HS-ROADMAP-product-master — product phases + horizon manifest.
1691
- - HS-7KOO-0022-EPIC-continuity-layer-gaps — the Continuity Layer epic these additions serve.
1692
- - HS-BVZT-XBPJ-RES-bd-feature-inventory — the research spec that justifies each row of the schema.
1693
- - memory-protocols-subsection-of-memory — Protocols are a Memory subsection in navigation; epics author protocols.
1694
-
1695
- ## Change log
1696
-
1697
- | Date | Author | Change |
1698
- | --- | --- | --- |
1699
- | 2026-05-14 | pwher (with claude opus 4.7) | Initial draft. Derived the entity-type set from L1 use cases plus the bd inventory. Locked in four dependency types (matching bd) and Obsidian-compatible wikilink resolution. Folder layout uses subfolders per type for human navigation; resolution is folder-agnostic. |
1700
- | 2026-05-16 | pwher | §`draft`: companion/voice + optional spatial cursor/click hints (uc-voice-ingest-hey-spex §Spatial pointers). |
1701
- | 2026-05-16 | pwher (with claude opus 4.7) | Added `branch-brief` entity type (folder `branches/`, ID `HS-BB-<slug>`, plan/actual + drift enum) and the cross-type `evidence:` array with inline shorthand. Per adr-0008-branch-brief-and-evidence-schema; closes HS-7E41/0052/0053 of HS-7KOO-0022-EPIC-continuity-layer-gaps. |
1702
- | 2026-05-17 | pwher | Added `roadmap` type + `roadmap_manifest`; `future-considerations/` layout; draft `source_intent_hash` / `review_stage`; task `roadmap_admission` / `ingest_draft`. Per adr-0012-roadmap-storage. |
1703
- | 2026-05-17 | pwher | Draft `deliberation_record` (ai-deliberation-gate); task `close_reason` enum + `superseded_by`; archive = filter not folder. |
1704
- | 2026-05-17 | pwher | Draft `suggestions:` + `review_record:` per HS-EU98-GCGE-TASK-staged-draft-suggestion-schema; omit-when-empty rule. |
1705
- | 2026-05-17 | pwher | `submission_kind`, `companion_dialogue[]`; companion `capture_channel` per companion-external-media-vs-refined-intent-council. |
1706
- | 2026-05-17 | pwher | `capture_bundle_id`, `source_set[]` for companion multi-article bundle submit (companion-capture-inbox-and-bundles, HS-D1YU-OVFO-TASK-bundle-submit-composite-draft-source-set). |
1707
- | 2026-05-17 | pwher | `triage_record` scout schema for Review inbox workshop (review-inbox-workshop-scout-overview). |
1708
- | 2026-05-18 | pwher (with claude opus 4.5) | Added `in_review` status to universal enum — human gate between agent work and closure (HS-YYEO-FLX9-TASK-review-column-human-gate-before-done). |
1709
- | 2026-05-20 | pwher | `canvases/diagrams/` + `canvases/as-built/` for spec diagram dual-surface (spec-diagram-design-vs-as-built-canvas). |
1710
- | 2026-05-20 | pwher | Added `specs/canvases/` for JSON Canvas design files per adr-0013-canvas-vault-format-and-obsidian-interop. |
1711
- | 2026-05-20 | pwher | Task/epic ID formats: random alphanumeric suffix for new specs (vault-id-assignment-protocol); legacy `HS-NNNN` migration IDs retained. |
1712
- | 2026-05-21 | pwher | Added `planning-flow` type (`specs/flows/`); research `bound_epic` / `planning_flow`; epic `research` / `planning_flow` requirements (planning-flow-research-epic-lifecycle). |
1713
- | 2026-05-22 | pwher + Claude Opus 4.7 (1M) | `idea` schema: `research_stage` removed (single binary `promoted_to`); body shape v2 — `# Core Idea` / `## Notes` / `## Concept` (≤1200 chars) / `<details>Details</details>`. See HS-KQ5O-IDFV-EPIC-unified-idea-workflow-v2 / HS-QOAC-IDFL-RES-unified-idea-workflow-v2. |
1714
- | 2026-05-22 | pwher + Claude Opus 4.7 (1M) | **Capture rename:** `type: idea` → `type: capture`; new required `kind:` field (idea / bug-fix / clip / reference / question); folder `specs/captures/` → `specs/captures/`; ID prefix `HS-IDEA-*` → `HS-CAP-*` (legacy preserved in `legacy_idea_id:`); research `source_idea:` → `source_capture:`. Per HS-B2B8-DLQI-EPIC-capture-rename-migration-idea-to-capture; source rationale HS-ZKFN-1FXG-CAP-capture-as-universal-companion-verb-and-parent-term. |
1715
- | 2026-05-24 | Claude Opus 4.7 (1M) | Capture schema: added `capture_status: raw \| expanded` (default `raw`). Flipped to `expanded` by MCP `executeUpdateCapture` on core_idea/concept/agent_expansion writes; force-corrected by console save-time linter when `# Core Idea` body has content. Backfilled across 21 existing captures by `scripts/backfill-capture-status.mjs`. Per HS-20PH-P2WA-TASK-b3-new-frontmatter-field-idea-status (B3 of HS-BDDT-Q1B7-EPIC-idea-capture-modal-v3-single-textbox-cle). |
1716
- | 2026-05-25 | pwher (with Claude Opus 4.7) | **Business layer added.** New entity types `business-positioning` / `business-pricing` / `business-marketing` / `business-model` filed under `specs/business/{positioning,pricing,marketing,model}/`. IDs `HS-BIZ-POS-* / HS-BIZ-PRI-* / HS-BIZ-MKT-* / HS-BIZ-MOD-*`. Browsed in the console on a dedicated **Business** surface that mirrors Memory's category-rail layout. `positioning-2nd-brain-harness-for-specs` migrated from `memory/decisions/` (`type: memory-decision`) to `business/positioning/` (`type: business-positioning`); basename unchanged so wikilinks resolve. |
1717
- | 2026-05-29 | pwher (with Claude Opus 4.8 [1m]) | **`milestone` entity type formalized.** Added folder `specs/milestones/`, Entity-types-table row, ID format `HS-MIL-<suffix>` (4+ uppercase; legacy 4-char mnemonics like `HS-RM22` valid), filename pattern `HS-MIL-<suffix>-<slug>.md`, and a full `#### milestone` schema subsection (`epics`, `target_date`, status enum, explicit "no scalar priority"). Disambiguated the legacy `kind: milestone` task discriminator from the new `type: milestone` entity; added the `milestone:` parent pointer to the `#### epic` schema; registered the new fields under Reserved frontmatter keys. Describes the 33 `HS-MIL-*` files already on disk (seed HS-RM22-IART-MIL-console-ia-restructure). Ratified by introduction lineage HS-VK7C-MILE-EPIC-milestone-entity-type-and-surfacing → HS-OFR5-M2J3-EPIC-introduce-milestones-as-highest-entity (in lieu of a standalone ADR); canonical hierarchy in [[spec-hierarchy]]. Per HS-9AUC-HRMZ-TASK-define-milestone-entity-type-in-entity-m. |
1718
- | 2026-05-26 | pwher (with Claude Opus 4.7) | **`review` entity type formalized.** Added folder `specs/reviews/`, ID format `HS-REV-<suffix>` (suffix matches parent task/epic id 1:1), filename pattern `HS-REV-<suffix>-<slug>.md`, and full `#### review` schema subsection documenting the four required body sections (Acceptance verification / Reviewer notes / Memory candidates / Decision). Documents what CLAUDE.md §Session Completion + ~100 existing review files already implement; provides the authoritative L2 schema for console, indexer, MCP, and future CLI consumers. Per T1 of HS-3K55-0033-EPIC-review-sibling-testing-workflow (grounded against HS-19F9-TEST-MIL-api-testing-dual-path + HS-UKD6-CLR4-RES-close-out-review-screen-jtbd-architecture). |
1719
- | 2026-05-29 | pwher (with Claude Opus 4.7 [1m]) | **Provenance edges formalized (capture → research → milestone).** Added new §Provenance edges section between §Dependencies and §Evidence linking with the cross-type cardinality table (Capture→Research M:N, Research→Milestone M:1 strict, Research→Epic spawned 1:N, Research→Epic augmented M:N). New frontmatter fields: `research.enhances` (M:1 required on closed), `research.sources_captures` (M:N), `research.spawned_epics` (1:N), `research.enhances_epics` (M:N rare), `research.split_into` / `research.split_from` (split-flow audit); `milestone.informed_by` (reverse of enhances); `capture.informs_research` (M:N, distinct from binary `promoted_to`); optional `epic.derived_from` (reverse of spawned_epics). Reserved-keys list updated. Per HS-HQRM-YJX6-TASK-research-enhances-exactly-one-milestone-validator-and-bidirectional-linkage of HS-OFR5-M2J3-EPIC-introduce-milestones-as-highest-entity. |
1720
- | 2026-05-31 | pwher (with claude opus 4.7) | **`pillar` entity type added** (folder `specs/pillars/`, ID format `HS-PILR-<slug>` for canonical or `HS-PILR-<suffix>-<slug>` for customer-scoped, filename matches ID, `status: perpetual` non-terminal). New required fields on every epic: `pillar:` (single primary) and optional `secondary_pillars:` array. Type ratified by introduction lineage HS-EBIL-0011-EPIC-goal-hierarchy-l2-strategic-pillars-l3 following the milestone precedent (no standalone ADR for the entity itself); privacy-boundary for the Pillar Library learning loop is adr-0016-pillar-library-privacy-boundary. **`architecture-pillars.md` renamed to `architecture-foundations.md`** (ID `HS-L2-PILLARS` → `HS-L2-FOUNDATIONS`) to disambiguate from the new entity; 172 wikilinks updated in a single bulk pass. Three "pillar" usages now coexist: the `type: pillar` entity, the unrelated "Pillars 0–5" section names in HS-DOC-architecture-foundations, and the renamed "FIVE COMPONENTS" section in HS-OR8V-0010-EPIC-north-star-vision-first-class-entity (previously "FIVE PILLARS"). L3 Themes (per-epic teleological anchor) retired in the same resolution — collapsed into perpetual Pillars. See HS-AWDI-PILR-RES-pm-perpetual-container-terminology §"Resolution against existing HS-J6K8" for the full rationale. |
1721
- | 2026-06-03 | claude opus 4.7 [1m] · session `local` (with pwher) | **`canonical:` universal field added; two `superseded` enum values deprecated.** Per evergreen-doc-architecture-srp-and-seo-pillars + canonicalization-arbiter-is-link-graph-not-policy + closed-trackable-specs-are-historical-record-not-living-docs: `canonical: true \| false` is now a universal frontmatter field on every spec type (pillars, L1/L2/L3 docs, HS-DOC-*, ADRs, protocols, memory specs, epics, tasks, milestones, reviews). `true` (or absent) = current/authoritative; `false` = historical record. Trigger event for the flip: plan-side specs at the in_review → closed transition (per [[session-close-protocol]] step 4); evergreen docs at supersession (per supersession-protocol). `status: parked` / `status: deferred` keeps `canonical: true`. **Two enum values deprecated forward-only** per superseded-enum-values-deprecated-use-canonical-false-and-superseded-by: `status: superseded` removed from the ADR-status enum; `superseded` removed from the close_reason enum. Both were redundant with `canonical: false` + `superseded_by:`. Forward-only retirement — existing 11 specs (8 ADRs + 3 plan-side) keep their legacy values as historical record per immutability. New writes use `canonical: false` + `superseded_by:` instead. Added the `canonical:` field to the ADR and task schemas; expanded the `superseded_by:` description to clarify it's the pointer (vs. `canonical:` which is the state). |
1722
- | 2026-06-03 | claude opus 4.7 [1m] · session `local` (with pwher) | **`## Lifecycle classes` section added; `research` schema de-coupled from `status: closed`.** New top-level section between §Entity types and §Identity, IDs, and filenames classifying all 24 types into four lifecycle classes — **Evergreen** (`l1`/`l2`/`l3`/`adr`/`pillar`/`protocol`/`memory-*`/`research`/`business-*`), **Trackable plan-side** (`task`/`epic`/`milestone`/`review`/`branch-brief`/`planning-flow`), **Single-shot ingest** (`capture`/`draft`/`daily`), **Per-instance** (`roadmap`). Closes the discoverability gap that produced the research-is-evergreen incident: a reader asking *"can I flip this file's status to `in_review`?"* now has one canonical place to look. Cross-references research-is-evergreen, closed-trackable-specs-are-historical-record-not-living-docs, canonicalization-arbiter-is-link-graph-not-policy, [[session-close-protocol]]. **Research schema (`#### research`) amended**: the `enhances:` field gate changed from "REQUIRED on `status: closed`" to "REQUIRED when set" — bidirectional consistency is enforced whenever the field is present, independent of status. Added a one-line note above the schema declaring research evergreen with a pointer to research-is-evergreen. The matching validator update in `scripts/validate-vault-ids.mjs` is on the deferred list — schema-vs-validator drift is acknowledged in the schema comment until that catches up. |
1723
- | 2026-06-13 | claude opus 4.8 [1m] · session `local` (with pwher) | **`diagnose` entity type added** (folder `specs/diagnoses/`, ID `HS-DIAG-<suffix>`, filename `HS-DIAG-<suffix>-<slug>.md`). A user-reported bug fix as first-class executable work — standalone by default (exempt from the epic→milestone parent rule), optional `parent:` (epic or milestone), required `severity:` (error/warning/info) that maps onto the scheduler's existing priority signals, optional `blocks:` gating edges. Surfaces on the Resolution surface as a derived `kind: "bug"` row that links back to the diagnose. Added entity-types-table row, ID-format row, filename row, `#### diagnose` schema subsection, and Trackable-plan-side lifecycle classification. Ratified by introduction lineage HS-OXBS-BFIX-MIL-bug-fix-diagnose-lane (milestone-precedent, no standalone ADR). Wiring: `vault.ts` (TYPE_TO_FOLDER + KIND_LINKAGE + body skeleton + generateId), `vault-id.ts`, `vault-rules.mjs` (KIND_RULES/KIND_ORDER), `validator.ts`, `validate-vault-ids.mjs`. Per HS-AO2F-1KZK-RES-bug-fix-resolution-task-entity-model-and. |
1724
- | 2026-06-23 | Cursor · session `local` | **Delegation handoff envelope + task split lineage.** Task frontmatter: `split_of`, `split_index`, `handoff_id` (option B from HS-DTL1-CX5K-CAP-task-split-handoff-when-context-runs-low). Handoff envelope SSOT is body (`## Handoff envelope` + fenced yaml) per `schemas/handoff-envelope.schema.json`; validated by `handoff-envelope.mjs` + `validate-vault-ids.mjs`. Engine rules in `vault-rules.mjs` (`TASK_SPLIT_*`). Per HS-PD6K-NCH5-TASK-handoff-envelope-schema-and-split-child / HS-HXE2-YQ5A-EPIC-delegation-defer-work-from-one-agent-to. |
1725
- | 2026-06-23 | Cursor · session `local` | **`owned_paths` / `affects_paths` for parallel-agent stomp-detection.** New `#### agent` schema subsection documents `owned_paths:` (machine-checkable file globs on agent identities). Task schema adds optional `affects_paths:` (advisory module hints on trackable specs). Entity-types table + reserved-keys list updated. Per HS-ZMAQ-M15C-TASK-declare-owned-path-globs-on-agent-identi / HS-8PNX-Q1KC-EPIC-lanes-and-stomp-detection-for-parallel-a. |
1726
- | 2026-07-05 | Claude Sonnet 5 [1m] · session `local` (with pwher) | **`research.produces_diagnose` → `diagnose.derived_from` edge added (research → diagnose intake).** Bugs found during a research investigation now route into the Resolution lane without new diagnose machinery: `research.produces_diagnose:` (optional, list of wikilinks to `HS-DIAG-*`, mirrors `spawned_epics`) reverse-linked by `diagnose.derived_from:` (optional, single wikilink, mirrors `epic.derived_from`). Written by the new `heyspex_promote_research_to_diagnose` tool (`vendor/heyspex-mcp/promote-research-to-diagnose.mjs`), the research-side sibling of `heyspex_promote_capture_to_diagnose`. Also documented `diagnose.promoted_from:` (existing capture-side back-reference, previously undocumented in this schema subsection). Provenance cardinality table + reserved-keys list updated; validated by the `type: research` block of `schemas/heyspex-frontmatter.schema.json`. Per HS-W5VP-MSHY-TASK-research-diagnose-link-send-bugs-to-the of HS-0FTO-MSHY-EPIC-research-to-diagnose-link-send-bugs-foun. |
1727
- | 2026-07-05 | Claude Sonnet 5 [1m] · session `local` (with pwher) | **`code-audit` findings-table format + closure gate.** New `##### Code-audit findings table` subsection under `#### research` documents the required 4-column body table (`Finding \| Severity \| Action (diagnose\|task\|reference) \| Exit (open\|closed)`) and its three defined exits: bug→diagnose (reuses `heyspex_promote_research_to_diagnose`/`research.produces_diagnose`, no new tooling), cleanup→task (new `task.discovered_from:` reverse pointer to the source research doc), non-actionable→reference (the row itself is the exit). Enriched the existing `task.discovered_from:` doc comment to name this use. Added `discovered_from` to the `type: task` block of `schemas/heyspex-frontmatter.schema.json` (previously documented in entity-model.md only, unenforced). Validator rules in `vault-rules.mjs`: `CODE_AUDIT_FINDINGS_MALFORMED` (no/malformed table) and `CODE_AUDIT_FINDINGS_OPEN_ON_CLOSE` (the closure gate — a `closed`/`review_pending` code-audit doc with any `Exit: open` row) — both warn during a new `CODE_AUDIT_GRACE_END` (2026-08-15, mirrors `RESEARCH_TYPE_GRACE_END`) so pre-existing docs like `HS-ZHAB-RES-doc-authoring-contract-audit-required-st` (no `research_type:` set, predates this format) aren't hard-failed, then error. Per HS-7D0I-WL88-TASK-code-audit-research-type-with-a-defined of HS-BI9D-WL88-EPIC-code-audit-research-type-with-a-defined. |
1728
- | 2026-08-14 | Claude Code (Fable 5) · session `5785e74c` (with pwher) | **Iteration lineage — third relation tier added.** New §Iteration lineage section (between §Provenance edges and §Evidence linking): `iterates_on:` (list of wikilinks, newer-doc-only, one-edit-on-source like amends; computed reverse map `iteratedBy`) expresses "re-attempts an outcome the target shipped but did not fully deliver" — the case supersession (would falsify round 1) and amends (nothing is contradicted) both misdescribe. `outcome:` (`met` \| `partially-met` \| `not-met`) + `outcome_note:` — reviewer-set at close on trackable docs, never inferred, closed-doc-only (not echoed onto round 2). Required `## Related / lineage` resolved-wikilink table on promoted docs with role vocabulary. Invariants: both docs stay `canonical: true`, round 1 keeps `status: closed`, round 2 takes its own family key. Dependencies table gained the Iterates-on row; Reviewer-close bullet gained the outcome write. Field-name + echo decisions resolved at breakdown (flagged for owner Walk). Per HS-VGQV-T9T6-TASK-entity-model-iterates-on-relation-tier of HS-O97P-T9T6-EPIC-iteration-loop-lineage-relation-tier, from HS-3TZX-T9T6-RES-iteration-loop-lineage-and-capture Part A. |
1
+ ---
2
+ id: HS-L2-ENTITY
3
+ name: HeySpex Entity Model
4
+ type: l2
5
+ status: draft
6
+ priority: 0
7
+ owner: TODO — your name/handle
8
+ created: 2026-05-14
9
+ updated: 2026-05-18
10
+ tags: "[foundation, l2, entity-model, schema]"
11
+ ---
12
+
13
+
14
+
15
+
16
+
17
+
18
+ # HeySpex Entity Model
19
+
20
+ > [!important] Constitutional dependency
21
+ > Every constraint in this document derives from a numbered principle in heyspex-north-star. If a future change to this document contradicts the L1 spec, the change is wrong. When the L1 changes, this document updates downstream.
22
+
23
+ This L2 spec defines the shape of every artifact that lives in a HeySpex vault: the entity types, their frontmatter schemas, their on-disk layout, the dependency relations between them, and the wikilink resolution rules. It is the contract that the console, the CLI (`hs`), and any AI agent must satisfy when reading or writing the vault.
24
+
25
+ ## Vocabulary
26
+
27
+ - **Vault.** A folder of plain-text files under git. The on-disk layout is canonical. A single Builder owns one or more vaults; this document specifies the structure of one vault.
28
+ - **Spec.** A single markdown file in the vault. Every spec has YAML frontmatter and a markdown body. The frontmatter declares the spec's type and identity.
29
+ - **Entity type.** The value of the `type:` field in frontmatter. Determines which folder the spec lives in, which other fields are required, and how the spec is rendered in the console.
30
+ - **Hierarchy level.** L1 / L2 / L3. Vision → architecture → use-case. A different axis from entity type. Some entity types are level-tagged (`type: l1`, `type: l2`, `type: l3`); most are not.
31
+ - **Wikilink.** A reference of the form `[[link-target]]`, resolved by filename match across the vault.
32
+ - **Memory (conceptual layer).** Durable project knowledge surfaced together in the console: `memory-lesson`, `memory-decision`, `memory-reference`, `memory-ux-principle`, `memory-ui-pattern`, and `protocol`. Protocols are filed under `specs/protocols/` on disk but are a **subsection of Memory** in navigation and in `hs prime` — not a peer of Memory beside Epics/Tasks. See memory-protocols-subsection-of-memory. Work to *create* or change a protocol remains `type: epic` / `task`.
33
+
34
+ ## Vault root layout
35
+
36
+ ```
37
+ specs/
38
+ ├── heyspex-north-star.md # the L1; one file
39
+ ├── entity-model.md # this document; L2
40
+ ├── architecture-foundations.md # L2 sibling (renamed 2026-05-31 from architecture-pillars.md to disambiguate from the `type: pillar` entity)
41
+ ├── principles.md # L2 (expansion of L1's principle list)
42
+ ├── glossary.md # L2 (authoritative term dictionary) — North Star tier
43
+ ├── adr/ # one ADR per file
44
+ │ ├── adr-0001-vault-format.md
45
+ │ ├── adr-0002-derived-index.md
46
+ │ └── ...
47
+ ├── use-cases/ # one L3 use-case spec per file
48
+ │ ├── uc-capture-intent.md
49
+ │ ├── uc-decompose-intent.md
50
+ │ └── ...
51
+ ├── tasks/ # task / feature / bug specs
52
+ │ ├── HS-ZF0T-TASK-<slug>.md # legacy sequential, or HS-K7M2-<slug>.md (random)
53
+ │ └── ...
54
+ ├── milestones/ # milestone specs (the grouping above epic)
55
+ │ └── HS-MIL-<suffix>-<slug>.md # e.g. HS-IART-MIL-console-ia-restructure.md
56
+ ├── pillars/ # type: pillar — perpetual categorical containers (Scaling, Reliability, DX, etc.)
57
+ │ └── HS-PILR-<slug>.md # canonical Pillars; customers may also create HS-PILR-<suffix>-<slug>.md
58
+ ├── epics/ # epic specs
59
+ │ └── HS-0001-EPIC-<slug>.md # legacy, or HS-EPIC-X9P4-<slug>.md (random)
60
+ ├── branches/ # branch-brief specs (one per git branch)
61
+ │ └── <branch-slug>.md
62
+ ├── memory/
63
+ │ ├── lessons/ # memory-lesson specs
64
+ │ │ └── <slug>.md
65
+ │ ├── decisions/ # memory-decision specs
66
+ │ │ └── <slug>.md
67
+ │ ├── references/ # memory-reference specs
68
+ │ │ └── <slug>.md
69
+ │ ├── ux-principles/ # memory-ux-principle specs (cross-brand interaction rules)
70
+ │ │ └── <slug>.md
71
+ │ └── ui-patterns/ # memory-ui-pattern specs (reusable interaction/visual rules)
72
+ │ └── <slug>.md
73
+ ├── protocols/ # type: protocol — Memory subsection on disk (how-we-work rules)
74
+ │ └── <slug>.md # (not epic/task work; those stay in epics/ and tasks/)
75
+ ├── reviews/ # type: review — HS-REV-<suffix>-<slug>.md companions for in_review parent specs
76
+ │ └── HS-REV-<suffix>-<slug>.md
77
+ ├── business/ # business model / marketing / positioning / pricing
78
+ │ ├── positioning/ # business-positioning specs
79
+ │ │ └── <slug>.md
80
+ │ ├── pricing/ # business-pricing specs
81
+ │ │ └── <slug>.md
82
+ │ ├── marketing/ # business-marketing specs
83
+ │ │ └── <slug>.md
84
+ │ └── model/ # business-model specs
85
+ │ └── <slug>.md
86
+ ├── canvases/ # JSON Canvas design files + .heyspex.yaml sidecars ([[adr-0013-canvas-vault-format-and-obsidian-interop]])
87
+ │ ├── system-design.canvas
88
+ │ ├── system-design.heyspex.yaml
89
+ │ ├── diagrams/ # per-spec editable design diagrams ([[spec-diagram-design-vs-as-built-canvas]])
90
+ │ │ └── <spec-basename>-design.canvas
91
+ │ ├── as-built/ # sealed shipped spec diagrams (read-only in HeySpex)
92
+ │ │ └── <spec-basename>.canvas
93
+ │ └── ...
94
+ ├── roadmaps/ # phased roadmap specs (orchestration over tasks/epics)
95
+ │ └── HS-ROADMAP-<slug>.md
96
+ ├── roadmap-design-v2/ # Console Roadmap surface v2 design pack (protocol docs; not manifest entities)
97
+ │ ├── roadmap-design-v2-index.md # hub
98
+ │ ├── roadmap-v2-implementation-checklist.md # printable when coding
99
+ │ ├── roadmap-v2-implementation-handoff.md # read order + context
100
+ │ ├── roadmap-v2-tabextend-reference.md
101
+ │ ├── roadmap-v2-narrative-board-ux.md
102
+ │ ├── roadmap-v2-display-titles-and-summaries.md
103
+ │ └── roadmap-v2-*.md # see index for full map
104
+ ├── companion-research-board/ # Chrome + Companion mirrored research board (protocol docs)
105
+ │ ├── companion-research-board-index.md
106
+ │ ├── companion-research-board-dual-surface-mirror.md
107
+ │ ├── companion-research-board-implementation-checklist.md
108
+ │ └── companion-research-board-*.md
109
+ ├── future-considerations/ # pre-task research (promote to tasks when scheduled)
110
+ │ └── <slug>.md
111
+ ├── research/ # audit/research/draft notes
112
+ │ ├── HS-XBPJ-RES-bd-feature-inventory.md
113
+ │ └── ...
114
+ ├── captures/ # concise core capture entries — kind: idea | bug-fix | clip | reference | question | spec-import ([[capture-guardrails]])
115
+ │ └── HS-CAP-<suffix>-<slug>.md
116
+ ├── flows/ # planning-flow lifecycle diagrams ([[planning-flow-research-epic-lifecycle]])
117
+ │ └── HS-FLOW-<NNN>-<slug>.md
118
+ ├── drafts/ # ingested-but-untriaged
119
+ │ └── <slug>.md
120
+ ├── events/ # audit-export JSONL — one file per UTC month ([[HS-FD93-AEHR-EPIC-audit-export-heyspex-yaml-and-heyspex-retirement]])
121
+ │ └── YYYY-MM.jsonl # committed snapshot of claim/focus/spec events (git-clone-is-backup)
122
+ └── daily/ # optional daily notes
123
+ └── 2026-05-14.md
124
+ ```
125
+
126
+ Two committed siblings live **outside** `specs/` at the repo root, written/owned by the control plane:
127
+
128
+ - **`heyspex.yaml`** — the top-level vault marker (`schema_version`, `console_url`, `vault_root`) written by `hs init` (HS-FD93-AEHR-EPIC-audit-export-heyspex-yaml-and-heyspex-retirement §2). `schema_version: 2` = the post-SCAH Neon-control-plane layout; `1` was the implicit pre-SCAH `.heyspex/`-files layout. The web companion reads it to bootstrap a vault.
129
+ - **`.heyspex/`** — per-vault config home (adr-0009-heyspex-directory-contract): tracked `config.yaml`, `.gitignore`, `*.example` scaffolding templates, and `focus.yaml` (cross-machine pin SSOT, focus-yaml-is-tracked-pin-ssot). On the **free tier** (no Neon) this directory ALSO holds the live runtime state (`active-claims.json`, `claim-history.jsonl`, `focus-history.jsonl`); on the **paid tier** that state lives in Neon and these files are the export's free-tier source only (HS-7FA9-FREE-EPIC-zero-neon-local-only-tier). The audit export (`specs/events/`) reads whichever store is live for the tier.
130
+
131
+ The L1 lives at the vault root as `heyspex-north-star.md`. The other L2 specs (`entity-model.md`, `architecture-foundations.md`, `principles.md`, `glossary.md`) also live at the root because they are foundational — in the console they surface under the **North Star** rail group, not Business (which is scoped to `business-*` types) or Memory (scoped to `memory-*` types). The glossary carries a `marketing` *tag* because marketing copy consumes its definitions, but its `type: l2` keeps it in the foundational tier. Everything else is filed under a folder matching its entity type.
132
+
133
+ > [!note] Why folders, not flat?
134
+ > Obsidian works with either, and HeySpex's link resolution treats folders as opaque (see [Wikilink resolution rules](#wikilink-resolution-rules)). Folders exist purely for human navigation and for the indexer's "list all <type>" view. Files can be moved between folders without breaking links.
135
+
136
+ ## Entity types
137
+
138
+ The table below names every entity type in v1 and points at the frontmatter contract. Each type has its own subsection below with the full schema.
139
+
140
+ | `type:` value | Folder | Hierarchy level | One-line description |
141
+ | --- | --- | --- | --- |
142
+ | `l1` | root | L1 | The North Star vision; one per vault. |
143
+ | `l2` | root | L2 | Foundational architecture or model documents. |
144
+ | `l3` | `use-cases/` | L3 | Use-case specs that elaborate L1 system use cases. |
145
+ | `adr` | `adr/` | — | Architectural Decision Record. |
146
+ | `task` | `tasks/` | — | The atomic unit of work. Subsumes bd's `task`, `feature`, and `bug` types. |
147
+ | `epic` | `epics/` | — | A grouping of related tasks under a common goal. |
148
+ | `milestone` | `milestones/` | — | The grouping **above** epic: a coherent multi-epic ship that closes when every child epic closes and the milestone's own acceptance gate is met. Distinct from the legacy `kind: milestone` task discriminator (see [`#### task`](#task)). |
149
+ | `diagnose` | `diagnoses/` | — | A user-reported **bug fix** as first-class executable work (claimable, decomposable). Standalone by default — no epic/milestone parent required — with an optional `parent:` (epic or milestone) for organization. Carries `severity:`; surfaces on the Resolution surface as a derived `kind: "bug"` row that links back to the diagnose. Elevates the bug concept that `task` historically subsumed (see [`#### task`](#task)) into its own lane. Ratified by introduction lineage HS-OXBS-BFIX-MIL-bug-fix-diagnose-lane (milestone-precedent, like `milestone`/`pillar`). See HS-AO2F-1KZK-RES-bug-fix-resolution-task-entity-model-and. Proposed (not-yet-ratified) field extensions — a `problem_layer: symptom \| core \| unknown` classifier and an optional bidirectional `caused_by`/`symptoms` link pair — are designed in HS-4QOK-UBJM-RES-diagnose-problem-layer-classification-sy. End-to-end workflow procedure: resolution-diagnose-workflow. |
150
+ | `pillar` | `pillars/` | — | Perpetual categorical container that never closes; cross-cutting axis for epics. Every epic carries `pillar:` (required, single primary) + optional `secondary_pillars:`. Canonical Pillars ship pre-loaded with HeySpex; customers may add scoped Pillars. Orthogonal to `milestone` (a milestone is a shippable increment; a Pillar is a perpetual concern). See HS-PILR-pillars (meta-Pillar) and HS-AWDI-PILR-RES-pm-perpetual-container-terminology (research + 2026-05-31 resolution). |
151
+ | `agent` | `agents/` | — | A durable, user-authored **agent identity** (the "home where agents live"): name, role, preferred tools, model tier, procedures, and persona body. Compiles to the host-native agent formats (`.claude/agents/*.md`, `.cursor/agents/*.md`) — the vault spec is the SSOT, the generated files are derived. Carries HeySpex-only extras (`lane`, `color`, `owned_paths`, `golden_cases`, and `source_attribution` when imported) stripped on compile. `owned_paths` is the machine-checkable file-ownership set for parallel-agent stomp-detection; `lane` remains a human label. An **imported** agent (community-collection per HS-SURM-TMXZ-EPIC-default-agent-roster-and-import-path AC#2, or own-agent per HS-11ME-1NL4-EPIC-import-and-adopt-your-own-existing-agent) records its provenance in `source_attribution` (collection, url, path, commit, license, imported_at); authored agents omit it. Evergreen (does not close). See HS-XNX5-UQHA-MIL-agent-home. |
152
+ | `golden-case` | `agents/golden-cases/` | — | A saved **eval fixture** for one agent identity: the prompt (and optional setup + deterministic graders) the test-before-publish harness runs against a draft agent (HS-R1JM-OAG3-EPIC-agent-testing-harness-try-a-draft-agent). Stored under the `agents/` tree so fixtures version beside the identities they exercise in the same git history. Evergreen (updated in place). Introduced by HS-8AQT-J6FU-TASK-golden-case-vault-artifact-format-and-st. |
153
+ | `branch-brief` | `branches/` | — | "What this git branch is trying to do, and how it actually went." Plan-vs-actual + evidence for one branch. |
154
+ | `memory-lesson` | `memory/lessons/` | — | "Here's what went wrong and how to avoid it." |
155
+ | `memory-decision` | `memory/decisions/` | — | "Here's what we chose and why." |
156
+ | `memory-reference` | `memory/references/` | — | "Here's where to look for X." |
157
+ | `memory-ui-pattern` | `memory/ui-patterns/` | — | "Here's a reusable console interaction/visual pattern to copy." |
158
+ | `memory-ux-principle` | `memory/ux-principles/` | — | "Here's a cross-brand UX rule (what/why) before implementation." |
159
+ | `protocol` | `protocols/` | Memory subsection | Repeatable how-we-work rules for agents/humans (session-close, memory capture, MCP usage). Browsed under Memory → Protocols in console; not a peer nav item beside Memory. |
160
+ | `review` | `reviews/` | — | Companion fill-in workspace for an `in_review` parent spec — acceptance verification, reviewer notes, memory candidates, decision. One per parent, suffix-matched 1:1. |
161
+ | `business-positioning` | `business/positioning/` | — | Brand H1, taglines, audience qualifier, message hierarchy. |
162
+ | `business-pricing` | `business/pricing/` | — | Tier tables, AI add-on terms, discount/refund policy. |
163
+ | `business-marketing` | `business/marketing/` | — | Channels, launches, campaigns, copy decks. |
164
+ | `business-model` | `business/model/` | — | Unit economics, cost structure, monetization decisions. |
165
+ | `roadmap` | `roadmaps/` | — | Phased plan orchestrating tasks/epics; compact `roadmap_manifest` for indexer/CLI (adr-0012-roadmap-storage). |
166
+ | `capture` | `captures/` | — | Concise capture entry; `kind:` field discriminates idea / bug-fix / clip / reference / question / spec-import. Replaces the legacy `type: idea` entity (renamed 2026-05-22 per HS-B2B8-DLQI-EPIC-capture-rename-migration-idea-to-capture). `spec-import` is the **import-origin** kind — one capture per imported foreign doc, created by the bulk spec-import feature (HS-8V8T-8SBG-EPIC-first-connect-rough-import / HS-ROIU-9P4B-EPIC-import-preview-and-confirm); grounding HS-J7T5-VEW8-RES-import-specs-one-click-feature-pipeline. |
167
+ | `research` | `research/` | — | Audit reports, feature inventories, exploratory notes. |
168
+ | `planning-flow` | `flows/` | — | Epic lifecycle orchestration diagram (mermaid source + canvas projection). |
169
+ | `draft` | `drafts/` | — | Ingested content awaiting triage. |
170
+ | `daily` | `daily/` | — | Optional daily-note scratchpad. |
171
+ | `run-retrospective` | `dev-factory/reports/retrospectives/` (outside `specs/`, sibling to `heyspex.yaml`/`.heyspex/`) | — | A **generated** run-close record — never hand-authored, not scheduled/claimable, not a wikilink-addressable spec. One file per COO-dispatcher run, `<runId>.md` (`runId` = the run's milestone id), produced deterministically by `dev-factory/lib/retrospective.mjs` from `run-events.jsonl` + the gate ledger + `coo-dispatcher-state.json`. Frontmatter: `runId`, `generatedAt`, `shippedUnits[]` (gate-verified units), `autoAppliedDecisions[]` (dispatcher auto-applies), `stoppedReason`, `restartCondition`, `ownerGates[]` (pending owner-only gates). Body: the three Retrospective sections (what went well / what could be improved / actionable next steps). Read by What's Next's Retrospective hero (Assisted/Autonomous fronts) — see HS-4HI5-JEPA-EPIC-what-s-next-multi-run-mode-run-dashboard and the-retrospective-is-a-generated-run-rec.
172
+
173
+ Adding a new entity type requires an ADR. `branch-brief` was added by adr-0008-branch-brief-and-evidence-schema; `roadmap` by adr-0012-roadmap-storage; all other core types ratified at adr-0001-vault-format. `milestone` is the exception that proves the rule: the type was used informally first — 33 `HS-MIL-*` files (e.g. HS-RM22-IART-MIL-console-ia-restructure) were hand-authored before formalization — so rather than a standalone ADR, the convention is ratified by its introduction lineage, HS-VK7C-MILE-EPIC-milestone-entity-type-and-surfacing (this schema row, the validator, MCP support) followed by HS-OFR5-M2J3-EPIC-introduce-milestones-as-highest-entity (required-parent-on-epics + the roadmap drilldown). The canonical hierarchy these two epics serve is documented in [[spec-hierarchy]].
174
+
175
+ `pillar` follows the same milestone-precedent (no standalone "introduce the entity type" ADR): ratified by its introduction lineage HS-EBIL-0011-EPIC-goal-hierarchy-l2-strategic-pillars-l3 (the L2 perpetual-Pillars epic, sibling of HS-OR8V-0010-EPIC-north-star-vision-first-class-entity in HS-J6K8-NSPL-MIL-north-star-and-strategic-pillars-hierarchy) plus the design captured in HS-AWDI-PILR-RES-pm-perpetual-container-terminology; the privacy-boundary side of the work (aggregate-learning telemetry on the Pillar Library) is in adr-0016-pillar-library-privacy-boundary. The 2026-05-31 resolution that produced the Pillar tier also retired the previously planned L3 Themes tier (per-epic teleological anchoring collapsed into perpetual Pillars). First canonical Pillar filed: HS-PILR-pillars (the meta-Pillar; dogfooding instance).
176
+
177
+ `agent` follows the same milestone/pillar-precedent (no standalone "introduce the entity type" ADR): ratified by its introduction lineage HS-XNX5-UQHA-MIL-agent-home (this schema row, the frontmatter-schema branch, the `KIND_RULES`/`TYPE_TO_FOLDER` registration, and the compiler that exports identities to host-native `.claude/agents`/`.cursor/agents` formats). The agent identity is the durable, user-authored counterpart to the ephemeral control-plane presence (`active_claims`) — the vault holds *who an agent is*, the control plane holds *what it is doing now*. Positioning context: HS-O46R-R30X-RES-agent-layer-as-the-demand-side-twin-of-t.
178
+
179
+ `golden-case` follows the same introduction-lineage precedent under HS-R1JM-OAG3-EPIC-agent-testing-harness-try-a-draft-agent / HS-8AQT-J6FU-TASK-golden-case-vault-artifact-format-and-st: a first-class vault artifact for agent eval fixtures (not JSON sidecars or console-only blobs). Bodies follow a fixed section contract; optional deterministic graders validate against `specs/schemas/golden-case-body.schema.json`.
180
+
181
+ ## Lifecycle classes
182
+
183
+ Entity types fall into four lifecycle classes. The class determines which subset of the [universal status enum](#frontmatter-schemas) a type uses, when its `canonical:` flag flips, and what the close-out ritual is. **A reader asking *"can I flip this file's status to `in_review`?"* should consult this section first — the answer depends entirely on which class the file's type belongs to.**
184
+
185
+ ### Evergreen (living reference substrate)
186
+
187
+ `l1` · `l2` · `l3` · `adr` · `pillar` · `agent` · `golden-case` · `protocol` · `memory-lesson` · `memory-decision` · `memory-reference` · `memory-ui-pattern` · `memory-ux-principle` · `research` · `business-positioning` · `business-pricing` · `business-marketing` · `business-model`
188
+
189
+ These are **standalone reference artifacts**. `status: open` is the resting state and stays the resting state for the doc's entire life. There are no `in_review` or `closed` transitions — the close-as-historical-record ritual does not apply.
190
+
191
+ - **Updates** happen in-place: amend the body, bump `updated:`, append a change-log row.
192
+ - **Supersession** is the only "lifecycle event" — record it via relational fields (`supersedes:` / `superseded_by:` on ADRs, `superseded_by:` on memories and research, `canonical: false` as the universal drift tombstone per canonicalization-arbiter-is-link-graph-not-policy). The original stays as the historical record at `status: open`.
193
+ - **Research lineage** to downstream artifacts is recorded via `enhances:`, `spawned_epics:`, `bound_epic:`, `incorporated_into:` — these are relational edges, **not** status transitions. See research-is-evergreen for the full rule and the deferred MCP-tool / UI-epic / validator surfaces still being remediated.
194
+
195
+ Session-close protocol carve-out: an agent who touched an evergreen doc this session **must not** flip its status. See [[session-close-protocol]] step 1.
196
+
197
+ ### Trackable plan-side (open → in_progress → in_review → closed)
198
+
199
+ `task` · `epic` · `milestone` · `diagnose` · `review` · `branch-brief` · `planning-flow`
200
+
201
+ These are **work containers** with a definite scope and a moment at which they're done. They follow the full status flow and are **frozen at close** per closed-trackable-specs-are-historical-record-not-living-docs — the body becomes historical record of the plan as it was at the moment of close.
202
+
203
+ - **Session close** flips completed-this-session specs to `status: in_review` and creates an `HS-REV-<suffix>` companion ([[session-close-protocol]]).
204
+ - **Reviewer close** flips `in_review → closed`, flips `canonical: true → false`, optionally sets the `outcome:`/`outcome_note:` verdict (`met` | `partially-met` | `not-met` — reviewer judgment, never inferred; see §Iteration lineage), and freezes the frontmatter forever after.
205
+ - **`branch-brief`** closes when the branch ships; **`planning-flow`** closes with its bound epic.
206
+
207
+ ### Single-shot ingest (triage-driven; promotion replaces closure)
208
+
209
+ `capture` · `draft` · `daily`
210
+
211
+ These are **inbound surfaces** scoped to one input session. They don't close in the lifecycle sense — they get **promoted** (capture → research / task / epic; draft → some other type) or remain as a dated scratchpad (daily).
212
+
213
+ - **`capture`** carries `promoted_to:` as a binary pointer to the first downstream artifact and `informs_research:` as the M:N lineage list (see the `capture` schema below).
214
+ - **`draft`** is triaged by editing the `type:` field in place and moving the file to the destination folder.
215
+ - **`daily`** is a single-date scratchpad — no status semantics; the date IS the lifecycle.
216
+
217
+ ### Per-instance (class declared at filing time)
218
+
219
+ `roadmap`
220
+
221
+ `roadmap` is the one type whose lifecycle class depends on the specific roadmap. A perpetual product roadmap is evergreen; a per-quarter ship plan is trackable. Each `HS-ROADMAP-*` declares its class via its own `status:` value at filing: roadmaps that never close stay `open`; roadmaps with a defined ship horizon use the trackable flow. See adr-0012-roadmap-storage for the compact-manifest design that supports both shapes.
222
+
223
+ ### Cross-reference summary
224
+
225
+ | Class | Status values used | Close-as-historical-record? | `canonical: false` trigger |
226
+ |---|---|---|---|
227
+ | Evergreen | `open` (resting state) | No | Supersession (relational edge) |
228
+ | Trackable plan-side | `open` → `in_progress` → `in_review` → `closed` (plus `deferred`) | Yes — frozen at close | Close-out write |
229
+ | Single-shot ingest | `open` (capture/draft pre-promotion) / N/A (daily) | No — promoted or archived by date | Not used |
230
+ | Per-instance | Declared at filing | Per-instance | Per-instance |
231
+
232
+ ## Identity, IDs, and filenames
233
+
234
+ > [!important] Identifier format migration in progress (updated 2026-08-11)
235
+ > The vault is migrating to **file-key-first** (era 3) ordering: `HS-<file key>-<family key>-<type label>-<title slug>` — the leading token after `HS-` is the **unique per-file ID**, not the family (HS-6B0S-Q9YG-RES-naming-system-v3-file-key-first, rulings naming-v3-7-rulings-hs-namespace-kept; owner re-confirmed the token order 2026-08-11). On disk today most files are still **family-first** (era 2, `HS-<family key>-<type label>-…`) or legacy type-first (era 1); the vault-wide rename is built and **pending owner review/execution** (HS-L6Y4-01D7-EPIC-deterministic-rename-with-link-rewrite). The type label's position (1st/2nd/3rd token) tells you a filename's era — never guess which token is the file ID by position alone; read frontmatter `file_id:`/`id:`. The ID-format and filename tables below still show older orderings pending the flip; the token **names** are canonical in every era. Plain-language explainer: how-spec-ids-and-naming-work.
236
+
237
+ ### Identifier tokens (file key · family key · type label · title slug)
238
+
239
+ A managed spec's basename decomposes into named tokens. Canonical names per glossary §Identifier anatomy and HS-37O7-T9KP-RES-identifier-token-terminology-family-key (external-validated; supersedes the "pipeline ID / type token / file ID" names in HS-MF8M-CCVE-RES-family-first-spec-identifier-format-hs-i).
240
+
241
+ | Token | Example | Role |
242
+ | --- | --- | --- |
243
+ | **File key** | `L6Y4` | The per-file 4+ char uppercase-alphanumeric key (at least one letter), **unique vault-wide** — the identity and one-token citation handle for exactly one file. Recorded in frontmatter `file_id:` (SSOT) and embedded in the filename; becomes the `id:` itself (`id: HS-L6Y4`) under era 3. Server-minted, never hand-picked. |
244
+ | **Family key** | `P2TR` | The shared 4+ char alphanumeric code that groups one initiative's specs — an epic and all its child tasks, research, and reviews — into one greppable **family** (`grep HS-P2TR` returns the whole family). Shared across the family; uniqueness is enforced on the full **basename** per kind, never on the family key alone (see vault-id-assignment-protocol §Shared-suffix families). |
245
+ | **Type label** | `TASK` | The entity-kind token (`TASK`, `EPIC`, `MILESTONE`, `RESEARCH`, `REVIEW`, …). A **human-readable hint only**. |
246
+ | **Title slug** | `verification-state-schema` | The lowercased-hyphenated descriptive tail derived from `title:` — the part that makes each basename unique within a family. |
247
+
248
+ **Filename shapes (era = type-label position):**
249
+ - **File-key-first (era 3, ratified target):** `HS-<file key>-<family key>-<type label>-<title slug>.md` → `HS-L6Y4-01D7-EPIC-deterministic-rename-with-link-rewrite.md` — lands vault-wide with HS-L6Y4-01D7-EPIC-deterministic-rename-with-link-rewrite.
250
+ - **Family-first (era 2, current on disk / current minting):** `HS-<family key>-<type label>-[<file key>-]<title slug>.md` → `HS-P2TR-TASK-verification-state-schema.md`, `HS-0G910-EPIC-G3D3-api-foundation-keys-scopes-error.md`
251
+ - **Type-first (era 1, legacy):** `HS-<type label>-<family key>-<title slug>.md` → `HS-TASK-P2TR-verification-state-schema.md`
252
+
253
+ **Routing rule (SSOT for entity type):** code MUST determine a spec's entity type from the frontmatter **`type:`** field (and its directory), **never** by parsing the type label out of the filename. The type label is a convenience hint that survives out of directory context (wikilinks, search results, clipboard, terminal output); it is **not** authoritative and must never drive a mutation, a route, or a reclassification on its own. This is the semantic-identifier guardrail from HS-MF8M-CCVE-RES-family-first-spec-identifier-format-hs-i (don't route on parsed-ID semantics).
254
+
255
+ **Retired / avoid:** **"suffix"** (the legacy name for the family key — self-contradictory once the family key leads), **"pipeline ID"** (overloaded with CI/CD + the feature-pipeline graph), **"prefix" / "type prefix"** for the family key (industry convention encodes *type*, not a family). The legacy term `<suffix>` still appears in the tables below and in the `legacy_slug_only` naming atoms until the Phase 3 codegen flip.
256
+
257
+ ### ID format
258
+
259
+ | Entity type | ID format | Example |
260
+ | --- | --- | --- |
261
+ | `l1` | `HS-L1` | `HS-L1` |
262
+ | `l2` | `HS-L2-<UPPER>` | `HS-L2-ENTITY`, `HS-L2-FOUNDATIONS` |
263
+ | `l3` | `HS-UC-<short>` | `HS-UC-CAPTURE` |
264
+ | `adr` | `HS-ADR-NNNN` (zero-padded sequence) | `HS-ADR-0001` |
265
+ | `task` | `HS-TASK-<suffix>` — `<suffix>` is 4+ random A-Z0-9 (vault-id-assignment-protocol). Pre-2026-05-22 entries (`HS-NNNN`, `HS-<4+ A-Z0-9>`) were migrated to this prefix. | `HS-TASK-K7M2` |
266
+ | `epic` | `HS-EPIC-<suffix>` — same rules | `HS-DE8L`, `HS-V888` |
267
+ | `milestone` | `HS-MIL-<suffix>` — `<suffix>` is 4+ uppercase `A–Z0–9` (vault-id-assignment-protocol); legacy 4-character mnemonic IDs (e.g. `HS-RM22`) remain valid | `HS-RM22`, `HS-1JC8` |
268
+ | `diagnose` | `HS-DIAG-<suffix>` — `<suffix>` is 4+ random A-Z0-9 (vault-id-assignment-protocol) | `HS-DIAG-K7M2` |
269
+ | `pillar` | `HS-PILR-<slug>` (canonical; slug uniquely identifies the concept; no random suffix) or `HS-PILR-<suffix>-<slug>` (customer-scoped; random 4+ A-Z0-9) | `HS-PILR-pillars`, `HS-PILR-scaling`, `HS-PILR-K7M2-team-onboarding` |
270
+ | `branch-brief` | `HS-BB-<slug>` | `HS-BB-feature-checkout-redesign` |
271
+ | `memory-lesson` | `HS-LES-<slug>` | `HS-LES-bd-process-leak` |
272
+ | `memory-decision` | `HS-DEC-<slug>` | `HS-DEC-dep-relate-for-epic-children` |
273
+ | `memory-reference` | `HS-REF-<slug>` | `HS-REF-dolt-time-travel-docs` |
274
+ | `memory-ui-pattern` | `HS-UIP-<slug>` | `HS-UIP-rail-active-vs-keyboard-cursor` |
275
+ | `memory-ux-principle` | `HS-UXP-<slug>` | `HS-UXP-toggle-current-state-semantics` |
276
+ | `protocol` | `HS-PROTO-<slug>` | `HS-PROTO-session-close` |
277
+ | `review` | `HS-REV-<suffix>` — `<suffix>` matches the parent task/epic id 1:1 (e.g. parent `HS-76RX` → review `HS-QMHO`). Detection: [`extractSpecSuffix` in `console/src/lib/vault.ts:155`](../console/src/lib/vault.ts). | `HS-QMHO`, `HS-R3HO`, `HS-CCEQ` |
278
+ | `business-positioning` | `HS-BIZ-POS-<slug>` | `HS-BIZ-POS-2nd-brain-harness-for-specs` |
279
+ | `business-pricing` | `HS-BIZ-PRI-<slug>` | `HS-BIZ-PRI-tiers-and-byok-2026-05` |
280
+ | `business-marketing` | `HS-BIZ-MKT-<slug>` | `HS-BIZ-MKT-launch-playbook-companion` |
281
+ | `business-model` | `HS-BIZ-MOD-<slug>` | `HS-BIZ-MOD-flat-per-vault-economics` |
282
+ | `roadmap` | `HS-ROADMAP-<SLUG>` | `HS-ROADMAP-PRODUCT` |
283
+ | `research` | `HS-RES-<suffix>` — legacy `HS-RES-NNN` or random `HS-RES-<4+ A-Z0-9>` (vault-id-assignment-protocol) | `HS-RES-001`, `HS-RES-K7M2` |
284
+ | `capture` | `HS-CAP-<suffix>` — same rules; legacy `HS-IDEA-<suffix>` preserved in `legacy_idea_id:` for archaeology | `HS-CAP-JKLN`, `HS-CAP-OAP3` |
285
+ | `agent` | `HS-AGENT-<suffix>` — `<suffix>` is 4+ random A-Z0-9 (vault-id-assignment-protocol) | `HS-634I` |
286
+ | `golden-case` | `HS-GCASE-<suffix>` — same rules | `HS-HPIO` |
287
+ | `planning-flow` | `HS-FLOW-NNN` | `HS-FLOW-001` |
288
+ | `draft` | `HS-DRAFT-<slug>` | `HS-DRAFT-2026-05-14-1130-article-link` |
289
+ | `daily` | `HS-DAILY-YYYY-MM-DD` | `HS-DAILY-2026-05-14` |
290
+
291
+ **New task/epic/research/capture IDs** (console `POST /api/specs`, MCP `heyspex_save_research`, agents): allocate a random 4-character suffix from `A–Z` and `0–9`, checking the full vault for collisions (frontmatter `id` and filename prefix). When every combination at the current length is taken, the suffix grows to 5 characters, then 6, and so on. Legacy sequential IDs (`HS-NNNN`, `HS-RES-NNN`, `HS-IDEA-*`, slug-only research filenames) remain valid in the read-path until HS-23PN-9HVY-TASK-capture-rename-repo-wide-wikilink-rewrite retires them. Validate with `npm run validate:vault-ids` at the repo root.
292
+
293
+ ### Filename derivation
294
+
295
+ The filename is **derived from the ID + an optional slug**, lowercased, with the same prefix the ID uses minus the `HS-` prefix (kept lowercase in filenames for readability):
296
+
297
+ | Entity | Filename pattern | Example |
298
+ | --- | --- | --- |
299
+ | `l1` | `heyspex-north-star.md` (literal, only one) | `heyspex-north-star.md` |
300
+ | `l2` | `<topic>.md` (literal slug) | `entity-model.md` |
301
+ | `l3` | `uc-<short>.md` | `uc-capture-intent.md` |
302
+ | `adr` | `adr-NNNN-<slug>.md` | `adr-0001-vault-format.md` |
303
+ | `task` | `HS-TASK-<suffix>-<slug>.md` | `HS-TASK-K7M2-fix-foo.md` |
304
+ | `epic` | `HS-EPIC-<suffix>-<slug>.md` | `HS-0007-EPIC-console-resilience.md`, `HS-W9ZE-EPIC-design-system-enforcement-auto-surface-u.md` |
305
+ | `milestone` | `HS-MIL-<suffix>-<slug>.md` | `HS-IART-MIL-console-ia-restructure.md` |
306
+ | `diagnose` | `HS-DIAG-<suffix>-<slug>.md` | `HS-DIAG-K7M2-console-startup-lag.md` |
307
+ | `pillar` | `HS-PILR-<slug>.md` (canonical; slug IS the ID with no random suffix) or `HS-PILR-<suffix>-<slug>.md` (customer-scoped) | `HS-PILR-pillars.md`, `HS-PILR-K7M2-team-onboarding.md` |
308
+ | `branch-brief` | `<branch-slug>.md` | `feature-checkout-redesign.md` |
309
+ | `memory-lesson` | `<slug>.md` | `bd-process-leak.md` |
310
+ | `memory-decision` | `<slug>.md` | `dep-relate-for-epic-children.md` |
311
+ | `memory-reference` | `<slug>.md` | `dolt-time-travel-docs.md` |
312
+ | `memory-ui-pattern` | `<slug>.md` | `rail-active-vs-keyboard-cursor.md` |
313
+ | `memory-ux-principle` | `<slug>.md` | `toggle-current-state-semantics.md` |
314
+ | `protocol` | `<slug>.md` | `session-close-protocol.md` |
315
+ | `review` | `HS-REV-<suffix>-<slug>.md` (slug truncated from parent's slug, ≤40 chars) | `HS-SK1L-REV-hs-idea-skill-four-beats-layered-confirms.md` |
316
+ | `research` | `HS-RES-<suffix>-<slug>.md` (legacy: `<slug>.md` only) | `HS-RES-K7M2-agent-governance.md` |
317
+ | `capture` | `HS-CAP-<suffix>-<slug>.md` | `HS-CAP-JKLN-vault-triage.md` |
318
+ | `agent` | `HS-AGENT-<suffix>-<slug>.md` | `HS-9TNK-AGENT-code-reviewer.md` |
319
+ | `golden-case` | `HS-GCASE-<suffix>-<slug>.md` | `HS-R4VW-GCASE-review-missing-ac.md` |
320
+ | `planning-flow` | `HS-FLOW-<NNN>-<slug>.md` | `HS-FLOW-001-epic-planning-flow-studio.md` |
321
+ | `draft` | `<slug>.md` | `2026-05-14-1130-article-link.md` |
322
+ | `daily` | `YYYY-MM-DD.md` | `2026-05-14.md` |
323
+
324
+ Slug rules: lowercase, ASCII, dash-separated, no trailing punctuation, no leading number for non-task types.
325
+
326
+ > [!note] Branch-brief slug
327
+ > For `branch-brief` the slug is **derived from the git branch name**: lowercased, every run of non-`[a-z0-9]` characters collapsed to a single `-`, trailing `-` stripped. `feature/checkout-redesign` → `feature-checkout-redesign`. The raw, unsanitized branch name is preserved in the required `branch:` field — the slug is for the filesystem and wikilink resolution only; git lookups use `branch:`. Ratified in adr-0008-branch-brief-and-evidence-schema.
328
+
329
+ > [!note] Why not put the ID in the filename for memories?
330
+ > bd memory keys are content-derived (`lessons/bd-process-leak`), and the Builder already thinks in those terms. The filename `bd-process-leak.md` IS the key, the ID is the metadata. Wikilink resolution finds the file by name; the `HS-LES-` prefix in the `id:` field is for the index and for stable cross-references.
331
+
332
+ ### ID + filename for the migration
333
+
334
+ The migration plan (migration-from-bd) maps each bd issue to a new HeySpex task ID by sorting bd issues by `created_at` and assigning `HS-U2K9`, `HS-FMWM`, etc., in that order. The original bd ID is preserved as `legacy_bd_id` in frontmatter so cross-references in old commits/logs remain resolvable.
335
+
336
+ ## Frontmatter schemas
337
+
338
+ Every spec opens with a YAML frontmatter block. The schemas below define **required** fields (must be present and non-empty) and **optional** fields (may be omitted).
339
+
340
+ Universal fields (every spec has these):
341
+
342
+ ```yaml
343
+ id: # required, string, matches the entity type's ID format
344
+ name: # required, string, human-readable title
345
+ type: # required, one of the entity type values above
346
+ status: # required, see status enum below
347
+ priority: # required, integer 0–4 (0 = highest)
348
+ owner: # required, string (email or handle) — the ORIGINATING + accountable human (doubles as author).
349
+ # See §Authorship provenance below.
350
+ created: # required, ISO-8601 date (YYYY-MM-DD)
351
+ updated: # required, ISO-8601 date
352
+ contributors: # optional, list of "Name <email>" — OTHER humans who later enhanced the doc (append-only;
353
+ # OMIT when empty). Auto-stamped, never hand-maintained. See §Authorship provenance.
354
+ assisted_by: # optional, list of "AGENT" or "AGENT:MODEL" — agents/IDEs + model that assisted (append-only;
355
+ # OMIT when empty). Auto-stamped, never hand-maintained. See §Authorship provenance.
356
+ tags: # optional, list of strings
357
+ related: # optional, list of wikilinks (alternative to inline body links)
358
+ what_matters: # optional, enum: star | heart — per-spec user-priority signal
359
+ ```
360
+
361
+ **`what_matters`** is the per-spec user-priority signal that replaced the retired What Matters column (scheduler-and-whats-next-what-matters-column-mechanics, HS-78E7-Y9OJ-EPIC-deterministic-priority-scheduler). It is optional and two-level:
362
+
363
+ - `star` (★) — *priority*: "consider this please."
364
+ - `heart` (♥) — *very important*: "appreciate this being next if at all possible." A stronger signal than `star`.
365
+ - absent — unflagged (no default is injected).
366
+
367
+ The value is held verbatim; an unrecognized value is tolerated by the parser and emits a warn-only `INVALID_WHAT_MATTERS` validator notice rather than an error. A Phase 7b scheduler stage consumes it for ordering.
368
+
369
+ ### Authorship provenance
370
+
371
+ Three universal fields carry GitHub-faithful provenance, following the cross-foundation `Assisted-by:` convention (Linux Kernel, Fedora, LLVM, OpenTelemetry) that keeps the human as the sole accountable author and records AI as a tool that *assisted*, not a co-author. Origin: HS-N1WG-W48S-RES-git-github-authorship-attribution-best-p → HS-Y4JH-IKIM-EPIC-authorship-provenance-frontmatter-owner. The SSOT question (frontmatter vs git history) is resolved in provenance-frontmatter-is-ssot-not-git-echo.
372
+
373
+ | Field | Who | Item format | Cardinality |
374
+ | --- | --- | --- | --- |
375
+ | `owner` | the **originating + accountable** human (doubles as author) | handle or email (e.g. `pwher`) | single scalar |
376
+ | `contributors` | **other** humans who later enhanced the doc | `Name <email>` — GitHub form, round-trips with the `Co-authored-by:` trailer | append-only list |
377
+ | `assisted_by` | agents/IDEs + model that assisted | `AGENT` or `AGENT:MODEL` — e.g. `Claude Opus 4.8`, `Cursor` | append-only list |
378
+
379
+ Semantics (apply to every entity kind):
380
+
381
+ - **`owner` is universally required** and unchanged in type — only its meaning is enriched here (originator + accountable, not merely a pointer). A later ownership *transfer* is not preserved in frontmatter; git history (first-commit author) retains the original creator. Acceptable trade per HS-N1WG-W48S-RES-git-github-authorship-attribution-best-p.
382
+ - **`contributors` and `assisted_by` are universal-but-optional and omit-when-empty** — they are not emitted as empty lists, so a solo-vault doc stays clean and only grows a list once a second human or an agent touches the doc.
383
+ - **Append-only + de-dup, never reorder/remove.** A new acting human ≠ `owner` is appended to `contributors`; an acting agent is appended to `assisted_by`; an entry already present is not duplicated.
384
+ - **Auto-stamped, never hand-maintained.** Seeded at creation (`owner` from the resolved user, `assisted_by` from the acting agent) and appended on edit, sourced from the existing claim/session trail (Neon `active_claims`). Authoring/wiring lives in HS-Y4JH-IKIM-EPIC-authorship-provenance-frontmatter-owner.
385
+
386
+ > [!note] Schema addition self-gating
387
+ > Per entity-model-self-gates-schema-changes-via-adr, new reserved frontmatter keys are normally gated behind an ADR. `contributors`/`assisted_by` are admitted here **without a standalone ADR** under the same milestone-precedent the `milestone` and `pillar` entity types used: they are additive, optional, omit-when-empty enrichments of the existing universal `owner` field (not a new entity type or a new lifecycle), ratified by their introduction lineage HS-N1WG-W48S-RES-git-github-authorship-attribution-best-p → HS-Y4JH-IKIM-EPIC-authorship-provenance-frontmatter-owner. The decision rationale is recorded in provenance-frontmatter-is-ssot-not-git-echo.
388
+
389
+ ### Artifact linkage
390
+
391
+ An optional `artifact:` mapping records a published Claude Artifact that this spec/doc is the vault source for, so the artifact is never the only copy of its content — HS-6YIS-5GSW-EPIC-artifacts-as-vault-documents. **Universal** — unlike the type-specific schemas below, it attaches to ANY entity kind (a task, an epic, a research doc, a memory doc, or a non-frontmatter file like a roadmap page — see the HTML variant below), because a published artifact can belong to a doc at any level, not one lifecycle stage.
392
+
393
+ ```yaml
394
+ artifact: { title: "…", url: "https://claude.ai/code/artifact/…", version: "…", updated: "2026-08-23" }
395
+ # title: required when artifact: is present — the artifact's display title (matches its published <title>/heading).
396
+ # url: required — the published URL.
397
+ # version: optional, string — the last-published version stamp this vault file was confirmed against. This is
398
+ # a LOCAL claim, not a live read: it is the freshness anchor for the push-first stale-guard
399
+ # (HS-6YIS-5GSW-EPIC part 2) — before publishing this file over the artifact, the publishing session
400
+ # reads the LIVE page and confirms it is not ahead of this stamp, THEN writes the new version back
401
+ # here after a successful publish. Never hand-edited to "catch up" without actually reading the live
402
+ # page first (the incident this field exists to prevent: [[a-published-artifact-and-its-vault-copy]]).
403
+ # updated: optional, ISO-8601 date — when this vault file was last published/stamped against the artifact.
404
+ ```
405
+
406
+ Either a single-line flow mapping (above) or an equivalent nested block mapping is valid YAML; the artifact-publish tooling (HS-6YIS-5GSW-EPIC-artifacts-as-vault-documents part 2, `scripts/hs-artifact.mjs`) reads and writes the flow-mapping form. Omit the whole `artifact:` key when the doc has no published artifact (omit-when-empty, same posture as `contributors`/`assisted_by`).
407
+
408
+ **Non-frontmatter vault files** (a raw HTML page under `specs/roadmap/`, for example, has no YAML frontmatter at all): the same four fields are carried as a single machine-readable `ARTIFACT-STAMP: {...}` JSON line inside the file's existing leading `<!-- -->` source-of-truth comment, read/written by the same tooling. This is the shape `specs/roadmap/heyspex-sprint-roadmap0829.html` uses.
409
+
410
+ > [!note] Schema addition self-gating
411
+ > Per entity-model-self-gates-schema-changes-via-adr, `artifact:` is admitted here **without a standalone ADR** under the same milestone-precedent as `contributors`/`assisted_by` above: additive, optional, omit-when-empty, not a new entity type or lifecycle — ratified by its introduction lineage, the owner's immediate order following the incident recorded in a-published-artifact-and-its-vault-copy → HS-6YIS-5GSW-EPIC-artifacts-as-vault-documents.
412
+
413
+ Status enum (universal):
414
+
415
+ - `draft` — under active authorship, not yet ratified.
416
+ - `open` — ratified and available to work against (for tasks, epics) or stable (for L1/L2/L3/ADR).
417
+ - `in_progress` — claimed and being executed (tasks only).
418
+ - `in_review` — implementation complete, awaiting human verification before closure (tasks only).
419
+ - `closed` — done (tasks) or superseded (ADRs).
420
+ - `deferred` — **deprecated** (2026-06-24 per parked-is-the-single-hold-status-deferre). Use `status: parked` for indefinite hold or `defer_until:` for dated snooze instead. Kept for read-back-compat only.
421
+
422
+ ### Type-specific schemas
423
+
424
+ #### `l1`
425
+
426
+ ```yaml
427
+ type: l1
428
+ aliases: # optional, list of alternative names
429
+ brand: # optional, mapping with canonical/variants/pronunciation/comma
430
+ domains: # optional, list of relevant domains
431
+ version: # optional, monotonic integer — bumped on every material edit to the record.
432
+ # The alignment-drift detector ([[HS-E3D3-RDFT-EPIC-alignment-drift-detector-into-resolution]])
433
+ # compares the version an epic was last aligned against (its `alignment_baseline`)
434
+ # to this current version; a bump past the baseline surfaces a `drift` resolution item.
435
+ last_edited_at: # optional, ISO-8601 timestamp of the last edit to the record. Timestamp FALLBACK for
436
+ # drift detection when an epic carries no per-layer `alignment_baseline`: the layer's
437
+ # `last_edited_at` newer than the epic's `alignment_reviewed_at` means drift.
438
+ ```
439
+
440
+ Only one `l1` exists per vault. The `version` / `last_edited_at` fields are the versioning prerequisites carried from HS-OR8V-0010-EPIC-north-star-vision-first-class-entity; they make the L1 record a drift baseline anchor for the alignment-drift detector.
441
+
442
+ #### `l2`
443
+
444
+ Universal fields only. No required L2-specific fields.
445
+
446
+ #### `l3` (use case)
447
+
448
+ ```yaml
449
+ type: l3
450
+ actors: # required, list of actor names from L1
451
+ trigger: # required, string — what initiates this use case
452
+ outcomes: # required, list — observable end states
453
+ depends_on: # optional, list of wikilinks to other use cases
454
+ ```
455
+
456
+ #### `adr`
457
+
458
+ ```yaml
459
+ type: adr
460
+ decision_date: # required, ISO-8601 date
461
+ deciders: # required, list of strings
462
+ supersedes: # optional, wikilink to ADR this replaces
463
+ superseded_by: # optional, wikilink to ADR replacing this
464
+ status: # adr-status enum: proposed | accepted | deprecated
465
+ # `superseded` is DEPRECATED 2026-06-03 per [[superseded-enum-values-deprecated-use-canonical-false-and-superseded-by]]
466
+ # New writes: set `canonical: false` + `superseded_by: '[[<successor>]]'` instead. Existing
467
+ # ADRs that already carry `status: superseded` stay as historical record per the
468
+ # immutability principle ([[closed-trackable-specs-are-historical-record-not-living-docs]]) —
469
+ # readers infer `canonical: false` from the legacy value.
470
+ canonical: # universal flag — true (or absent) = current/authoritative; false = historical record
471
+ # (current canonical is at `superseded_by:`). Flipped at supersession-time via
472
+ # [[supersession-protocol]]; frozen thereafter.
473
+ ```
474
+
475
+ The ADR-specific `status` enum overrides the universal status enum for ADRs. The `canonical:` field applies to ADRs the same way it does to every spec type.
476
+
477
+ #### `task`
478
+
479
+ ```yaml
480
+ type: task
481
+ kind: # one of: infra | foundation | feature | polish (scheduler tier — SSOT is
482
+ # SCHEDULER_KIND_VOCAB in console/src/lib/indexer/vault-rules.mjs). Legacy bd
483
+ # kinds (bug | spike | story | milestone) are retired; migrate via
484
+ # scripts/migrate-to-scheduler-metadata.mjs. Bugs now have their own lane
485
+ # (kind: bug-fix capture → HS-DIAG-* diagnose spec → Resolution surface).
486
+ assignee: # optional, string
487
+ started_at: # optional, ISO-8601 datetime — set on first transition to in_progress
488
+ closed_at: # optional, ISO-8601 datetime
489
+ close_reason: # optional, enum: shipped | deprecated | obsolete | wont_fix | duplicate
490
+ # shipped → "Done" archive view; deprecated* → "Deprecated" archive view
491
+ # `superseded` is DEPRECATED 2026-06-03 per [[superseded-enum-values-deprecated-use-canonical-false-and-superseded-by]]
492
+ # New writes that involve supersession: omit `close_reason` (or use another value
493
+ # like `deprecated`) and set `canonical: false` + `superseded_by:` instead.
494
+ # Existing closed specs with `close_reason: superseded` stay as historical record
495
+ # per immutability ([[closed-trackable-specs-are-historical-record-not-living-docs]]).
496
+ defer_until: # optional, ISO-8601 date — dated snooze evaluated by isDeferredNow(); scheduler-invisible
497
+ # until the date, then auto-returns with no status change ([[parked-is-the-single-hold-status-deferre]]).
498
+ # Distinct from `status: parked` (indefinite hold). Do NOT pair with deprecated `status: deferred`.
499
+ target: # optional, string — repo-relative path(s) of the file(s) an autonomous builder edits
500
+ # for this task (comma-separated for multi-file). DISPATCH METADATA for the dev-factory
501
+ # overnight loop: its backlog selector only dispatches an open, dependency-ready task
502
+ # that carries target:; without it the task is skipped with a warning. Hand-editing this
503
+ # field is allowed (it is not status). Owner-approved 2026-07-04 (HS-BMAW go-live);
504
+ # interim docs lived in dev-factory/README.md §Vault-task target: frontmatter.
505
+ test: # optional, string — the test file that gates the autonomous build of this task
506
+ # (scoped vitest run / self-test). When omitted the gate falls back to tsc --noEmit
507
+ # plus the module's own self-test conventions. Pairs with target: above.
508
+ suggested_model: # optional, string — breakdown-authored Ollama model id for local builder dispatch
509
+ # (e.g. qwen3-coder:30b). Honored as first candidate in scheduler/backlog-selector;
510
+ # falls back through routing-table build chain when unavailable.
511
+ suggested_machine:# optional, string — machine key from routing-table (specops-gpu0, basement-geek, …).
512
+ # Scheduler tries this machine first when reachable + capable.
513
+ recommended_num_ctx: # optional, integer — Ollama num_ctx bucket (16384 | 32768 | 65536 | 131072).
514
+ # pick-num-ctx.mjs honors when ≤ machine ceiling (spec-ops 65536, basement 131072).
515
+ estimated_context_tokens: # optional, integer — breakdown estimate of prompt tokens (target + deps + spec).
516
+ context_files: # optional, list of repo-relative paths — passed to builder.mjs as --extra (types, deps).
517
+ # Omit the target file (builder always reads it). Body duplicate: ## Builder brief.
518
+ fit_assessment: # optional, enum: safe | tight | overflow — breakdown confidence in context fit.
519
+ parallel_safe: # optional, boolean — false when task must not run concurrently (shared config/schema).
520
+ affects_paths: # optional, list of repo-relative paths — read/write set for stomp detection at dispatch.
521
+ parallel_group: # optional, string — wave id; tasks in same group may run together when paths disjoint.
522
+ builder_profile: # optional on child tasks only when inherited from epic; see builder-fleet-capability-reference.
523
+ canonical: # universal flag — true (or absent) = current/authoritative; false = historical record.
524
+ # Flipped to false during the in_review → closed transition for plan-side specs per
525
+ # [[session-close-protocol]] §Reviewer responsibilities step 4, or at supersession per
526
+ # [[supersession-protocol]]. `status: parked` keeps `canonical: true` (paused, not historical).
527
+ superseded_by: # optional, wikilink — pointer to the current canonical when canonical: false.
528
+ # Required when canonical: false AND there is a successor (per [[supersession-protocol]]).
529
+ # When close_reason was historically deprecated|duplicate, this field can still point
530
+ # at the successor; the legacy enum value stays as historical record.
531
+
532
+ # Dependency edges (see "Dependencies" section below)
533
+ blocks: # optional, list of wikilinks — this task blocks these
534
+ blocked_by: # optional, list of wikilinks — these tasks block this one
535
+ related: # optional, list of wikilinks — soft, navigational
536
+ parent: # optional, single wikilink — hierarchical parent
537
+ children: # optional, list of wikilinks — hierarchical children
538
+ discovered_from: # optional, single wikilink — provenance edge ("this task was born while
539
+ # working on X"). Also the code-audit **cleanup→task exit**: a task filed
540
+ # from a `research_type: code-audit` finding row (Action: task) sets this to
541
+ # the source `[[HS-RES-…]]` (see §research "Code-audit findings table" /
542
+ # [[HS-7D0I-WL88-TASK-code-audit-research-type-with-a-defined]]).
543
+
544
+ # Delegation / task-split lineage ([[HS-HXE2-YQ5A-EPIC-delegation-defer-work-from-one-agent-to]],
545
+ # [[HS-DTL1-CX5K-CAP-task-split-handoff-when-context-runs-low]], [[HS-PD6K-NCH5-TASK-handoff-envelope-schema-and-split-child]])
546
+ split_of: # optional, single wikilink — parent task or epic this spec continues (option B:
547
+ # random child id + frontmatter linkage). Must pair with split_index.
548
+ split_index: # optional, positive integer — 1-based sequence among siblings split from the
549
+ # same split_of parent. Required when split_of is set.
550
+ handoff_id: # optional, string — dedup key for idempotent defer re-posts
551
+ # ([[HS-PJN4-W146-TASK-defer-idempotency-handoff-id-and-cross-a]]).
552
+
553
+ # Ingest + roadmap admission ([[plan-ingest-review-roadmap-workflow]], [[adr-0012-roadmap-storage]])
554
+ ingest_draft: # optional, wikilink to draft this spec was promoted from
555
+ forked_from: # optional, wikilink — source spec when forked from sealed as-built diagram ([[HS-G8JS-F6P1-TASK-fork-spec-from-as-built-canvas]])
556
+ forked_from_canvas: # optional, string — vault-relative as-built canvas path
557
+ has_diagram: # optional, boolean — spec contains architecture mermaid; indexes Shipped gallery
558
+ source_intent_hash: # optional, copied from draft at promote time
559
+ roadmap_admission: # optional, enum: pending | scheduled | horizon | declined
560
+ roadmap_phase: # optional, string — phase id from a HS-ROADMAP-* manifest (e.g. M3)
561
+ # horizon backlog: set priority: 4 and/or tag horizon; roadmap_admission: horizon
562
+
563
+ # Optional structured content; preferred form is markdown sections in body
564
+ acceptance: # optional, list of acceptance criteria. Each entry is EITHER a plain string
565
+ # (prose criterion — stays fully valid) OR an object carrying a runnable check:
566
+ # - text: "<the criterion>"
567
+ # verification: # optional — how a machine proves this criterion
568
+ # kind: test | lint | types | command | guard | evidence
569
+ # run: "<command or test path>" # required for machine kinds
570
+ # expect: exit-0 | no-net-new # default exit-0
571
+ # `kind: evidence` names an evidence type a human/receipt records instead of a
572
+ # command. Prose-only criteria are receipt-marked "human-verified" honestly.
573
+ # Backward compatible by construction ([[HS-MNW5-IU2S-EPIC-executable-acceptance-criteria-and]]);
574
+ # kinds/expects mirror the checks-manifest contract ([[checks-manifest-authoring]],
575
+ # parser: spex-console/src/lib/cli/checks-manifest.ts).
576
+ skills: # optional, list of strings
577
+ context: # optional, list of strings
578
+ evidence: # optional, evidence array — sources proving acceptance met
579
+ # (see "Evidence linking" below)
580
+
581
+ # Council session output ([[adr-0015-council-as-cross-cutting-capability]])
582
+ deliberation_record: # optional; multi-LLM debate output, append-only `runs[]`. Permitted on task/epic/review
583
+ # in addition to draft. Schema lives under `draft` §`deliberation_record` above. Attaches
584
+ # on promoted-spec recheck (invocation #3) via `/hs-next` claim path; never gates work.
585
+
586
+ # Parallel-agent module hints ([[HS-ZMAQ-M15C-TASK-declare-owned-path-globs-on-agent-identi]])
587
+ affects_paths: # optional, list of repo-relative glob strings — coarse module hints for
588
+ # overlap detection when multiple agents work in parallel; advisory in v1
589
+ # (same posture as the advisory `lane:` label on agent identities).
590
+
591
+ # Migration provenance
592
+ legacy_bd_id: # optional, string — original bd id for migrated entries
593
+ ```
594
+
595
+ ##### Handoff envelope (body)
596
+
597
+ When an agent defers or splits remaining work into a child task, the **handoff envelope** carries the next agent's starting context. SSOT is the **body**, not frontmatter — a `## Handoff envelope` heading followed by a fenced ` ```yaml ` block matching `schemas/handoff-envelope.schema.json`:
598
+
599
+ ```yaml
600
+ reason: "Why the defer/split happened (context budget, blocked, lane hand-off)"
601
+ summary_done:
602
+ - "Work completed before the handoff"
603
+ remaining:
604
+ - "Actionable items for the picking-up agent"
605
+ success_criteria:
606
+ - "How the reviewer knows the deferred work is done"
607
+ ```
608
+
609
+ Field semantics align with the WIP ledger (`remaining` / verified-style `summary_done`) plus delegation-specific `reason` and `success_criteria` per HS-SUMD-4T35-RES-parallel-agent-coordination-handoff-and. A task with `split_of:` **must** include this section; `scripts/validate-vault-ids.mjs` enforces shape. The defer command (HS-MS3T-1U0Z-TASK-hs-defer-mcp-tool-and-slash-command) authors both the lineage frontmatter and this block.
610
+
611
+ #### `agent`
612
+
613
+ Durable agent identity (HS-XNX5-UQHA-MIL-agent-home). Evergreen — does not close. Compiles to `.claude/agents/<name>.md` and `.cursor/agents/<name>.md`; the vault spec is the SSOT.
614
+
615
+ ```yaml
616
+ type: agent
617
+ role: # required, string — human-readable role title (also used as description fallback on compile)
618
+ model: # optional, enum: opus | sonnet | haiku
619
+ description: # optional, string — host-native description on compile
620
+ goal: # optional, string — HeySpex-resident intent label (not compiled)
621
+ allowed_tools: # optional, list of strings — compiled to host `tools:` when present
622
+ disallowed_tools: # optional, list of strings — compiled to host `disallowedTools:` when present
623
+ procedures: # optional, list of wikilinks — HeySpex-resident procedure refs
624
+ handoffs: # optional, list — HeySpex-resident handoff targets
625
+ guardrails: # optional, list of strings — HeySpex-resident; persona body carries compiled guardrails
626
+ lane: # optional, string — human label for parallel work (advisory in v1)
627
+ isolation: # optional, enum: default | worktree — operator guidance for checkout
628
+ # isolation when parallel agents each need a dev server; advisory in v1
629
+ # ([[parallel-agent-isolation-guidance]]). `worktree` = use a dedicated git
630
+ # worktree before implementation. HeySpex-only — stripped on compile (like `lane`).
631
+ color: # optional, string — console roster accent (HeySpex-only)
632
+ owned_paths: # optional, list of repo-relative glob strings — machine-checkable file/module
633
+ # ownership for stomp-detection ([[HS-8PNX-Q1KC-EPIC-lanes-and-stomp-detection-for-parallel-a]]).
634
+ # Example: spex-console/src/lib/scheduler/**, vendor/heyspex-mcp/**. HeySpex-only — stripped on compile.
635
+ golden_cases: # optional, list of wikilinks to HS-GCASE-* fixtures owned by this agent.
636
+ # Reverse of each case's `agent:` pointer. HeySpex-only — stripped on compile
637
+ # ([[HS-8AQT-J6FU-TASK-golden-case-vault-artifact-format-and-st]]).
638
+ source_attribution: # optional object — import provenance for community/own-agent imports only
639
+ tags: # required for roster grouping — MUST include `agent` plus at least one
640
+ # `purpose/*` tag (see Purpose tags below). Plain frontmatter array;
641
+ # no agent-only grouping engine ([[HS-IAYA-5345-TASK-lock-agent-purpose-taxonomy-and-tag-conv]]).
642
+ ```
643
+
644
+ **Purpose tags** (HS-E2VS-LBUS-EPIC-roster-organization-and-discovery-for-a, HS-WDHW-0009-EPIC-tag-taxonomy-tag-browser-obsidian-style): agent identities reuse the vault's nested-tag namespaces — a dedicated **`purpose/`** namespace (seventh namespace alongside 0009's `area/`, `type/`, `surface/`, `theme/`, `dep/`, `stage/`) for roster discovery. Values are multi-select tags in `tags:`; grouping UI and `GET /api/agents` read them later (HS-NRND-UF9J-TASK-expose-agent-tags-in-get-api-agents-rost).
645
+
646
+ | Tag | When to use |
647
+ |-----|-------------|
648
+ | `purpose/reviewer` | Verify acceptance criteria, review diffs, read-only audit before close-out |
649
+ | `purpose/researcher` | Synthesize cited research into durable `HS-RES-*` specs |
650
+ | `purpose/planner` | Ground plans/epics, stress-test assumptions at the plan→execute boundary |
651
+ | `purpose/triager` | Intake routing — captures, resolution items, smallest-fix triage |
652
+ | `purpose/implementer` | Build, edit, deploy — agents that modify code or infra |
653
+ | `purpose/writer` | Prose/docs authoring (reserved; no starter roster member yet) |
654
+
655
+ Rules:
656
+
657
+ 1. **Required:** every shipped `type: agent` spec carries **at least one** `purpose/*` tag in `tags:` (in addition to the `agent` marker).
658
+ 2. **Primary purpose:** the **first** `purpose/*` tag in `tags:` array order is the roster section key. Downstream surfaces expose it as `primaryPurpose` (computed server-side — not a separate frontmatter field). When an agent legitimately spans multiple purposes, list the primary group first and secondary `purpose/*` tags after.
659
+ 3. **`lane:` is not a substitute:** `lane` is parallel-work coordination (advisory, HeySpex-only, stripped on compile). `purpose/*` is vault-resident discovery metadata that flows to the API. They often align but are independent fields.
660
+
661
+ #### `golden-case`
662
+
663
+ Saved eval fixture for the agent testing harness (HS-R1JM-OAG3-EPIC-agent-testing-harness-try-a-draft-agent). Evergreen — updated in place as the agent evolves; does not close. One golden case exercises exactly one `type: agent` identity. The eval spawn (HS-YNIE-ZDQ7-TASK-transient-eval-spawn-and-trajectory-capt) reads the body; the verdict engine (HS-ZOHQ-MOOE-TASK-verdict-engine-deterministic-graders-plu) consumes optional deterministic graders. Expected trajectories (regression baselines) are **run artifacts**, not stored in the fixture at v1 — they land in run history later (HS-ZR0Z-TS11-TASK-run-history-storage).
664
+
665
+ ```yaml
666
+ type: golden-case
667
+ agent: # required, wikilink to HS-AGENT-* — the identity under test
668
+ tags: # should include golden-case, agents, testing
669
+ related: # optional wikilinks — e.g. the task spec used as setup context
670
+ ```
671
+
672
+ **Bidirectional linkage:** each golden case carries `agent:`; the agent MAY carry `golden_cases:` (list of wikilinks). Filing via `createSpec` / `heyspex_file_spec` with `frontmatter.agent` auto-appends to the agent's `golden_cases:` array (same pattern as task→epic `children:`).
673
+
674
+ **Required body sections:**
675
+
676
+ | Section | Required | Purpose |
677
+ | --- | --- | --- |
678
+ | `## Prompt` | yes | The user message the eval spawn sends to the draft agent (≥12 characters). |
679
+ | `## Setup` | no | Freeform context — wikilinks to task specs, files in scope, env hints. |
680
+ | `## Deterministic graders` | no | Fenced `yaml` block matching `specs/schemas/golden-case-body.schema.json`. Keys the reproducible pass/fail signal; LLM-judge critique remains advisory per OAG3 AC#4. |
681
+
682
+ **On-disk layout:** `specs/agents/golden-cases/HS-GCASE-<suffix>-<slug>.md` — nested under `agents/` so fixtures travel in the same git subtree as agent identities.
683
+
684
+ #### `epic`
685
+
686
+ Same as `task` schema (including optional `affects_paths:`). The `type: epic` is what differentiates. An epic typically has many `children:` and few `blocks:`/`blocked_by:`.
687
+
688
+ **Planning flow requirements** (planning-flow-research-epic-lifecycle): every epic MUST include `research:` and `planning_flow:` wikilinks. Console Epic Studio and `POST /api/specs` enforce this for newly filed epics.
689
+
690
+ ```yaml
691
+ research: # required for new epics — wikilink to HS-RES-*
692
+ planning_flow: # required for new epics — wikilink to HS-FLOW-*
693
+ milestone: # REQUIRED — single wikilink to the parent HS-MIL-* this epic belongs to.
694
+ # The inverse of the milestone's `epics:` array (write both sides;
695
+ # `scripts/validate-vault-ids.mjs` rejects one-sided drift).
696
+ # Grace window: missing `milestone:` is a validator warning until
697
+ # 2026-06-26, then escalates to an error
698
+ # ([[HS-V5UA-T5PX-TASK-required-milestone-parent-on-epic-validator-and-grace-window]],
699
+ # part of [[HS-OFR5-M2J3-EPIC-introduce-milestones-as-highest-entity]]).
700
+ # Pre-rule orphan epics live at
701
+ # [[HS-3DJS-ORPH-MIL-uncategorized-pre-2026-05-26-epics]]
702
+ # pending walk-the-floor reassignment.
703
+ derived_from: # optional, single wikilink to HS-RES-* — the research that spawned this epic.
704
+ # Reverse of `research.spawned_epics:` (1:N). See §Provenance edges.
705
+ ```
706
+
707
+ **Alignment fields** (drift-detection prerequisites; HS-E3D3-RDFT-EPIC-alignment-drift-detector-into-resolution, owned by HS-KA1E-0012-EPIC-creation-guardrails-agent-pushback for the vault-wide backfill):
708
+
709
+ ```yaml
710
+ alignment_rationale: # optional (backfill in progress), string — a wikilink to the Pillar / L1 principle
711
+ # this epic advances PLUS a one-line "why", e.g.
712
+ # "[[HS-PILR-architecture]] — advances the foundations concern by converging detection paths".
713
+ # Distinct from the membership `pillar:` field: `pillar:` categorizes; `alignment_rationale`
714
+ # carries the teleological "why" the drift detector reasons over. The wikilink target
715
+ # SHOULD match the epic's `pillar:`. An `alignment_rationale` that resolves to no layer
716
+ # record surfaces as an `orphan` resolution item.
717
+ alignment_baseline: # optional, mapping { <layer-id>: <int version>, anti_goals?: <fingerprint> } — the layer
718
+ # versions this epic was last aligned against. PRIMARY drift baseline: when a referenced
719
+ # layer's current `version` exceeds the baseline here, a `drift` item is emitted. The
720
+ # `anti_goals` key stamps the L1 anti-goal fingerprint so a changed anti-goal set re-checks
721
+ # every active + recently-closed epic. Absent → the detector falls back to
722
+ # `alignment_reviewed_at` vs the layer's `last_edited_at`; absent both → no drift (never a
723
+ # false positive). Stamped by the alignment checkpoint on re-alignment.
724
+ ```
725
+
726
+ #### `milestone`
727
+
728
+ The grouping **above** epic: a coherent multi-epic ship (a restructure or release) that closes when every child epic closes and the milestone's own acceptance gate is met. Formalized by HS-VK7C-MILE-EPIC-milestone-entity-type-and-surfacing; the canonical hierarchy and filing rules live in [[spec-hierarchy]]. The 33 `HS-MIL-*` files on disk (seed: HS-RM22-IART-MIL-console-ia-restructure) predate this schema row and are described by it retroactively.
729
+
730
+ ```yaml
731
+ type: milestone
732
+ status: # one of: open | in_progress | closed (subset of the universal enum)
733
+ epics: # list of wikilinks to every child epic — the mirror of each child epic's
734
+ # `milestone:` pointer. Write both sides (bidirectional linkage; see [[spec-hierarchy]]).
735
+ informed_by: # optional, list of wikilinks to HS-RES-* — research docs that enhance this milestone.
736
+ # Reverse of `research.enhances:` (each research enhances exactly one milestone, M:1;
737
+ # one milestone may be informed by many research docs over time). Bidirectional
738
+ # consistency enforced by `scripts/validate-vault-ids.mjs`. See §Provenance edges.
739
+ target_date: # optional, ISO-8601 date or empty string "" — the intended ship date
740
+ tags: # optional, list of strings
741
+ related: # optional, list of wikilinks
742
+ ```
743
+
744
+ **No scalar `priority:` field.** Milestone ordering is the **output** of the deterministic priority scheduler, never a stored scalar (deterministic-priority-scheduler-supersedes-scored-priority, HS-78E7-Y9OJ-EPIC-deterministic-priority-scheduler). This deliberately diverges from the universal "`priority:` required" rule above — the same way current epics and tasks omit the scalar. Do not add a `priority:` to a milestone.
745
+
746
+ Sub-milestones are **out of scope**: large milestones are split horizontally (more milestones at the same level), not nested ([[spec-hierarchy]] §Open questions). There is no `milestone:` (parent-milestone) field on a milestone.
747
+
748
+ Required body sections ([[spec-hierarchy]] §Frontmatter requirements per level): rationale ("why this is a milestone, not loose epics"), an acceptance gate (numbered closure conditions), suggested landing order, and out-of-scope.
749
+
750
+ #### `diagnose`
751
+
752
+ A user-reported **bug fix** as first-class executable work — claimable and decomposable like a task, but typed so it reads as "a reported bug to fix," not planned roadmap work. Introduced by HS-OXBS-BFIX-MIL-bug-fix-diagnose-lane (milestone-precedent, no standalone ADR). Standalone by default; an optional `parent:` (epic OR milestone) groups related fixes. Surfaces on the Resolution surface as a derived `kind: "bug"` row (the row is derived; this spec is the work — see blockers-are-gating-subset-of-resolution and HS-AO2F-1KZK-RES-bug-fix-resolution-task-entity-model-and).
753
+
754
+ ```yaml
755
+ type: diagnose
756
+ status: # universal enum: open | in_progress | in_review | closed | …
757
+ severity: # REQUIRED — one of: error | warning | info. Maps onto the scheduler's
758
+ # existing priority signals (channel B): a higher-severity diagnose
759
+ # competes higher for on-deck. Drives the derived bug row's color.
760
+ parent: # OPTIONAL — single wikilink to an HS-EPIC-* or HS-MIL-* parent.
761
+ # Standalone (absent) is the DEFAULT and is exempt from the
762
+ # epic→milestone mandatory-parent rule. One-way pointer (no auto back-link).
763
+ depends_on: # optional, list of wikilinks — tasks/specs that must close first.
764
+ blocks: # optional, list of wikilinks to HS-EPIC-*/HS-TASK-* this fix gates.
765
+ # Surfaces as a gating item in that spec's On-Deck overlay (channel A).
766
+ promoted_from: # optional, single wikilink to HS-CAP-* — set when this diagnose was promoted
767
+ # from a bug-report capture via `heyspex_promote_capture_to_diagnose`.
768
+ # Reverse of `capture.promoted_to:` (first-promotion).
769
+ derived_from: # optional, single wikilink to HS-RES-* — the research that surfaced this bug,
770
+ # set when this diagnose was promoted from a research investigation via
771
+ # `heyspex_promote_research_to_diagnose` (mirrors `epic.derived_from`).
772
+ # Reverse of `research.produces_diagnose:` (1:N).
773
+ tags: # optional, list of strings
774
+ related: # optional, list of wikilinks
775
+ ```
776
+
777
+ **No scalar `priority:`** — like task/epic/milestone, ordering is the scheduler's output; `severity:` is the diagnose's contribution to that ranking, not a stored priority.
778
+
779
+ Required body sections: Symptom, Reproduction, Root cause, Fix, Acceptance criteria.
780
+
781
+ #### `pillar`
782
+
783
+ Perpetual categorical container that never closes — the cross-cutting axis to which every epic associates via a primary `pillar:` field. Pillars are the structural primitive that makes HeySpex's "preserve context across time" claim true rather than aspirational ("Linear and Jira are queues; HeySpex is context" — see heyspex-market-positioning). Introduced 2026-05-31 per HS-AWDI-PILR-RES-pm-perpetual-container-terminology; ratified by introduction lineage HS-EBIL-0011-EPIC-goal-hierarchy-l2-strategic-pillars-l3 (milestone precedent, no standalone ADR for the entity itself). The meta-Pillar — the Pillar about Pillars themselves, dogfooding instance — is HS-PILR-pillars.
784
+
785
+ ```yaml
786
+ type: pillar
787
+ status: # `perpetual` — non-terminal. Pillars never transition to closed/in_review/etc.
788
+ # Universal status enum does NOT apply.
789
+ canonical: # required, boolean
790
+ # true → curated by HeySpex team; ships pre-loaded in customer vaults; read-only (fork to customize)
791
+ # false → customer-scoped Pillar (filed by the customer with high friction)
792
+ library_tier: # optional enum: meta | foundational | discretionary (placeholder; library taxonomy TBD)
793
+ secondary_pillars: # always present; usually empty []. Reserved for future cross-Pillar linking.
794
+ version: # optional, monotonic integer — same semantics as the `l1` `version` field above.
795
+ # A Pillar edit that materially changes scope bumps this; epics aligned against the
796
+ # prior version surface as `drift` items ([[HS-E3D3-RDFT-EPIC-alignment-drift-detector-into-resolution]]).
797
+ last_edited_at: # optional, ISO-8601 timestamp of the last edit — drift timestamp fallback (see `l1`).
798
+ related: # optional, list of wikilinks
799
+ tags: # optional, list of strings
800
+ ```
801
+
802
+ The `version` / `last_edited_at` fields are the versioning prerequisites carried from HS-EBIL-0011-EPIC-goal-hierarchy-l2-strategic-pillars-l3; together with the L1 fields they let the alignment-drift detector tell a material Pillar revision apart from a no-op touch.
803
+
804
+ **Per-epic membership** (new fields on every epic, added 2026-05-31 by HS-EBIL-0011-EPIC-goal-hierarchy-l2-strategic-pillars-l3):
805
+
806
+ ```yaml
807
+ pillar: # required on every epic, single wikilink to a Pillar.
808
+ # The home for library/learning. Forces "what perpetual concern does this advance?"
809
+ # at filing time. HARD-BLOCKED at the filing boundary (owner decision 2026-06-18):
810
+ # POST /api/specs / heyspex_file_spec refuse an epic with no resolvable HS-PILR pillar
811
+ # (400 pillar_required). Legacy pillar-less epics are caught by the EPIC_MISSING_PILLAR
812
+ # vault-rule — warning (Resolution backfill queue) until 2026-07-31, then error.
813
+ # Full rules: [[pillar-assignment-protocol]].
814
+ secondary_pillars: # optional, list of wikilinks to Pillars.
815
+ # For genuinely cross-cutting work ("Add caching layer" is primarily Scaling but also Reliability).
816
+ # Tasks inherit their epic's primary pillar; do not set secondary on tasks.
817
+ ```
818
+
819
+ **Authored as the work surfaces themes.** Pillars are created over time, not enumerated up front — there are deliberately only a few today and many more are expected. When filing an epic surfaces a theme with no pillar yet, the agent is prompted to author one (`heyspex_file_spec({ type: "pillar", name })`); the owner may also author a pillar standalone whenever a theme is foreseen. The procedure is pillar-assignment-protocol.
820
+
821
+ **Milestones surface a derived pillar roll-up.** A milestone has **no authored `pillar:` field** — it is a dated ship that groups epics, which may belong to different pillars. Instead a milestone *surfaces* the de-duped set of pillars its child epics carry (computed in `getSpec`, rendered on the milestone detail drawer). The roll-up is derived and may list multiple pillars, so it never drifts from the epics' `pillar:` fields.
822
+
823
+ **Status semantics.** Pillars carry `status: perpetual` as a single non-terminal value, distinct from the universal enum. The close-out protocol skips Pillars; the scheduler skips Pillars as on-deck candidates (only their epics are scheduled); review-doc flow does not apply. A Pillar's body grows over time as epics close and lessons accumulate against the concern — accumulation IS the point.
824
+
825
+ **Body sections.** Required: a one-line "why this Pillar exists" hook; `## In scope` / `## Out of scope`; `## Anticipated epics`; `## Library status` (canonical | customer-scoped). The body is a living document, not a frozen artifact.
826
+
827
+ **Disambiguation — three distinct uses of "pillar" in this vault:**
828
+
829
+ 1. **`type: pillar` entities (`HS-PILR-*`)** — this section. Perpetual product concerns (Scaling, Reliability, DX, etc.). Queryable, schematized.
830
+ 2. **HS-DOC-architecture-foundations** (renamed 2026-05-31 from `architecture-pillars`) — L2 doc that names the five technical foundations of the system (vault, indexer, console, CLI, sync mesh) and refers to them as "Pillars 0–5" in internal section headers. Unrelated to (1); the section names are preserved for now and may be renamed in a future cosmetic pass.
831
+ 3. **HS-OR8V-0010-EPIC-north-star-vision-first-class-entity's FIVE COMPONENTS section** — five implementation parts of that epic. Previously named "FIVE PILLARS"; renamed 2026-05-31 to remove the overload.
832
+
833
+ Only sense (1) is a queryable entity. (2) and (3) use the word as a non-schematized noun within their own documents.
834
+
835
+ #### `branch-brief`
836
+
837
+ A durable "plan receipt" for one git branch. Ratified by adr-0008-branch-brief-and-evidence-schema; closes the Continuity Layer schema gaps (HS-7KOO-0022-EPIC-continuity-layer-gaps). One brief per branch; re-generation overwrites the file in place (git history is the version trail). Generated by HS-838Y-ALDY-TASK-branch-brief-generation.
838
+
839
+ ```yaml
840
+ type: branch-brief
841
+ branch: # required, string — the raw (unsanitized) git branch name
842
+ repo: # optional, string — repo name if multi-repo
843
+ goal: # required, string — what this branch is trying to accomplish
844
+ implements: # optional, list of wikilinks — tasks/epics this branch implements
845
+ plan: # optional, list of { step: int, description: string }
846
+ # the ORIGINAL intent. Sourced (priority order) from:
847
+ # 1. the linked implementing spec's acceptance criteria
848
+ # (its `acceptance:` array, else its `## Acceptance` body)
849
+ # 2. a manual plan authored here (exploratory branches)
850
+ # Commit messages / PR bodies are NOT v1 plan sources.
851
+ actual: # optional, list of { step: int, description: string,
852
+ # status: <drift enum>, evidence: [<evidence ref>] }
853
+ # what was implemented, captured at brief-generation time.
854
+ risks: # optional, list of strings — identified risks/concerns
855
+ open_questions: # optional, list of strings — unresolved questions
856
+ next_steps: # optional, list of strings — recommended next actions
857
+ decisions: # optional, list of wikilinks — memory-decisions made on this branch
858
+ evidence: # optional, evidence array (see "Evidence linking" below)
859
+ ```
860
+
861
+ **Drift enum** (the `status:` of each `actual` step) — defined once here, reused by HS-838Y-ALDY-TASK-branch-brief-generation and any drift tooling (HS-CM22-0019-EPIC-post-edit-alignment-drift-detection):
862
+
863
+ | Value | Meaning |
864
+ | --- | --- |
865
+ | `match` | Implementation achieves the planned step. |
866
+ | `drift` | Implementation differs from the plan in a material way. |
867
+ | `missing` | Planned step not implemented. |
868
+ | `extra` | Implemented change that was not in the plan. |
869
+
870
+ Required body sections: `## Goal`, `## Plan vs Actual` (a table rendering `plan`/`actual` with an `Evidence` column), and — when any step is `drift`/`missing`/`extra` — a `## Drift Analysis` section explaining each, with risk and a recommendation. Optional: `## Decisions`, `## Risks`, `## Open Questions`, `## Next Steps`, `## Evidence`. The frontmatter `plan`/`actual` arrays are canonical for queries; the body table is the human view (same canonical-frontmatter rule as dependencies and tags).
871
+
872
+ #### `memory-lesson`
873
+
874
+ ```yaml
875
+ type: memory-lesson
876
+ topic: # required, short string — the subject
877
+ observed: # required, ISO-8601 date — when the lesson was learned
878
+ related: # optional, list of wikilinks
879
+ evidence: # optional, evidence array — sources grounding the problem/fix
880
+ # (see "Evidence linking" below)
881
+ ```
882
+
883
+ The body of a memory-lesson spec has two required sections: `## Problem` and `## Fix`.
884
+
885
+ #### `memory-decision`
886
+
887
+ ```yaml
888
+ type: memory-decision
889
+ topic: # required, short string
890
+ decided: # required, ISO-8601 date
891
+ deciders: # required, list of strings
892
+ supersedes: # optional, wikilink to a prior decision
893
+ evidence: # optional, evidence array — sources grounding the rationale
894
+ # (see "Evidence linking" below)
895
+ ```
896
+
897
+ The body has two required sections: `## Decision` and `## Rationale`. (An ADR is a heavier-weight decision; a `memory-decision` is the day-to-day choice that doesn't warrant an ADR but should still be persisted.)
898
+
899
+ #### `memory-reference`
900
+
901
+ ```yaml
902
+ type: memory-reference
903
+ topic: # required, short string
904
+ url: # optional, URL the reference points at
905
+ evidence: # optional, evidence array — additional grounded sources
906
+ # (see "Evidence linking" below)
907
+ ```
908
+
909
+ Body is the brief context for why this reference matters.
910
+
911
+ #### `memory-ui-pattern`
912
+
913
+ ```yaml
914
+ type: memory-ui-pattern
915
+ topic: # required, short string — the pattern name/slug
916
+ documented: # required, ISO-8601 date — when the pattern was captured
917
+ related: # optional, list of wikilinks
918
+ evidence: # optional, evidence array
919
+ ```
920
+
921
+ Reusable **console interaction and visual patterns** (keyboard ownership, two-layer highlights, drill-in between panes). Distinct from `memory-ux-principle` (cross-brand rules) and `memory-decision` (a one-time choice). Body sections: `## Pattern`, `## When to apply`, `## Rules`, `## Implementation` (file map + copy-paste shape). See rail-active-vs-keyboard-cursor.
922
+
923
+ #### `memory-ux-principle`
924
+
925
+ ```yaml
926
+ type: memory-ux-principle
927
+ topic: # required, short string — the principle name/slug
928
+ documented: # required, ISO-8601 date — when the principle was captured
929
+ related: # optional, list of wikilinks
930
+ evidence: # optional, evidence array
931
+ ```
932
+
933
+ Cross-brand **interaction rules** (Apple HIG, Material, Linear, Nielsen) — product-agnostic **what/why**. Distinct from `memory-ui-pattern` (HeySpex **how**). Body sections: `## Principle`, `## When to apply`, `## Rules`, `## Related UI patterns`, `## Sources`. See toggle-current-state-semantics and ux-principles-vs-ui-patterns.
934
+
935
+ #### `protocol`
936
+
937
+ Part of the **Memory** layer (memory-protocols-subsection-of-memory). Epics/tasks that *implement* or *revise* a protocol stay in `epics/` / `tasks/`; only the finished operational doc is `type: protocol`.
938
+
939
+ ```yaml
940
+ type: protocol
941
+ scope: # required, one of: session | project | vault | tool
942
+ version: # optional, semver-ish version of the protocol
943
+ ```
944
+
945
+ Body describes the protocol with numbered steps.
946
+
947
+ #### `review`
948
+
949
+ Companion fill-in workspace for a parent spec at `status: in_review`. One review doc per parent spec; created by the agent at the moment of flipping the parent to `in_review`. The reviewer (human) walks acceptance criteria, reads the agent's session observations, scores memory candidates, and records pass/reopen. Naming: `HS-REV-<suffix>-<slug>.md` where `<suffix>` matches the parent's id 1:1 (`HS-76RX` → `HS-SK1L-REV-…`, `HS-KQ5O` → `HS-IDFV-REV-…`).
950
+
951
+ ```yaml
952
+ type: review
953
+ parent: # required, single wikilink — the spec under review
954
+ reviews: # optional, single wikilink — same as parent (legacy alias retained for back-compat reads)
955
+ # parent: is the canonical edge; reviews: is kept for older docs
956
+ deliberation_record: # optional; Council session output for invocation #4 (review close-out)
957
+ # ([[adr-0015-council-as-cross-cutting-capability]]). Schema lives under `draft`
958
+ # §`deliberation_record` above. Attached when the human triggers council during
959
+ # `/hs-review` close-out; never gates the human's pass/reopen decision.
960
+ ```
961
+
962
+ Body has four required sections — the unified workspace:
963
+
964
+ - `## Acceptance verification` — checkboxes mirroring the parent spec's acceptance criteria; reviewer ticks them during verification and adds inline deviation notes. For epic-level reviews, expand into a manual testing walkthrough (fast-fail commands, click-by-click steps, expected behavior the reviewer can follow top-to-bottom).
965
+ - `## Reviewer notes` — free-form workspace. **The agent writes implementation observations here during the work session** (architecture choices, judgment calls, surprises, gotchas). The reviewer adds deviation/surprise notes during the testing pass. This prose is the raw material the close-out memory-extraction pass reads.
966
+ - `## Memory candidates` — table of proposed lessons / decisions / UI patterns / references derived from the Reviewer notes. Each row scored against the four-axis rubric (non-derivability / reusability / surprise-cost / concreteness, 0–3 each, threshold ≥ 7/12) and **MUST include a "Conflict check" column** noting `New`, `Refines [[existing-memory]]`, or `Conflict [[existing-memory]]`. A `Conflict` row is a red flag and must be resolved before accepting. Owned by HS-35JN-REVD-TASK-review-doc-fill-in-template-and-memory-extraction.
967
+ - `## Decision: close or reopen` — explicit pass/fail outcome with reasoning, plus a checkbox indicating whether to materialize accepted memory candidates.
968
+
969
+ Cross-link from the parent spec: every parent spec at `in_review` MUST add a `## Review` section pointing to its companion: `Reviewer workspace for this spec lives at [[HS-REV-<suffix>-<slug>]] — pre-review checklist, manual verification steps, reviewer notes, and memory candidates extracted at close-out.`
970
+
971
+ The console shows a "Review" button on cards only when the parent spec is `status: in_review` and an `HS-REV-<suffix>-*` companion exists (detection: `console/src/lib/vault.ts:155 extractSpecSuffix`). Once the parent moves to `closed`, the button disappears but the wikilink in the parent body remains.
972
+
973
+ The four-section unified-workspace template and the close-out flow are specified in HS-35JN-REVD-TASK-review-doc-fill-in-template-and-memory-extraction and CLAUDE.md §Session Completion. The MCP tool that loads a review doc into agent context (`heyspex_review_on_deck` + `/hs-review`) is specified in HS-XRO1-RVDC-TASK-heyspex-review-on-deck-mcp-and-hs-review. The legacy `{basename}.review.md` sibling naming was migrated by `scripts/migrate-review-doc-naming.mjs` — do not use it for new docs.
974
+
975
+ #### `business-positioning` / `business-pricing` / `business-marketing` / `business-model`
976
+
977
+ Parallel layer to Memory for **business-side** knowledge: how the product is positioned, priced, marketed, and the business model itself. Like Memory, browsed in the console on its own surface (**Business**) with category filters.
978
+
979
+ ```yaml
980
+ type: business-positioning # or business-pricing | business-marketing | business-model
981
+ topic: # required, short string — the subject (e.g. "homepage-h1", "tier-table")
982
+ status: # universal status enum — draft | open | in_progress | in_review | closed | deferred
983
+ # `active` is allowed as a synonym for `open` on migrated docs.
984
+ related: # optional, list of wikilinks
985
+ evidence: # optional, evidence array — sources grounding the claim/decision
986
+ ```
987
+
988
+ Body is free-form prose; the recommended shape is a one-line H1 frame, then sections that match the doc's purpose (e.g. **Decision** / **Why each word is load-bearing** for positioning; **Tier table** / **AI add-on** / **What this also unlocks** for pricing).
989
+
990
+ Distinct from `memory-decision` — memory captures *how-we-work* decisions for agents/humans inside the project; business docs capture *what-we-sell-and-how* decisions for the outside world. A pricing change is a `business-pricing` doc; a "don't mock the database in tests" rule is a `memory-decision`.
991
+
992
+ #### `capture`
993
+
994
+ Concise core-intent capture **before** research (capture-guardrails). Replaces the legacy `type: idea` entity (renamed 2026-05-22 per HS-B2B8-DLQI-EPIC-capture-rename-migration-idea-to-capture, source rationale HS-ZKFN-1FXG-CAP-capture-as-universal-companion-verb-and-parent-term). Not a draft inbox item and not a research doc.
995
+
996
+ ```yaml
997
+ type: capture
998
+ kind: # required, one of: idea | bug-fix | clip | reference | question | spec-import | feedback
999
+ # idea → speculative concept worth exploring (default — migrated entries land here)
1000
+ # bug-fix → defect observation paired with proposed fix
1001
+ # clip → web/document excerpt saved verbatim for later use
1002
+ # reference → a pointer to an external resource worth keeping
1003
+ # question → an open question to revisit when more context lands
1004
+ # spec-import→ an imported foreign doc (README / top-level project doc) landed as a RAW
1005
+ # capture by the bulk spec-import feature ([[HS-8V8T-8SBG-EPIC-first-connect-rough-import]]);
1006
+ # source doc in ## Source Material; carries source_repo/source_path for
1007
+ # (repo, vault) idempotency; stays RAW for manual triage. The
1008
+ # "pin → run feature pipeline" exit ([[HS-J7T5-VEW8-RES-import-specs-one-click-feature-pipeline]])
1009
+ # is a deferred follow-on — NOT wired in this wave.
1010
+ # feedback → app feedback / bug report / product idea / support question captured through
1011
+ # a deterministic (no-AI) intake channel ([[HS-KQEW-I5DV-EPIC-feedback-capture-type-deterministic]]);
1012
+ # classified by `routeFeedback()` ([[HS-FUII-EI9R-TASK-deterministic-feedback-router-module-no]]) toward diagnose/research/
1013
+ # answered, or stays `feedback` when unmatched. Optional structured fields below
1014
+ # (declared alongside `source:` further down this block — see `example`/
1015
+ # `user_story`/`situation`).
1016
+ example: # optional, string — a concrete example/repro illustrating the feedback (e.g. steps to reproduce a bug).
1017
+ # Only ever populated on `kind: feedback` captures; never forced, never required by the schema.
1018
+ user_story: # optional, string — "as a user, I want to… so that…" framing for a feedback-sourced product idea.
1019
+ # Same optionality as `example`.
1020
+ situation: # optional, string — the situational context the user was in when the feedback arose.
1021
+ # Same optionality as `example`.
1022
+ capture_status: # required, one of: raw | expanded — default raw
1023
+ # raw → no Core Idea content yet (single textarea / quick capture)
1024
+ # expanded → Core Idea / Concept / Details has been written (modal v3 or agent rewrite)
1025
+ # Flips raw → expanded automatically when an agent writes to core_idea,
1026
+ # concept, or agent_expansion via executeUpdateCapture (vendor/heyspex-mcp).
1027
+ # Console save-time linter force-corrects raw → expanded when `# Core Idea`
1028
+ # body has content. See [[HS-BDDT-Q1B7-EPIC-idea-capture-modal-v3-single-textbox-cle]].
1029
+ promoted_to: # binary: null (not promoted) or "[[HS-RES-<suffix>-<slug>]]" (promoted to research).
1030
+ # Records the FIRST research doc this capture was promoted into; not the full set.
1031
+ # For the M:N "every research doc this capture informs" view, see `informs_research:`.
1032
+ informs_research: # optional, list of wikilinks to HS-RES-* — every research doc this capture informs (M:N).
1033
+ # Reverse of `research.sources_captures:`. Distinct from `promoted_to:` (binary
1034
+ # first-promotion pointer); a capture may appear in multiple research docs over time.
1035
+ # Bidirectional consistency enforced by `scripts/validate-vault-ids.mjs`. See §Provenance edges.
1036
+ source: # optional capture channel (console, cli, companion, agent, …). On `kind: feedback` captures this
1037
+ # also doubles as the free-text originating-channel/page-context signal `routeFeedback()`
1038
+ # reads ([[HS-FUII-EI9R-TASK-deterministic-feedback-router-module-no]]) — still optional, never forced.
1039
+ related: # optional wikilinks
1040
+ legacy_idea_id: # optional, string — original HS-IDEA-* id for migrated entries (archaeology)
1041
+ ```
1042
+
1043
+ `promoted_to` is the **single binary promotion field** (HS-KQ5O-IDFV-EPIC-unified-idea-workflow-v2 / HS-QOAC-IDFL-RES-unified-idea-workflow-v2). `research_stage` was removed 2026-05-22; intermediate "promoted-but-research-not-written-yet" state lives in `.heyspex/research-focus.yaml`, not in the spec.
1044
+
1045
+ `kind:` is the taxonomy field locked in by HS-B2B8-DLQI-EPIC-capture-rename-migration-idea-to-capture Milestone A. Migration of pre-2026-05-22 `type: idea` entries set `kind: idea` for every record; new captures pick the kind at creation time. The enum was extended from 5 to 6 values on 2026-07-10 — `spec-import` added as the **import-origin** kind for the bulk spec-import feature (HS-8V8T-8SBG-EPIC-first-connect-rough-import / HS-ROIU-9P4B-EPIC-import-preview-and-confirm, grounding HS-J7T5-VEW8-RES-import-specs-one-click-feature-pipeline). `spec-import` was added as a documentation/protocol convention only, not to the hard validator enum — `scripts/validate-vault-schema` (`schemas/heyspex-frontmatter.schema.json`) only required that a capture carry a `kind:` and a `capture_status:`, so `spec-import` slipped past AJV. `feedback` (7th value, HS-KQEW-I5DV-EPIC-feedback-capture-type-deterministic / HS-PUK1-48ZZ-TASK-add-feedback-capture-kind-optional) was added to BOTH the doc above and the hard AJV `kind` enum in `schemas/heyspex-frontmatter.schema.json` — a `kind: feedback` capture passes `scripts/validate-vault-schema.mjs` the same way the original five values do. Further consolidation (getting `spec-import` onto the hard enum too) is still deferred.
1046
+
1047
+ Body **must** use four named sections — `# Core Idea` / `## Notes` (verbatim human words inside `"quotes"`) / `## Concept` (AI brief summary, ≤1200 chars hard / ≤800 soft) / `<details>Details</details>` (AI synthesis / deep dive, collapsed by default). Core Idea ≤280 chars soft / ≤500 hard. (The body section names retain the legacy "Idea" / "Notes" / "Concept" / "Details" wording even for captures whose `kind:` is not `idea`; the headings are a fixed scaffold, not a taxonomy field — agents and humans use the same four-section shape for every kind.) Legacy `# Core idea` / `## Notes` / `<details>Expanded idea (agent)</details>` is read back-compat during the deprecation window; writes always emit v2. AI may write to any section but: (a) never alters words inside a quote, (b) `## Notes` format-wrap is confirm-free, (c) `# Core Idea` / `## Concept` / `## Details` writes require user confirm. Agents must not add research sections (Executive Summary, Findings, Sources) to capture files — only the separate research doc after `/hs-res` (capture-guardrails).
1048
+
1049
+ #### `research`
1050
+
1051
+ Research is **evergreen** per the lifecycle-class taxonomy above and the canonical decision research-is-evergreen. `status: open` is the resting state for the doc's entire life — there are no `in_review` or `closed` transitions. Lineage to downstream artifacts is recorded through the relational fields below (`enhances:`, `spawned_epics:`, `bound_epic:`, `incorporated_into:`, `superseded_by:`), **never** through a status flip.
1052
+
1053
+ ```yaml
1054
+ type: research
1055
+ research_type: # REQUIRED, enum — the declared purpose of this research doc, set at creation
1056
+ # ([[HS-45YJ-120I-EPIC-research-type-field-the-declared-purpose]]). Coherent with the
1057
+ # capture `kind:` vocabulary (`idea | bug-fix | clip | reference | question`) one
1058
+ # level up the lifecycle, but not identical — research_type describes what the
1059
+ # research is FOR, not what kind of capture seeded it. Five values:
1060
+ # new-feature — grounds a not-yet-built capability or product surface
1061
+ # enhancement — improves or extends something already shipped
1062
+ # diagnose — investigates a bug, defect, or quality problem
1063
+ # code-audit — reviews existing code/architecture with no specific bug in hand
1064
+ # reference — captures external/background knowledge for reuse (the default)
1065
+ # Enforced by `schemas/heyspex-frontmatter.schema.json`. Missing on a pre-existing
1066
+ # doc is a WARNING during the grace window (`RESEARCH_TYPE_GRACE_END` in
1067
+ # `spex-console/src/lib/indexer/vault-rules.mjs`, mirroring `EPIC_PILLAR_GRACE_END`),
1068
+ # then a hard error. Deterministic kind→research_type suggestion is out of scope
1069
+ # here — owned by [[HS-6CFV-MVC1-EPIC-deterministic-type-assist-suggest-resear]].
1070
+ scope: # optional, enum — how broad a search context this research was run at:
1071
+ # internal — specs/, memories, ADRs only
1072
+ # codebase — the repo (console/, vendor/, scripts/, etc.)
1073
+ # external — web search
1074
+ # all — vault + codebase + web
1075
+ # Default "internal" when omitted. This is a CONSISTENCY fix, not a new
1076
+ # requirement — scope is NOT required and has no grace window
1077
+ # ([[HS-NGD1-TASK-consolidate-and-fix-the-research-scope-field]]). Written the
1078
+ # same way (same enum, same default) by `heyspex_save_research`,
1079
+ # `heyspex_promote_capture`, and `heyspex_complete_research`. Validated as a
1080
+ # string enum by `schemas/heyspex-frontmatter.schema.json` (distinct from the
1081
+ # unrelated freetext `scope:` on `type: protocol`).
1082
+ enhances: # REQUIRED when set — bidirectional consistency rule. If `enhances:` exists, the
1083
+ # target milestone MUST list this research in `informed_by:` (see §Provenance edges).
1084
+ # Single wikilink to the HS-MIL-* this research enhances. Not an array (M:1 rule).
1085
+ # Open research may omit during exploration; the validator enforces bidirectional
1086
+ # consistency whenever the field IS set. A research doc that spans multiple
1087
+ # milestones must be split first ([[HS-TQQS-GTH9-TASK-hs-split-research-mcp-tool]]).
1088
+ # NOTE: `scripts/validate-vault-ids.mjs` still gates on the legacy
1089
+ # `status: closed` framing — validator catch-up to this "when set" framing is on
1090
+ # the deferred list in [[research-is-evergreen]].
1091
+ sources_captures: # optional, list of wikilinks to HS-CAP-* — captures this research aggregates (M:N).
1092
+ # Reverse of `capture.informs_research:`. Bidirectional consistency enforced.
1093
+ spawned_epics: # optional, list of wikilinks to HS-EPIC-* — epics this research spawned (1:N).
1094
+ # Reverse of `epic.derived_from:`.
1095
+ enhances_epics: # optional, list of wikilinks to HS-EPIC-* — already-existing epics this research
1096
+ # augmented (M:N, rare). One-sided in v1 — no reverse field on epic.
1097
+ produces_diagnose: # optional, list of wikilinks to HS-DIAG-* — bugs found during this research and
1098
+ # promoted to a diagnose (1:N, mirrors `spawned_epics`). Reverse of
1099
+ # `diagnose.derived_from:`. Written by `heyspex_promote_research_to_diagnose`
1100
+ # ([[HS-W5VP-MSHY-TASK-research-diagnose-link-send-bugs-to-the]]); reuses the existing
1101
+ # diagnose machinery — see [[resolution-diagnose-workflow]].
1102
+ split_into: # optional, list of wikilinks — on the original (archive) research when split;
1103
+ # names the child research docs that carry forward.
1104
+ split_from: # optional, single wikilink — on a research created by splitting from an ancestor;
1105
+ # mirror of `split_into:` on the ancestor.
1106
+ source_capture: # optional wikilink to HS-CAP-* — legacy single-capture seed pointer; retained for
1107
+ # read-side back-compat. New research uses `sources_captures:` (M:N) as canonical.
1108
+ planning_flow: # optional until flow filed; then wikilink to HS-FLOW-*
1109
+ bound_epic: # optional; set when epic filed — locks findings to append-only ([[planning-flow-research-epic-lifecycle]])
1110
+ forked_from: # optional, wikilink — source HS-RES when forked for a new epic
1111
+ ```
1112
+
1113
+ Body is the research note. After `bound_epic:` is set, findings sections are append-only; new epic scope uses `## Scope for <epic name>` addendum sections.
1114
+
1115
+ ##### Code-audit findings table (`research_type: code-audit`)
1116
+
1117
+ A `research_type: code-audit` doc's body **must** carry a findings table — the authored standard for "reviewed the code, found N things, here's what happens to each" (HS-7D0I-WL88-TASK-code-audit-research-type-with-a-defined). Four columns, in this order:
1118
+
1119
+ | Finding | Severity | Action (diagnose\|task\|reference) | Exit (open\|closed) |
1120
+ | --- | --- | --- | --- |
1121
+ | One-line description of what was found | freetext (e.g. high/med/low) | `diagnose` \| `task` \| `reference` | `open` \| `closed` |
1122
+
1123
+ Every row's `Action` maps to exactly one of three defined exits — no fourth "just leave it" option:
1124
+
1125
+ - **`diagnose`** — a real bug. Promote it with the existing `heyspex_promote_research_to_diagnose` tool (HS-W5VP-MSHY-TASK-research-diagnose-link-send-bugs-to-the), which writes `research.produces_diagnose:` / `diagnose.derived_from:` (see §Provenance edges). Not new machinery — reuse it.
1126
+ - **`task`** — a cleanup/follow-up with no bug attached. File a task and set its `discovered_from: [[HS-RES-…]]` back to this research doc (see `task.discovered_from` above and §Dependencies "Discovered-from").
1127
+ - **`reference`** — non-actionable; the row itself IS the exit. Set `Exit: closed` directly, no further linkage required.
1128
+
1129
+ **Enforcement** (`spex-console/src/lib/indexer/vault-rules.mjs`):
1130
+
1131
+ - `CODE_AUDIT_FINDINGS_MALFORMED` — no findings table, or one that doesn't match the 4-column header shape, or a data row with an unrecognized `Action`/`Exit` value. Warning during the `CODE_AUDIT_GRACE_END` grace window (mirrors `RESEARCH_TYPE_GRACE_END`'s pattern — pre-existing code-audit docs like `HS-ZHAB-RES-doc-authoring-contract-audit-required-st` predate this format and are exempt while the window is open), then a hard error.
1132
+ - `CODE_AUDIT_FINDINGS_OPEN_ON_CLOSE` — the **closure gate**: a code-audit doc at `status: closed` or `review_pending` with any finding row whose `Exit` is `open`. Warning during the same grace window, then error — blocks closing a code-audit doc until every finding has exited.
1133
+
1134
+ #### `planning-flow`
1135
+
1136
+ Orchestration artifact for epic filing (planning-flow-research-epic-lifecycle). Body contains a `## Planning flow` section with fenced `mermaid` (semantic source for canvas projection per spec-diagram-design-vs-as-built-canvas).
1137
+
1138
+ ```yaml
1139
+ type: planning-flow
1140
+ research: # required, wikilink to HS-RES-*
1141
+ decisions: # optional, list of wikilinks — memory-decision or adr
1142
+ epic: # optional until epic filed; then wikilink to HS-EPIC-*
1143
+ has_diagram: # required, true — indexes canvas bridge + Shipped gallery when closed
1144
+ related: # optional, wikilinks to tasks and supporting specs
1145
+ ```
1146
+
1147
+ #### `roadmap`
1148
+
1149
+ ```yaml
1150
+ type: roadmap
1151
+ roadmap_manifest: # required, machine-readable phase index (see below)
1152
+ related_roadmaps: # optional, wikilinks to other HS-ROADMAP-* specs
1153
+ ```
1154
+
1155
+ `roadmap_manifest` shape (indexer validates; agents request via `--compact`):
1156
+
1157
+ ```yaml
1158
+ roadmap_manifest:
1159
+ version: 1
1160
+ phases:
1161
+ - id: M2 # short stable id
1162
+ name: Indexer MVP
1163
+ tasks: [HS-V3YS, HS-1BML] # HS-NNNN or HS-EPIC-NNNN ids
1164
+ horizon: # may not ship — priority-4 / tag:horizon backlog
1165
+ - HS-WYHZ
1166
+ initiatives: # optional links to other roadmap docs
1167
+ - HS-ROADMAP-VOICE-CHROME
1168
+ ```
1169
+
1170
+ Body is the human narrative (goals, exit criteria, risks). **Do not duplicate** the task list in prose without mirroring ids in `roadmap_manifest` — the manifest is the console/CLI source of truth for ordering.
1171
+
1172
+ Horizon items use native `HS-NNNN` ids (or wikilinks to `future-considerations/` research until promoted). No separate FC id namespace (adr-0012-roadmap-storage).
1173
+
1174
+ #### `draft`
1175
+
1176
+ ```yaml
1177
+ type: draft
1178
+ source: # optional, where this draft came from (URL, "phone capture", "screenshot", etc.)
1179
+ captured_at: # required, ISO-8601 datetime
1180
+ source_intent_hash: # optional but required on Agent ingest; SHA-256 of canonical capture blob ([[plan-ingest-review-roadmap-workflow]])
1181
+ source_intent: # optional verbatim excerpt (≤2 KB) of human inputs at capture time
1182
+ agent_output_hash: # optional; SHA-256 of structured Agent preview at Save-to-drafts
1183
+ review_stage: # optional, enum: inbox | deliberating | staging | review | promoted | rejected
1184
+ council_requested: # optional bool | null — per-submit override; null = apply deliberation.triggers policy ([[council-opt-in-pre-review-conditioning]])
1185
+ triage_record: # optional; scout pre-pass ([[council-triage-scout-optional-llm]], [[review-inbox-workshop-scout-overview]])
1186
+ deliberation_record: # optional; Gate 0 output ([[ai-deliberation-gate]]) — append-only runs
1187
+ suggestions: # optional; staged Gate A records ([[HS-EU98-GCGE-TASK-staged-draft-suggestion-schema]]) — omit when empty
1188
+ review_record: # optional; Gate A commit audit — who/when/per-suggestion outcomes
1189
+ ingest_mode: # optional, enum: manual | agent_llm — default manual when omitted
1190
+ capture_scope: # optional, enum: selection | full — did a highlight scope the body? (HS-M54O); absent ⇒ full
1191
+ purpose: # optional free string — the user's stated "why" for capturing (HS-7S40)
1192
+ context_blocks: # optional list of { source: typed | voice, text, at: ISO-8601 } — enrichment
1193
+ # added on the intent card (HS-JQ7O/0077), each bound to this draft's trace_id.
1194
+ # Append-only within a trace_id; ordered by `at`. Absent ⇒ no extra context.
1195
+ llm_route: # optional, required when ingest_mode is agent_llm — builder-selected route from console picker:
1196
+ # chatgpt | gemini | claude | auto | ... (enum grows with integrations; must stay in sync with UI labels)
1197
+ llm_resolved_at: # optional, ISO-8601 datetime when ingest_mode agent_llm — when the routed model finished
1198
+ resolved_model: # optional free string or structured id — actual provider/model id after routing (meaningful when llm_route: auto or when aliases map to concrete models)
1199
+ submission_kind: # optional, enum: external_media | refined_intent — Gate 0 epistemic mode ([[companion-external-media-vs-refined-intent-council]]); companion defaults external_media
1200
+ capture_channel: # optional, enum: manual_ui | cli | voice | extension | companion | ...
1201
+ companion_dialogue: # optional append-only [{ role: user|assistant, text, at }] — pre-send chat about capture ([[HS-67TW-2966-TASK-companion-pre-council-context-dialogue]]); omit when empty
1202
+ capture_group_id: # optional string — shared id linking multiple captures made in one multi-capture
1203
+ # companion session; drafts sharing this id render as a connected set on the
1204
+ # Captures surface, ordered by capture_group_seq. Distinct from capture_bundle_id
1205
+ # below (one composite draft with multiple sources) — this links multiple SEPARATE
1206
+ # draft files. Omit for standalone single captures.
1207
+ capture_group_seq: # optional positive integer — 1-based position within capture_group_id's group.
1208
+ # Only meaningful when capture_group_id is set.
1209
+ capture_bundle_id: # optional UUID — companion-local bundle id when submitted from Capture inbox ([[companion-capture-inbox-and-bundles]])
1210
+ source_set: # optional — multi-source bundle; list of members, each with { url | file_path, title, member_trace_id, member_note, file_size?, file_type? }
1211
+ # For web captures: url (required), title, member_trace_id, member_note ([[HS-D1YU-OVFO-TASK-bundle-submit-composite-draft-source-set]])
1212
+ # For file captures: file_path (required), title (filename), file_size, file_type, member_trace_id, member_note ([[HS-5FI0-0036-EPIC-file-system-selection-voice-capture]])
1213
+ # Omit for single-source captures
1214
+ wake_used: # optional bool — companion wake phrase invoked (vs shortcut-only capture)
1215
+ foreground_app: # optional string — OS-visible focused app title/name at capture time
1216
+ spoken_command_tail: # optional sanitized transcript excerpt when capture_channel includes voice (see [[uc-voice-ingest-hey-spex]])
1217
+ shortcut_binding_id: # optional stable identifier for whichever hotkey initiated capture
1218
+ voice_companion_semver: # optional — installed companion semantic version emitting the draft
1219
+ pointer_screen_x: # optional — mouse pointer X in screen px, sampled once at ingest trigger
1220
+ pointer_screen_y: # mouse pointer Y (screen px)
1221
+ pointer_dpi_scale:
1222
+ pointer_sampled_at: # ISO-8601
1223
+ last_click_screen_x:
1224
+ last_click_screen_y:
1225
+ last_click_at:
1226
+ spatial_hint_ttl_ms:
1227
+ chrome_viewport_ratio_x:
1228
+ chrome_viewport_ratio_y:
1229
+ pointer_dom_hint: # optional heuristic from Chrome elementFromPoint; non-authoritative
1230
+
1231
+ # File capture provenance (when source_type: local_file) — [[HS-5FI0-0036-EPIC-file-system-selection-voice-capture]]
1232
+ source_type: # optional enum: local_file | url — discriminator; url is default/implicit for browser captures
1233
+ file_path: # optional string — absolute path (single file or root folder); present when source_type: local_file
1234
+ file_size: # optional int — file size in bytes
1235
+ file_type: # optional string — MIME type or file extension
1236
+ file_modified_at: # optional ISO-8601 — file mtime at capture
1237
+ folder_depth: # optional int — recursion depth when folder captured
1238
+ folder_file_count: # optional int — total files in folder/zip capture
1239
+ zip_extracted: # optional bool — true if zip was extracted to temp for ingestion
1240
+ zip_source_path: # optional string — original zip path when zip_extracted: true
1241
+ sensitive_files_included: # optional list of { path, pattern_matched, user_confirmed_at } — audit when user overrode sensitive file warning
1242
+ ```
1243
+
1244
+ Body is the ingested content; triage promotes a draft into a task, memory, or research file by editing the frontmatter `type:` and moving the file.
1245
+
1246
+ Agent-assisted drafts **should** end the markdown body with a short human-readable provenance line so readers skimming raw files see how the draft was produced; **frontmatter carries the authoritative fields.**
1247
+
1248
+ Example footer (console-rendered; values must mirror frontmatter):
1249
+
1250
+ ```markdown
1251
+
1252
+ ---
1253
+
1254
+ _Ingest provenance:_ route `claude` → resolved `anthropic/claude-3-7-sonnet` · _Captured:_ 2026-05-16T12:00:00Z
1255
+ ```
1256
+
1257
+ See uc-ingest-anywhere §Console toggle: Manual vs Agent ingest, HS-DOC-architecture-foundations §What it exposes (API), companion capture + optional spatial hints in uc-voice-ingest-hey-spex §Persistence / §Spatial pointers when `capture_channel: voice`.
1258
+
1259
+ **`deliberation_record`** (when present):
1260
+
1261
+ Permitted on `draft`, `task`, `epic`, and `review` types — Council session output, append-only across all four ai-deliberation-gate invocation points per adr-0015-council-as-cross-cutting-capability. The host spec retains its existing type; the record is purely advisory and never gates anything (human disposition stays in Gates A/B/C per heyspex-north-star Principle 3).
1262
+
1263
+ ```yaml
1264
+ deliberation_record:
1265
+ latest_run_id: "<uuid>"
1266
+ runs:
1267
+ - run_id: "<uuid>"
1268
+ at: "<ISO-8601>"
1269
+ invocation_point: pre_review | canvas_live | promoted_recheck | review_close
1270
+ # which of the four Council invocation points produced this run
1271
+ # (per [[adr-0015-council-as-cross-cutting-capability]])
1272
+ submission_lens: refined_intent | external_media | promoted_spec_recheck
1273
+ # role objective discriminator — lifts the draft body's
1274
+ # `submission_kind` to the run so promoted-spec contexts carry it
1275
+ parent_run_id: "<uuid>" | null # loop-suppression input; null on first run for a spec
1276
+ test_preset: free | cheap | production | null # when [[ai-deliberation-gate]] test_mode was active
1277
+ api_calls: 4 # optional audit — varies by invocation: 1 (canvas_live),
1278
+ # 3 (review_close, skip rebuttal), 4 (pre_review skip_rebuttal),
1279
+ # 7 (full pre_review or promoted_recheck)
1280
+ roles:
1281
+ - id: tech_lead | program_lead | ux_lead
1282
+ provider: "<string>"
1283
+ model: "<string>"
1284
+ position:
1285
+ disposition: schedule_now | schedule_later | horizon | defer | deprecated | merge | reject | split
1286
+ priority: 0-4
1287
+ roadmap_phase: "<id>"
1288
+ merge_into: "[[HS-NNNN]]"
1289
+ north_star_alignment: aligned | tension | conflict
1290
+ arguments: ["<bullet>"]
1291
+ facilitator: # absent on `canvas_live` (single-role) and on
1292
+ # `review_close` when skip_rebuttal is set
1293
+ recommended_disposition: "<enum>"
1294
+ confidence: 0.0-1.0
1295
+ disagreements: ["<role_a vs role_b on topic>"]
1296
+ north_star_conflict: # present when ≥2 roles report conflict
1297
+ agreed: true
1298
+ summary: "<string>"
1299
+ path_a_change_l1: { summary, typical: false }
1300
+ path_b_reshape_plan: { summary, suggested_edits: [] }
1301
+ ```
1302
+
1303
+ **Storage bound:** `runs[]` is capped at the last 10 entries in frontmatter; older runs archive to `specs/.deliberation-archive/<spec-id>.yaml` (gitignored, audit-only) per adr-0015-council-as-cross-cutting-capability §Consequences.
1304
+
1305
+ **`triage_record`** (when present; scout only — not council):
1306
+
1307
+ ```yaml
1308
+ triage_record:
1309
+ at: "<ISO-8601>"
1310
+ model: "<slug>"
1311
+ title_line: "<≤80 chars>"
1312
+ memory_jogger: "<2-3 sentences>"
1313
+ review_category: feature_request | external_media | brainstorm | research | bug | mixed | unclear
1314
+ recommend_council: true | false
1315
+ confidence: 0.0-1.0
1316
+ rationale: "<string>"
1317
+ suggested_actions:
1318
+ - id: run_council | add_context | gate_a_review | defer | promote_quick | discard
1319
+ label: "<string>"
1320
+ primary: true
1321
+ quick_tags: ["<optional>"]
1322
+ ```
1323
+
1324
+ **`suggestions`** (when present; omit block entirely when empty — pre-HS-GCGE-TASK drafts unchanged):
1325
+
1326
+ ```yaml
1327
+ suggestions:
1328
+ - id: "<stable-uuid-within-draft>"
1329
+ kind: roadmap | priority | child_task | consideration
1330
+ payload: {} # kind-specific proposed fields (epic stub, priority, etc.)
1331
+ rationale: "<one line>"
1332
+ disposition: pending | accepted | edited | declined
1333
+ edited_payload: {} # optional; present when disposition: edited
1334
+ provenance:
1335
+ llm_route: "<enum>"
1336
+ resolved_model: "<provider/model>"
1337
+ at: "<ISO-8601>"
1338
+ ```
1339
+
1340
+ **`review_record`** (written on Gate A staging commit):
1341
+
1342
+ ```yaml
1343
+ review_record:
1344
+ at: "<ISO-8601>"
1345
+ by: pwher
1346
+ outcomes:
1347
+ - suggestion_id: "<uuid>"
1348
+ disposition: accepted | edited | declined
1349
+ note: "<optional>"
1350
+ ```
1351
+
1352
+ #### `daily`
1353
+
1354
+ ```yaml
1355
+ type: daily
1356
+ date: # required, ISO-8601 date
1357
+ ```
1358
+
1359
+ Body is free-form notes for the day.
1360
+
1361
+ ## Body structure conventions
1362
+
1363
+ Beyond the type-specific required sections noted above, all specs follow these structural conventions:
1364
+
1365
+ - The first heading is `# <Name>` (the human-readable title), matching `name:` in frontmatter.
1366
+ - Optional first block: a single `> [!note]` / `> [!important]` callout if there's a one-line framing message.
1367
+ - Sections use `##` headings. Sub-sections `###`.
1368
+ - A spec ends with `## Related specs` (optional) and `## Change log` (required for L1, L2, L3, ADR, and protocol; optional but encouraged elsewhere).
1369
+
1370
+ ### Recommended sections for long-lived specs
1371
+
1372
+ Two SCEH-Pattern conventions (HS-U88E-SP34-EPIC-sceh-patterns-3-and-4-markdown-conventions) define optional `##` sections useful on specs that accumulate work across many sessions:
1373
+
1374
+ | Section | Protocol | Use when | Placement |
1375
+ |---|---|---|---|
1376
+ | `## Session Boundaries` | session-boundary-marker | Multi-session epics, milestones, and HS-REV review docs benefit from a five-column session-by-session trail (`session_id` / `agent` / `started` / `ended` / `summary`). | Immediately above `## Change log`. |
1377
+ | `## Conflict Resolution` | conflict-resolution-section | Two or more sessions left contradictory opinions in the body, change log, or HS-REV companions and a reviewer has had to pick. Three-row table (opinions + bold `**resolution**`) with optional memory-extraction routing into `memory-decision` specs. | Below `## Session Boundaries` if present, above `## Change log`. |
1378
+
1379
+ Both are **recommended** — opt-in per spec, not enforced by the wikilink validator. They are content commentary about the spec; control-plane runtime state (claim/focus/spec events, handoff context) stays in Neon (control-plane-belongs-in-neon).
1380
+
1381
+ ## Dependencies
1382
+
1383
+ HeySpex inherits beads' four dependency types and adds a normalization choice: dependencies are expressed **both** as structured frontmatter arrays **and** as inline wikilinks in body prose. The frontmatter is canonical for queries; the body wikilinks are for reading flow.
1384
+
1385
+ | Type | Frontmatter field | Inverse field | Semantics |
1386
+ | --- | --- | --- | --- |
1387
+ | **Blocks** | `blocks: [[...]]` | `blocked_by: [[...]]` | Hard. A blocked task is not ready until all `blocked_by` resolve. |
1388
+ | **Related** | `related: [[...]]` | `related:` (symmetric) | Soft. Navigational only; no readiness implication. |
1389
+ | **Parent-child** | `children: [[...]]` | `parent: [[...]]` | Hierarchical. Children roll up to parent for "completion of parent" calculations. |
1390
+ | **Discovered-from** | `discovered_from: [[...]]` | `discovered:` (list) | Provenance. "This task was born while working on X." |
1391
+ | **Iterates-on** | `iterates_on: [[...]]` | `iteratedBy:` (computed) | Re-attempt. "This initiative re-attempts an outcome the target shipped but did not fully deliver." No readiness implication; neither doc is demoted. See §Iteration lineage below. |
1392
+
1393
+ Rules:
1394
+
1395
+ - The indexer maintains both sides of every edge; the Builder only needs to write one side, and the console fills the other on save. (Implementation detail; specified in HS-DOC-architecture-foundations.)
1396
+ - The indexer rejects edges that form a cycle in the `blocks` graph or the `parent`/`children` graph.
1397
+ - `related` edges are symmetric; the indexer enforces symmetry on save.
1398
+ - A task without a `parent:` and not referenced as a child by anyone is treated as a top-level task.
1399
+ - `discovered_from` is single-valued; if a task has multiple origins, model the additional ones as `related:`.
1400
+
1401
+ ## Provenance edges (capture → research → milestone)
1402
+
1403
+ The promotion lifecycle — capture observation → research synthesis → milestone commitment — has cross-type provenance edges with strict cardinalities. Ratified by HS-OFR5-M2J3-EPIC-introduce-milestones-as-highest-entity §Architecture; enforced by `scripts/validate-vault-ids.mjs` (HS-HQRM-YJX6-TASK-research-enhances-exactly-one-milestone-validator-and-bidirectional-linkage).
1404
+
1405
+ | Edge | Cardinality | Forward field | Reverse field |
1406
+ | --- | --- | --- | --- |
1407
+ | **Capture → Research** | M:N | `capture.informs_research: [[[HS-RES-…]]]` | `research.sources_captures: [[[HS-CAP-…]]]` |
1408
+ | **Research → Milestone** | **M:1** (strict) | `research.enhances: [[HS-MIL-…]]` (single, not array) | `milestone.informed_by: [[[HS-RES-…]]]` |
1409
+ | **Research → Epic (spawned)** | 1:N | `research.spawned_epics: [[[HS-EPIC-…]]]` | `epic.derived_from: [[HS-RES-…]]` (single) |
1410
+ | **Research → Epic (augmented)** | M:N | `research.enhances_epics: [[[HS-EPIC-…]]]` | (one-sided — no reverse field on epic in v1) |
1411
+ | **Research → Diagnose (bug found)** | 1:N | `research.produces_diagnose: [[[HS-DIAG-…]]]` | `diagnose.derived_from: [[HS-RES-…]]` (single) |
1412
+
1413
+ Rules:
1414
+
1415
+ - **Research is M:1 to milestone — strict.** A single research doc that spans multiple milestones must be split before promotion. The split-action protocol is owned by HS-TQQS-GTH9-TASK-hs-split-research-mcp-tool; the original research stays as the audit archive with `status: closed` and `split_into: [[HS-RES-A]], [[HS-RES-B]]`, while N child research docs each carry `split_from: [[HS-RES-original]]` and their own `enhances:` target.
1416
+ - **Open research may omit `enhances:`.** The validator only requires it on `status: closed` research (the promotion-time gate); during exploration the target milestone can still be ambiguous.
1417
+ - **`enhances:` is single-valued.** Not an array — the validator rejects array form and points the author at the split flow.
1418
+ - **Bidirectional consistency is enforced.** If research X has `enhances: Y`, milestone Y MUST list X in `informed_by:`. If capture C has `informs_research: [X, Y]`, both X.sources_captures and Y.sources_captures MUST include C. The validator surfaces one-sided drift as a hard error on each side.
1419
+ - **Captures M:N research is intentional.** A single capture may inform multiple research docs over time (e.g. a "voice capture works" observation that lands in both companion research and console-voice research). The first promotion writes `capture.promoted_to:` (binary pointer); subsequent re-uses extend `capture.informs_research:` without touching `promoted_to:`.
1420
+ - **`spawned_epics` vs `enhances_epics`.** `spawned_epics` is the canonical 1:N edge — epics this research *created*, mirrored as `epic.derived_from:` on each child. `enhances_epics` is the rarer case where research *augmented* an already-existing epic; it's a one-sided list with no reverse field in v1.
1421
+ - **`produces_diagnose` mirrors `spawned_epics`.** A research investigation may surface a bug, which is promoted into a first-class `HS-DIAG-*` (not new machinery — the existing diagnose entity per resolution-diagnose-workflow). `research.produces_diagnose:` is the canonical 1:N edge, mirrored as `diagnose.derived_from:` on each child, written by `heyspex_promote_research_to_diagnose`. This is the research-side sibling of the capture-side exit `heyspex_promote_capture_to_diagnose` (which writes `diagnose.promoted_from:` ⇔ `capture.promoted_to:`/`informs_diagnose:` instead).
1422
+
1423
+ Promotion-action contract (`/hs-promote-research`, HS-2ERO-QQHP-TASK-hs-promote-research-mcp-tool): writes all four sides (research.enhances, research.spawned_epics, milestone.informed_by, epic.derived_from) atomically. The validator is the safety net for hand-edited drift, not the primary write path.
1424
+
1425
+ ## Iteration lineage (round-to-round re-attempts)
1426
+
1427
+ The **third doc-to-doc relation tier**, alongside supersession (terminal, whole-doc) and amends (partial, non-terminal) — ratified via HS-3TZX-T9T6-RES-iteration-loop-lineage-and-capture Part A, built by HS-O97P-T9T6-EPIC-iteration-loop-lineage-relation-tier. It expresses what neither existing tier can: *work shipped and closed, the outcome fell short of the goal, and a NEW family re-attempts the SAME goal.* Supersession would falsify round 1 (it wasn't wrong — it shipped, its review is accurate history); amends would misdescribe it (round 2 contradicts nothing).
1428
+
1429
+ ### Fields
1430
+
1431
+ ```yaml
1432
+ iterates_on: # optional, YAML list of wikilinks — lives ONLY on the NEWER doc (one-edit-on-source,
1433
+ # same principle as amends). Semantics: "this initiative re-attempts an outcome the
1434
+ # target initiative shipped but did not fully deliver." Typically carried by the
1435
+ # round-2 research doc pointing at the round-1 research doc — the research chain is
1436
+ # the spine (research is evergreen, so the root research id is the durable handle
1437
+ # spanning every round; no new entity type).
1438
+ # iteratedBy — COMPUTED reverse map (indexer/validator output, like amendedBy) — never written
1439
+ # by hand into frontmatter.
1440
+ outcome: # optional, on TRACKABLE docs only, set by the HUMAN REVIEWER at close — never
1441
+ # inferred, never agent-set. Enum: met | partially-met | not-met. Lives ONLY on
1442
+ # the closed doc (not echoed onto the round-2 doc; discovery goes through the
1443
+ # iteratedBy reverse map).
1444
+ outcome_note: # optional, one-line reviewer prose qualifying the verdict.
1445
+ ```
1446
+
1447
+ ### Invariants
1448
+
1449
+ - **Nothing is demoted.** Both docs stay `canonical: true`; round 1 keeps `status: closed`. `iterates_on:` is a lineage edge, not a supersession — demoting round 1 to `canonical: false` would falsify the record and hide shipped scope from round 2's breakdown.
1450
+ - **`outcome:` is the reviewer's judgment alone.** `status: closed` without an `outcome:` says only "shipped"; it must never be read as "goal met." The verdict is written at reviewer close (the one mutable-at-close moment), per [[session-close-protocol]].
1451
+ - **Distinct family keys are correct.** Round 2 is its own initiative with its own children and review, so it takes a new family key; `iterates_on:` is the cross-family edge that replaces the missing shared grep handle.
1452
+
1453
+ ### Required `## Related / lineage` section on promoted docs
1454
+
1455
+ Every promoted doc's outline ends with a resolved-wikilink table (immediately before `## Change log`), authored at promote-time and gated by `heyspex_validate_linkage` (every row must resolve under `specs/`):
1456
+
1457
+ ```markdown
1458
+ ## Related / lineage
1459
+
1460
+ | Doc | Role |
1461
+ |---|---|
1462
+ | [[round-1-research]] | Iterates on |
1463
+ | [[HS-REV-...]] | Prior attempt's review |
1464
+ | [[HS-CAP-...]] | Feedback capture |
1465
+ ```
1466
+
1467
+ Role vocabulary: **Iterates on · Prior attempt's review · Feedback capture · Amends · Superseded by · Pillar · Spawned epics** (extend with prose roles when none fits — e.g. "Source capture", "Enhances").
1468
+
1469
+ ## Evidence linking
1470
+
1471
+ > [!important] "Summaries with receipts"
1472
+ > Ratified by adr-0008-branch-brief-and-evidence-schema for HS-7KOO-0022-EPIC-continuity-layer-gaps. Every claim a memory or brief makes should be able to point at a verifiable source. Without evidence, a summary is unfalsifiable; with it, a reader can navigate from claim to proof.
1473
+
1474
+ `evidence:` is an optional array on `branch-brief`, `memory-lesson`, `memory-decision`, `memory-reference`, `memory-ui-pattern`, and `task`. Each entry:
1475
+
1476
+ ```yaml
1477
+ evidence:
1478
+ - type: commit # required — one of the evidence types below
1479
+ ref: "abc123" # required — the reference, format per type
1480
+ line: 42 # optional — line number (file/diff only)
1481
+ description: "..." # optional — what this evidence shows
1482
+ ```
1483
+
1484
+ Evidence types:
1485
+
1486
+ | Type | `ref` format | Example |
1487
+ | --- | --- | --- |
1488
+ | `commit` | short or full SHA | `abc123`, `abc123def…` |
1489
+ | `file` | repo-relative path, optional `:line` | `src/foo.ts`, `src/foo.ts:42` |
1490
+ | `pr` | PR number | `#123` |
1491
+ | `diff` | `commit:path` | `abc123:src/pages/dashboard.tsx` |
1492
+ | `issue` | issue number | `#42` |
1493
+ | `note` | freeform text | `Builder confirmed via Slack 2026-05-15` |
1494
+ | `test` | test/gate identifier | `vitest:src/lib/continuity/plan-vs-actual.test.ts`, `tsc:noEmit` |
1495
+
1496
+ > [!note] `test` added 2026-08-11 (receipt-on-ship, HS-XGG4-IU2S-EPIC-receipt-on-ship-plan-vs-actual-proof-at). Additive extension per adr-0008-branch-brief-and-evidence-schema §Consequences ("a future change to evidence types … is a normal entity-model edit"); lets Plan Receipts cite test/gate results as typed evidence.
1497
+
1498
+ ### Inline shorthand
1499
+
1500
+ In body prose, evidence may be cited inline as `[evidence:<type>:<ref>]`:
1501
+
1502
+ ```markdown
1503
+ We chose Payment Elements over hosted checkout [evidence:commit:abc123]
1504
+ because it gives better customization [evidence:pr:#123].
1505
+ ```
1506
+
1507
+ The indexer harvests inline shorthand and the frontmatter `evidence:` array into the same evidence index; **frontmatter is canonical** for "what grounds this spec?" queries (same rule as `tags:` and dependency edges). Console rendering of evidence references (commit → diff view, file → file viewer at line, pr/issue → git host) is an implementation concern specified downstream, not part of this schema contract.
1508
+
1509
+ ## Delivery snapshot
1510
+
1511
+ > [!note] User-persisted, not auto-maintained
1512
+ > See HS-WB81-RBJ8-TASK-spec-delivery-frontmatter-snapshot. The live source of truth for "which commits touched this spec" is always `git log -- <spec_path>`; the `delivery:` block is an *optional persisted snapshot* the Builder takes from the console's On Deck row so the vault carries its own delivery trail.
1513
+
1514
+ `delivery:` is an **optional, cross-type** key. It exists only after the Builder has clicked "snapshot to spec" in the console's delivery dropdown. Shape:
1515
+
1516
+ ```yaml
1517
+ delivery:
1518
+ snapshot_at: 2026-05-21T07:00:00-05:00 # ISO timestamp of when the snapshot was taken
1519
+ commits:
1520
+ - sha: 8648ad5e3f... # full SHA
1521
+ short_sha: 8648ad5 # 7-char SHA
1522
+ branch: hs-mvp0520 # primary branch (local before remote)
1523
+ pushed: true # boolean — reachable from origin/*
1524
+ at: 2026-05-21T05:14:32-05:00 # commit author time
1525
+ author: pwher
1526
+ message: "feat(console): per-spec delivery dropdown on On Deck child rows"
1527
+ ```
1528
+
1529
+ Rules:
1530
+
1531
+ - One primary branch per row, not the full branch list (live view stays authoritative for fan-out).
1532
+ - `message` is the first commit-message line only; full commit bodies live in git, not the vault.
1533
+ - `delivery:` may be present on **any** spec type; treat it as a presentational artifact, not a dependency edge — the indexer does not crawl it.
1534
+ - A stale snapshot (live count ≠ snapshot count) is rendered as such in the console; the snapshot is never auto-refreshed.
1535
+ - Persisted by `PUT /api/specs/[id]/delivery`; no git hook or agent workflow rewrites it.
1536
+
1537
+ ## Wikilink resolution rules
1538
+
1539
+ HeySpex uses Obsidian-compatible wikilinks: `[[target]]` or `[[target|display text]]`.
1540
+
1541
+ Resolution algorithm:
1542
+
1543
+ 1. **Strip extension.** `[[foo]]`, `[[foo.md]]`, and `[[foo|Foo]]` all target the file basename `foo`.
1544
+ 2. **Case-insensitive match.** The resolver compares lowercased basenames. `[[Foo]]` and `[[foo]]` resolve to the same file.
1545
+ 3. **No path qualifier needed.** Folders are not part of the resolution. `[[entity-model]]` finds `specs/entity-model.md` regardless of where the linking spec lives. (Consistent with Obsidian's "default" link mode.)
1546
+ 4. **Aliases.** If a spec's frontmatter has `aliases: [foo, bar]`, both `[[foo]]` and `[[bar]]` also resolve to that spec.
1547
+ 5. **Forward references are valid.** A wikilink to a non-existent spec is not an error; it is a signal that the linked spec should be written. The indexer records forward references in a `forward_refs` view; the console shows them inline so the Builder can see what's pending.
1548
+ 6. **Duplicate basenames are forbidden.** Two specs may not share a basename. The indexer rejects writes that would create a duplicate.
1549
+ 7. **Filename is canonical; do not author id-form links.** The resolver also matches a link target against a spec's frontmatter `id:` (it tries id → basename → alias, in that order). This is a **tolerant fallback** so that hand-typed or legacy `[[HS-DEC-…]]`/`[[HS-TASK-…]]` links still resolve — it is **not** the form to write. Author every link by **filename basename** so the vault stays Obsidian-portable (Obsidian does not resolve by the `id:` field). For memory specs (decisions, lessons, references, ui-patterns) the file is slug-named while its `id` carries a type prefix, so `[[HS-DEC-foo]]` is the id form and `[[foo]]` is the canonical filename form — prefer the latter. The vault was swept to filename form on 2026-05-28 (`scripts/sweep-hs-dec-links-to-basename.mjs`).
1550
+
1551
+ Display text:
1552
+
1553
+ - `[[entity-model|the entity model]]` renders "the entity model" as a link to `entity-model.md`.
1554
+ - This is the same syntax Obsidian uses; HeySpex inherits it unchanged.
1555
+
1556
+ ## Tags
1557
+
1558
+ Tags use the `tags:` array in frontmatter. They are flat strings, no nesting (no `#foo/bar` semantics in v1 beyond convention).
1559
+
1560
+ Body-inline `#tag` syntax is allowed for compatibility with Obsidian users; the indexer harvests both frontmatter tags and body inline tags into the same index, but frontmatter is canonical for "is this spec tagged X?" queries.
1561
+
1562
+ ## Reserved frontmatter keys
1563
+
1564
+ The following keys are reserved by HeySpex and must not be repurposed:
1565
+
1566
+ - `id`, `name`, `type`, `status`, `priority`, `owner`, `created`, `updated`, `tags`, `related`, `aliases`
1567
+ - `blocks`, `blocked_by`, `parent`, `children`, `discovered_from`, `discovered`
1568
+ - `assignee`, `started_at`, `closed_at`, `close_reason`, `defer_until`
1569
+ - `evidence` (cross-type; see [Evidence linking](#evidence-linking))
1570
+ - `delivery` (cross-type; user-persisted git-log snapshot — see [Delivery snapshot](#delivery-snapshot))
1571
+ - `legacy_bd_id`, `legacy_dolt_commit`
1572
+ - Type-specific fields named above (including `branch-brief`'s `branch`, `goal`, `implements`, `plan`, `actual`, `risks`, `open_questions`, `next_steps`, `decisions`; `milestone`'s `epics`, `target_date`, `informed_by`; the `epic`'s `milestone` parent pointer and optional `derived_from`; `task`'s `split_of`, `split_index`, `handoff_id` (split lineage — envelope lives in body) and optional `affects_paths`; `research`'s `enhances`, `sources_captures`, `spawned_epics`, `enhances_epics`, `produces_diagnose`, `split_into`, `split_from`; `diagnose`'s optional `promoted_from` and `derived_from`; `capture`'s `informs_research`; the `agent` type's `role`, `model`, `allowed_tools`, `disallowed_tools`, `procedures`, `handoffs`, `guardrails`, `lane`, `color`, `owned_paths`, and `source_attribution` import-provenance object). Type-specific fields are additive and do **not** require an ADR (only a new *universal* reserved key does); `source_attribution` was added under HS-GMLJ-K3KR-TASK-lock-the-community-import-design-and-add; `owned_paths` / `affects_paths` under HS-ZMAQ-M15C-TASK-declare-owned-path-globs-on-agent-identi.
1573
+ - `reviews` (review type — back-compat alias for `parent:` pointing at the spec under review)
1574
+
1575
+ Adding a new universal reserved key requires an ADR.
1576
+
1577
+ ## Validation
1578
+
1579
+ The indexer (see HS-DOC-architecture-foundations §Indexer) runs frontmatter validation on every write:
1580
+
1581
+ - All required fields present and of the expected type.
1582
+ - `id` matches the entity type's pattern.
1583
+ - `id` is unique across the vault.
1584
+ - `status` is in the enum.
1585
+ - All wikilinks in frontmatter resolve, or are tracked as forward references.
1586
+ - No cycles in `blocks` or `parent`.
1587
+ - No duplicate basenames.
1588
+
1589
+ Validation failures are surfaced in the console and via `hs lint`. They do not block git writes (the vault is plain text and the Builder is trusted), but the indexer marks the affected spec as invalid until corrected.
1590
+
1591
+ ## Examples
1592
+
1593
+ ### Minimal task
1594
+
1595
+ ```markdown
1596
+ ---
1597
+ id: HS-O3E0
1598
+ name: Fix console keyboard navigation
1599
+ type: task
1600
+ status: open
1601
+ priority: 2
1602
+ owner: pwher
1603
+ created: 2026-05-14
1604
+ updated: 2026-05-14
1605
+ kind: bug
1606
+ blocked_by:
1607
+ - "[[HS-IS1A-TASK-SpexConsole-routing-rewrite]]"
1608
+ related:
1609
+ - "[[HS-582Q-TASK-arrow-key-conflicts]]"
1610
+ tags: [console, ux, keyboard]
1611
+ ---
1612
+
1613
+ # Fix console keyboard navigation
1614
+
1615
+ Arrow keys do not move focus between issues in the list view; only Tab works. Compare to Linear, where ↑/↓ navigates the list.
1616
+
1617
+ ## Acceptance
1618
+
1619
+ - Up/Down arrow keys move focus between rows in `IssuesSurface`.
1620
+ - Enter on a focused row opens the detail pane.
1621
+ - No regression for input fields (arrow keys still move the caret inside text inputs).
1622
+
1623
+ ## Change log
1624
+
1625
+ | Date | Author | Change |
1626
+ | --- | --- | --- |
1627
+ | 2026-05-14 | pwher | Created from drafts/triage. |
1628
+ ```
1629
+
1630
+ ### Memory lesson
1631
+
1632
+ ```markdown
1633
+ ---
1634
+ id: HS-LES-bd-process-leak
1635
+ name: bd CLI process leak
1636
+ type: memory-lesson
1637
+ status: open
1638
+ priority: 1
1639
+ owner: pwher
1640
+ created: 2026-05-14
1641
+ updated: 2026-05-14
1642
+ topic: bd-process-leak
1643
+ observed: 2026-05-14
1644
+ related:
1645
+ - "[[HS-LES-dolt-embedded-windows]]"
1646
+ - "[[adr-0006-replace-beads]]"
1647
+ tags: [bd, dolt, windows, process-leak]
1648
+ ---
1649
+
1650
+ # bd CLI process leak
1651
+
1652
+ ## Problem
1653
+
1654
+ A single `bd create` issued the prior session sat alive consuming 107 GB working set, plus seven orphaned bd procs at ~67 MB each. RAM hit 80% of 128 GB.
1655
+
1656
+ ## Fix
1657
+
1658
+ At session start, run `Get-Process bd | Sort WorkingSet -Desc` on Windows and kill any orphans before issuing new bd commands. Be wary of invoking `bd create` with `run_in_background` — bd does not always exit cleanly when the parent harness moves on.
1659
+
1660
+ ## Change log
1661
+
1662
+ | Date | Author | Change |
1663
+ | --- | --- | --- |
1664
+ | 2026-05-14 | pwher | Migrated from bd memory `lessons/bd-process-leak`. |
1665
+ ```
1666
+
1667
+ ## Open questions
1668
+
1669
+ These are intentionally left for the Builder's confirmation in Phase 5 (migration), to avoid pre-committing the migration translation rules:
1670
+
1671
+ 1. **Resolved (2026-05-18):** Memory uses subfolders — `memory/lessons`, `memory/decisions`, `memory/references`, plus `protocols/` as a Memory subsection on disk (memory-protocols-subsection-of-memory). **2026-05-20:** added `memory/ui-patterns/` (`memory-ui-pattern`). **2026-05-21:** added `memory/ux-principles/` (`memory-ux-principle`). Console Memory surface: All / Lessons / Decisions / References / **UX Principles** / UI Patterns / Protocols.
1672
+ 2. The id format for migrated tasks — `HS-NNNN-<slug>` with `legacy_bd_id` preserved. Confirm the slug source (proposal: from `title:` lowercased, truncated to ~40 chars, dash-separated).
1673
+ 3. Whether to expose `body inline tags` (`#tag` inside prose) as queryable, or treat them as decorative-only. Proposal: queryable but frontmatter `tags:` wins on ties.
1674
+ 4. Whether the `daily/` notes folder should exist at all in v1 (it's not in L1's use cases). Proposal: include it for Builder convenience; cost is one folder.
1675
+
1676
+ ## Related specs
1677
+
1678
+ - heyspex-north-star — the L1 this entity model serves.
1679
+ - HS-DOC-architecture-foundations — the tech that implements the contract this document specifies.
1680
+ - uc-capture-intent — first-class typed capture and the phone-to-draft path; promotes drafts per §`draft` below.
1681
+ - uc-ingest-anywhere — Manual vs Agent ingest; drives optional `draft` LLM provenance fields and body footer conventions.
1682
+ - uc-voice-ingest-hey-spex — Hey Spex wake companion + shortcuts; optional `capture_channel: voice` fields above.
1683
+ - migration-from-bd — the L3 spec that uses the IDs/filenames defined here.
1684
+ - adr-0001-vault-format — the ADR that locks in markdown + YAML + wikilinks as the file format.
1685
+ - adr-0008-branch-brief-and-evidence-schema — the ADR that ratifies the `branch-brief` type and `evidence:` key.
1686
+ - adr-0012-roadmap-storage — `roadmap` type, `roadmap_manifest`, horizon backlog, ingest provenance fields.
1687
+ - plan-ingest-review-roadmap-workflow — default ingest → review → roadmap admission protocol.
1688
+ - ai-deliberation-gate — Gate 0 multi-model debate on drafts.
1689
+ - ingest-review-deliberation-roadmap-lifecycle — consolidated ingest/roadmap/council decision.
1690
+ - HS-ROADMAP-product-master — product phases + horizon manifest.
1691
+ - HS-7KOO-0022-EPIC-continuity-layer-gaps — the Continuity Layer epic these additions serve.
1692
+ - HS-BVZT-XBPJ-RES-bd-feature-inventory — the research spec that justifies each row of the schema.
1693
+ - memory-protocols-subsection-of-memory — Protocols are a Memory subsection in navigation; epics author protocols.
1694
+
1695
+ ## Change log
1696
+
1697
+ | Date | Author | Change |
1698
+ | --- | --- | --- |
1699
+ | 2026-05-14 | pwher (with claude opus 4.7) | Initial draft. Derived the entity-type set from L1 use cases plus the bd inventory. Locked in four dependency types (matching bd) and Obsidian-compatible wikilink resolution. Folder layout uses subfolders per type for human navigation; resolution is folder-agnostic. |
1700
+ | 2026-05-16 | pwher | §`draft`: companion/voice + optional spatial cursor/click hints (uc-voice-ingest-hey-spex §Spatial pointers). |
1701
+ | 2026-05-16 | pwher (with claude opus 4.7) | Added `branch-brief` entity type (folder `branches/`, ID `HS-BB-<slug>`, plan/actual + drift enum) and the cross-type `evidence:` array with inline shorthand. Per adr-0008-branch-brief-and-evidence-schema; closes HS-7E41/0052/0053 of HS-7KOO-0022-EPIC-continuity-layer-gaps. |
1702
+ | 2026-05-17 | pwher | Added `roadmap` type + `roadmap_manifest`; `future-considerations/` layout; draft `source_intent_hash` / `review_stage`; task `roadmap_admission` / `ingest_draft`. Per adr-0012-roadmap-storage. |
1703
+ | 2026-05-17 | pwher | Draft `deliberation_record` (ai-deliberation-gate); task `close_reason` enum + `superseded_by`; archive = filter not folder. |
1704
+ | 2026-05-17 | pwher | Draft `suggestions:` + `review_record:` per HS-EU98-GCGE-TASK-staged-draft-suggestion-schema; omit-when-empty rule. |
1705
+ | 2026-05-17 | pwher | `submission_kind`, `companion_dialogue[]`; companion `capture_channel` per companion-external-media-vs-refined-intent-council. |
1706
+ | 2026-05-17 | pwher | `capture_bundle_id`, `source_set[]` for companion multi-article bundle submit (companion-capture-inbox-and-bundles, HS-D1YU-OVFO-TASK-bundle-submit-composite-draft-source-set). |
1707
+ | 2026-05-17 | pwher | `triage_record` scout schema for Review inbox workshop (review-inbox-workshop-scout-overview). |
1708
+ | 2026-05-18 | pwher (with claude opus 4.5) | Added `in_review` status to universal enum — human gate between agent work and closure (HS-YYEO-FLX9-TASK-review-column-human-gate-before-done). |
1709
+ | 2026-05-20 | pwher | `canvases/diagrams/` + `canvases/as-built/` for spec diagram dual-surface (spec-diagram-design-vs-as-built-canvas). |
1710
+ | 2026-05-20 | pwher | Added `specs/canvases/` for JSON Canvas design files per adr-0013-canvas-vault-format-and-obsidian-interop. |
1711
+ | 2026-05-20 | pwher | Task/epic ID formats: random alphanumeric suffix for new specs (vault-id-assignment-protocol); legacy `HS-NNNN` migration IDs retained. |
1712
+ | 2026-05-21 | pwher | Added `planning-flow` type (`specs/flows/`); research `bound_epic` / `planning_flow`; epic `research` / `planning_flow` requirements (planning-flow-research-epic-lifecycle). |
1713
+ | 2026-05-22 | pwher + Claude Opus 4.7 (1M) | `idea` schema: `research_stage` removed (single binary `promoted_to`); body shape v2 — `# Core Idea` / `## Notes` / `## Concept` (≤1200 chars) / `<details>Details</details>`. See HS-KQ5O-IDFV-EPIC-unified-idea-workflow-v2 / HS-QOAC-IDFL-RES-unified-idea-workflow-v2. |
1714
+ | 2026-05-22 | pwher + Claude Opus 4.7 (1M) | **Capture rename:** `type: idea` → `type: capture`; new required `kind:` field (idea / bug-fix / clip / reference / question); folder `specs/captures/` → `specs/captures/`; ID prefix `HS-IDEA-*` → `HS-CAP-*` (legacy preserved in `legacy_idea_id:`); research `source_idea:` → `source_capture:`. Per HS-B2B8-DLQI-EPIC-capture-rename-migration-idea-to-capture; source rationale HS-ZKFN-1FXG-CAP-capture-as-universal-companion-verb-and-parent-term. |
1715
+ | 2026-05-24 | Claude Opus 4.7 (1M) | Capture schema: added `capture_status: raw \| expanded` (default `raw`). Flipped to `expanded` by MCP `executeUpdateCapture` on core_idea/concept/agent_expansion writes; force-corrected by console save-time linter when `# Core Idea` body has content. Backfilled across 21 existing captures by `scripts/backfill-capture-status.mjs`. Per HS-20PH-P2WA-TASK-b3-new-frontmatter-field-idea-status (B3 of HS-BDDT-Q1B7-EPIC-idea-capture-modal-v3-single-textbox-cle). |
1716
+ | 2026-05-25 | pwher (with Claude Opus 4.7) | **Business layer added.** New entity types `business-positioning` / `business-pricing` / `business-marketing` / `business-model` filed under `specs/business/{positioning,pricing,marketing,model}/`. IDs `HS-BIZ-POS-* / HS-BIZ-PRI-* / HS-BIZ-MKT-* / HS-BIZ-MOD-*`. Browsed in the console on a dedicated **Business** surface that mirrors Memory's category-rail layout. `positioning-2nd-brain-harness-for-specs` migrated from `memory/decisions/` (`type: memory-decision`) to `business/positioning/` (`type: business-positioning`); basename unchanged so wikilinks resolve. |
1717
+ | 2026-05-29 | pwher (with Claude Opus 4.8 [1m]) | **`milestone` entity type formalized.** Added folder `specs/milestones/`, Entity-types-table row, ID format `HS-MIL-<suffix>` (4+ uppercase; legacy 4-char mnemonics like `HS-RM22` valid), filename pattern `HS-MIL-<suffix>-<slug>.md`, and a full `#### milestone` schema subsection (`epics`, `target_date`, status enum, explicit "no scalar priority"). Disambiguated the legacy `kind: milestone` task discriminator from the new `type: milestone` entity; added the `milestone:` parent pointer to the `#### epic` schema; registered the new fields under Reserved frontmatter keys. Describes the 33 `HS-MIL-*` files already on disk (seed HS-RM22-IART-MIL-console-ia-restructure). Ratified by introduction lineage HS-VK7C-MILE-EPIC-milestone-entity-type-and-surfacing → HS-OFR5-M2J3-EPIC-introduce-milestones-as-highest-entity (in lieu of a standalone ADR); canonical hierarchy in [[spec-hierarchy]]. Per HS-9AUC-HRMZ-TASK-define-milestone-entity-type-in-entity-m. |
1718
+ | 2026-05-26 | pwher (with Claude Opus 4.7) | **`review` entity type formalized.** Added folder `specs/reviews/`, ID format `HS-REV-<suffix>` (suffix matches parent task/epic id 1:1), filename pattern `HS-REV-<suffix>-<slug>.md`, and full `#### review` schema subsection documenting the four required body sections (Acceptance verification / Reviewer notes / Memory candidates / Decision). Documents what CLAUDE.md §Session Completion + ~100 existing review files already implement; provides the authoritative L2 schema for console, indexer, MCP, and future CLI consumers. Per T1 of HS-3K55-0033-EPIC-review-sibling-testing-workflow (grounded against HS-19F9-TEST-MIL-api-testing-dual-path + HS-UKD6-CLR4-RES-close-out-review-screen-jtbd-architecture). |
1719
+ | 2026-05-29 | pwher (with Claude Opus 4.7 [1m]) | **Provenance edges formalized (capture → research → milestone).** Added new §Provenance edges section between §Dependencies and §Evidence linking with the cross-type cardinality table (Capture→Research M:N, Research→Milestone M:1 strict, Research→Epic spawned 1:N, Research→Epic augmented M:N). New frontmatter fields: `research.enhances` (M:1 required on closed), `research.sources_captures` (M:N), `research.spawned_epics` (1:N), `research.enhances_epics` (M:N rare), `research.split_into` / `research.split_from` (split-flow audit); `milestone.informed_by` (reverse of enhances); `capture.informs_research` (M:N, distinct from binary `promoted_to`); optional `epic.derived_from` (reverse of spawned_epics). Reserved-keys list updated. Per HS-HQRM-YJX6-TASK-research-enhances-exactly-one-milestone-validator-and-bidirectional-linkage of HS-OFR5-M2J3-EPIC-introduce-milestones-as-highest-entity. |
1720
+ | 2026-05-31 | pwher (with claude opus 4.7) | **`pillar` entity type added** (folder `specs/pillars/`, ID format `HS-PILR-<slug>` for canonical or `HS-PILR-<suffix>-<slug>` for customer-scoped, filename matches ID, `status: perpetual` non-terminal). New required fields on every epic: `pillar:` (single primary) and optional `secondary_pillars:` array. Type ratified by introduction lineage HS-EBIL-0011-EPIC-goal-hierarchy-l2-strategic-pillars-l3 following the milestone precedent (no standalone ADR for the entity itself); privacy-boundary for the Pillar Library learning loop is adr-0016-pillar-library-privacy-boundary. **`architecture-pillars.md` renamed to `architecture-foundations.md`** (ID `HS-L2-PILLARS` → `HS-L2-FOUNDATIONS`) to disambiguate from the new entity; 172 wikilinks updated in a single bulk pass. Three "pillar" usages now coexist: the `type: pillar` entity, the unrelated "Pillars 0–5" section names in HS-DOC-architecture-foundations, and the renamed "FIVE COMPONENTS" section in HS-OR8V-0010-EPIC-north-star-vision-first-class-entity (previously "FIVE PILLARS"). L3 Themes (per-epic teleological anchor) retired in the same resolution — collapsed into perpetual Pillars. See HS-AWDI-PILR-RES-pm-perpetual-container-terminology §"Resolution against existing HS-J6K8" for the full rationale. |
1721
+ | 2026-06-03 | claude opus 4.7 [1m] · session `local` (with pwher) | **`canonical:` universal field added; two `superseded` enum values deprecated.** Per evergreen-doc-architecture-srp-and-seo-pillars + canonicalization-arbiter-is-link-graph-not-policy + closed-trackable-specs-are-historical-record-not-living-docs: `canonical: true \| false` is now a universal frontmatter field on every spec type (pillars, L1/L2/L3 docs, HS-DOC-*, ADRs, protocols, memory specs, epics, tasks, milestones, reviews). `true` (or absent) = current/authoritative; `false` = historical record. Trigger event for the flip: plan-side specs at the in_review → closed transition (per [[session-close-protocol]] step 4); evergreen docs at supersession (per supersession-protocol). `status: parked` / `status: deferred` keeps `canonical: true`. **Two enum values deprecated forward-only** per superseded-enum-values-deprecated-use-canonical-false-and-superseded-by: `status: superseded` removed from the ADR-status enum; `superseded` removed from the close_reason enum. Both were redundant with `canonical: false` + `superseded_by:`. Forward-only retirement — existing 11 specs (8 ADRs + 3 plan-side) keep their legacy values as historical record per immutability. New writes use `canonical: false` + `superseded_by:` instead. Added the `canonical:` field to the ADR and task schemas; expanded the `superseded_by:` description to clarify it's the pointer (vs. `canonical:` which is the state). |
1722
+ | 2026-06-03 | claude opus 4.7 [1m] · session `local` (with pwher) | **`## Lifecycle classes` section added; `research` schema de-coupled from `status: closed`.** New top-level section between §Entity types and §Identity, IDs, and filenames classifying all 24 types into four lifecycle classes — **Evergreen** (`l1`/`l2`/`l3`/`adr`/`pillar`/`protocol`/`memory-*`/`research`/`business-*`), **Trackable plan-side** (`task`/`epic`/`milestone`/`review`/`branch-brief`/`planning-flow`), **Single-shot ingest** (`capture`/`draft`/`daily`), **Per-instance** (`roadmap`). Closes the discoverability gap that produced the research-is-evergreen incident: a reader asking *"can I flip this file's status to `in_review`?"* now has one canonical place to look. Cross-references research-is-evergreen, closed-trackable-specs-are-historical-record-not-living-docs, canonicalization-arbiter-is-link-graph-not-policy, [[session-close-protocol]]. **Research schema (`#### research`) amended**: the `enhances:` field gate changed from "REQUIRED on `status: closed`" to "REQUIRED when set" — bidirectional consistency is enforced whenever the field is present, independent of status. Added a one-line note above the schema declaring research evergreen with a pointer to research-is-evergreen. The matching validator update in `scripts/validate-vault-ids.mjs` is on the deferred list — schema-vs-validator drift is acknowledged in the schema comment until that catches up. |
1723
+ | 2026-06-13 | claude opus 4.8 [1m] · session `local` (with pwher) | **`diagnose` entity type added** (folder `specs/diagnoses/`, ID `HS-DIAG-<suffix>`, filename `HS-DIAG-<suffix>-<slug>.md`). A user-reported bug fix as first-class executable work — standalone by default (exempt from the epic→milestone parent rule), optional `parent:` (epic or milestone), required `severity:` (error/warning/info) that maps onto the scheduler's existing priority signals, optional `blocks:` gating edges. Surfaces on the Resolution surface as a derived `kind: "bug"` row that links back to the diagnose. Added entity-types-table row, ID-format row, filename row, `#### diagnose` schema subsection, and Trackable-plan-side lifecycle classification. Ratified by introduction lineage HS-OXBS-BFIX-MIL-bug-fix-diagnose-lane (milestone-precedent, no standalone ADR). Wiring: `vault.ts` (TYPE_TO_FOLDER + KIND_LINKAGE + body skeleton + generateId), `vault-id.ts`, `vault-rules.mjs` (KIND_RULES/KIND_ORDER), `validator.ts`, `validate-vault-ids.mjs`. Per HS-AO2F-1KZK-RES-bug-fix-resolution-task-entity-model-and. |
1724
+ | 2026-06-23 | Cursor · session `local` | **Delegation handoff envelope + task split lineage.** Task frontmatter: `split_of`, `split_index`, `handoff_id` (option B from HS-DTL1-CX5K-CAP-task-split-handoff-when-context-runs-low). Handoff envelope SSOT is body (`## Handoff envelope` + fenced yaml) per `schemas/handoff-envelope.schema.json`; validated by `handoff-envelope.mjs` + `validate-vault-ids.mjs`. Engine rules in `vault-rules.mjs` (`TASK_SPLIT_*`). Per HS-PD6K-NCH5-TASK-handoff-envelope-schema-and-split-child / HS-HXE2-YQ5A-EPIC-delegation-defer-work-from-one-agent-to. |
1725
+ | 2026-06-23 | Cursor · session `local` | **`owned_paths` / `affects_paths` for parallel-agent stomp-detection.** New `#### agent` schema subsection documents `owned_paths:` (machine-checkable file globs on agent identities). Task schema adds optional `affects_paths:` (advisory module hints on trackable specs). Entity-types table + reserved-keys list updated. Per HS-ZMAQ-M15C-TASK-declare-owned-path-globs-on-agent-identi / HS-8PNX-Q1KC-EPIC-lanes-and-stomp-detection-for-parallel-a. |
1726
+ | 2026-07-05 | Claude Sonnet 5 [1m] · session `local` (with pwher) | **`research.produces_diagnose` → `diagnose.derived_from` edge added (research → diagnose intake).** Bugs found during a research investigation now route into the Resolution lane without new diagnose machinery: `research.produces_diagnose:` (optional, list of wikilinks to `HS-DIAG-*`, mirrors `spawned_epics`) reverse-linked by `diagnose.derived_from:` (optional, single wikilink, mirrors `epic.derived_from`). Written by the new `heyspex_promote_research_to_diagnose` tool (`vendor/heyspex-mcp/promote-research-to-diagnose.mjs`), the research-side sibling of `heyspex_promote_capture_to_diagnose`. Also documented `diagnose.promoted_from:` (existing capture-side back-reference, previously undocumented in this schema subsection). Provenance cardinality table + reserved-keys list updated; validated by the `type: research` block of `schemas/heyspex-frontmatter.schema.json`. Per HS-W5VP-MSHY-TASK-research-diagnose-link-send-bugs-to-the of HS-0FTO-MSHY-EPIC-research-to-diagnose-link-send-bugs-foun. |
1727
+ | 2026-07-05 | Claude Sonnet 5 [1m] · session `local` (with pwher) | **`code-audit` findings-table format + closure gate.** New `##### Code-audit findings table` subsection under `#### research` documents the required 4-column body table (`Finding \| Severity \| Action (diagnose\|task\|reference) \| Exit (open\|closed)`) and its three defined exits: bug→diagnose (reuses `heyspex_promote_research_to_diagnose`/`research.produces_diagnose`, no new tooling), cleanup→task (new `task.discovered_from:` reverse pointer to the source research doc), non-actionable→reference (the row itself is the exit). Enriched the existing `task.discovered_from:` doc comment to name this use. Added `discovered_from` to the `type: task` block of `schemas/heyspex-frontmatter.schema.json` (previously documented in entity-model.md only, unenforced). Validator rules in `vault-rules.mjs`: `CODE_AUDIT_FINDINGS_MALFORMED` (no/malformed table) and `CODE_AUDIT_FINDINGS_OPEN_ON_CLOSE` (the closure gate — a `closed`/`review_pending` code-audit doc with any `Exit: open` row) — both warn during a new `CODE_AUDIT_GRACE_END` (2026-08-15, mirrors `RESEARCH_TYPE_GRACE_END`) so pre-existing docs like `HS-ZHAB-RES-doc-authoring-contract-audit-required-st` (no `research_type:` set, predates this format) aren't hard-failed, then error. Per HS-7D0I-WL88-TASK-code-audit-research-type-with-a-defined of HS-BI9D-WL88-EPIC-code-audit-research-type-with-a-defined. |
1728
+ | 2026-08-14 | Claude Code (Fable 5) · session `5785e74c` (with pwher) | **Iteration lineage — third relation tier added.** New §Iteration lineage section (between §Provenance edges and §Evidence linking): `iterates_on:` (list of wikilinks, newer-doc-only, one-edit-on-source like amends; computed reverse map `iteratedBy`) expresses "re-attempts an outcome the target shipped but did not fully deliver" — the case supersession (would falsify round 1) and amends (nothing is contradicted) both misdescribe. `outcome:` (`met` \| `partially-met` \| `not-met`) + `outcome_note:` — reviewer-set at close on trackable docs, never inferred, closed-doc-only (not echoed onto round 2). Required `## Related / lineage` resolved-wikilink table on promoted docs with role vocabulary. Invariants: both docs stay `canonical: true`, round 1 keeps `status: closed`, round 2 takes its own family key. Dependencies table gained the Iterates-on row; Reviewer-close bullet gained the outcome write. Field-name + echo decisions resolved at breakdown (flagged for owner Walk). Per HS-VGQV-T9T6-TASK-entity-model-iterates-on-relation-tier of HS-O97P-T9T6-EPIC-iteration-loop-lineage-relation-tier, from HS-3TZX-T9T6-RES-iteration-loop-lineage-and-capture Part A. |