Term entity type, generated glossary, and the docs generate surface
Status: open/accepted
Summary
Section titled “Summary”- A new
termentity type (prefixTM, incrementing ids,docs/planning/terms/) makes each glossary entry a first-class entity: schema, body manifest, template,sdlc term create, and automatic audit/validate/migrate/setup coverage. (§1, §2) glossary.mdbecomes a build artifact — generated from term entities, never hand-edited — and moves fromdocs/planning/glossary.mdtodocs/glossary.md, beside the other generated front-door,index.md. (§3)- The generator surface becomes
sdlc docs generate [<artifact>…]— one op, one deterministic pass over the generated-docs family: the wiki index (docs/index.md), the glossary (docs/glossary.md), and the docs site’s generated pages. Artifacts select positionally for scoped runs (sdlc docs generate glossary); bare invocation regenerates the committed wiki artifacts, withsitenamed explicitly (D-0010-deterministic-site-assembly). Subsumesindex generate; amends the D-H7FS-op-substrate-surface noun table (index→docs). (§4) - The glossary ships into the docs site as a generated page with
definitions inlined from term entities — and site generation moves
onto the substrate:
site/scripts/regen.mjsports into the docs service as first-class lib code and is deleted. (§4, Migration) - A term’s Definition section carries the
^summaryblock; the glossary table transcludes it, so the table never drifts from the entity — the mechanismindex.mdalready uses. (§2) - The type is named
term, notdefinition— “definition” already names the per-entity companion doc (definition.md, S-0005), and theDprefix is taken. Supersedes the working name in B-T2DB-definition-entity-for-glossary. (Options considered) - Migration is the dogfood: ~13 instances minted from today’s glossary rows, the hand-written file deleted, deep links re-pointed at term entities, D-0002/D-ORMG prefix registries amended. (Migration)
Context
Section titled “Context”The glossary (docs/planning/glossary.md, PR #218) was an interim
home by its own admission: its preamble defers to “a definition
entity type” and B-T2DB-definition-entity-for-glossary holds the
deferral.
Every other planning artifact is entity-shaped per
D-0004-entity-definition-architecture: schema’d frontmatter, body
manifest, immutable id, audit/migrate coverage, and a generated
representation in index.md. The glossary is the last hand-maintained
roster. As a markdown table it has no per-term identity to link, no
lifecycle, no validation, and drifts silently — an empty trailing row
sits in the table today, and the index doesn’t know the glossary
exists.
The vocabulary itself is load-bearing. Principles deep-link into it
(reoccurring workflow in P-0001 and
P-0002); decisions cite it in related:. Terms deserve the same
identity and link stability as the entities that cite them.
Decision
Section titled “Decision”1. The term entity type
Section titled “1. The term entity type”| Aspect | Choice |
|---|---|
| Type name | term — one instance is one vocabulary term |
| Package | plugin/lib/model/entities/term/ |
| Instances dir | docs/planning/terms/ (pluralize("term") — zero wiring) |
| Prefix | TM (T taken by Task; G reserved for Goal per D-0002-entity-identifier-shape) |
| Numbering | Incrementing (TM-0001) — curated, low-volume catalog per D-0002’s split |
| Filename | TM-NNNN-<slug>.md, e.g. TM-0001-substrate.md |
| Wikilink | TM-NNNN-<slug>, piped for display: substrate |
| Create op | sdlc term create [<slug>] --title <term> (mirrors standard create; slug derived from title when omitted) |
2. Frontmatter and body shape
Section titled “2. Frontmatter and body shape”Frontmatter — common fields per the shared CommonFrontmatter schema
(plugin/lib/model/entities/_common.ts), plus:
| Field | Required? | Shape | Default | Notes |
|---|---|---|---|---|
type | required | term | Validator-dispatch tag | |
id | required | ^TM-[0-9A-Z]{4}$ | Immutable; matches filename | |
title | required | string | The term itself, canonical capitalization (Substrate) | |
aliases | optional | list of strings | [] | Alternate names (protocol adapter for Adapter, deterministic tail for Tail); rendered beside the term in the glossary |
status | required | enum (see Lifecycle) | open/active | |
related | optional | list of wikilinks | [] | The Source column: the decisions/standards/principles where the term is normative |
Body grammar (lenient section order, unknown sections allowed):
| Section | Required? | Notes |
|---|---|---|
| Definition | required | 1–3 sentences; the defining paragraph carries ^summary for transclusion. Table-cell discipline: one paragraph, no lists, no headings |
| Contrast | optional | What the term is not (adapter ≠ harness) |
| Notes | optional | Elaboration that doesn’t fit one cell (the reoccurring-workflow session-bounds prose) |
| References | optional | Inbound/outbound links |
Lifecycle — values chosen so the index service’s existing buckets
apply verbatim (bucketFor unchanged):
| Status | Meaning | Glossary table |
|---|---|---|
open/active | Adopted vocabulary; the default — a term is captured because it is in use | Active |
open/draft | Proposed vocabulary, not yet settled | Emerging |
closed/retired | No longer used; kept for link stability | Obsoleted |
3. The generated glossary artifact
Section titled “3. The generated glossary artifact”docs/glossary.md is a build artifact: never hand-edited, always
regenerated, byte-stable across re-runs. It moves out of
docs/planning/ because that tree is source-of-truth space; generated
front-door artifacts live at docs/ (index.md precedent). Wikilinks
resolve by basename, so every existing glossary reference
survives the move unedited.
Shape:
- A fixed preamble owned by the template (the front-door note and the
adapter-vs-harness caution pointer), mirroring
index_template.eta’s Narrative block. - One table —
Term | Definition | Source— sorted alphabetically bytitle(case-insensitive; ids carry identity, not order). Term cell is a piped wikilink, aliases parenthetical after the term:[Substrate](/glossary/substrate/). Definition cell transcludesTM-0001-substrate. Source cell rendersrelated:. - Active terms in the main table;
open/draft/closed/retiredin the follow-up Emerging / obsoleted table, status shown, omitted when empty — the index’s bucketing, reused.
Hand-written prose in today’s file relocates into entities:
| Today | Destination |
|---|---|
| Adapter-vs-harness warning paragraph | Contrast sections of the Adapter and Harness terms; one-line pointer stays in the template preamble |
| ”Op classes” paragraph | Notes on the Op term |
| ”reoccurring workflow” H2 | Its own term instance (TM-NNNN-reoccurring-workflow) |
4. Generator: sdlc docs generate
Section titled “4. Generator: sdlc docs generate”The generator grows a richer surface: the index noun broadens to
docs — the family of deterministically generated documentation
artifacts — and sdlc docs generate subsumes index generate.
| Aspect | Choice |
|---|---|
| Op | sdlc docs generate [<artifact>…] — variadic positional (docs generate glossary, docs generate index site); bare invocation regenerates the committed wiki artifacts, site named explicitly (D-0010-deterministic-site-assembly) |
| Artifacts | index (docs/index.md), glossary (docs/glossary.md), references (docs/references.md, joined per D-0009-reference-entity-and-docs-appendix), site (the docs site’s generated pages) |
| Output | { artifacts: [{ name, path, wrote }] }; --dry-run covers every artifact |
| Service | plugin/lib/services/index/ renames to plugin/lib/services/docs/; buildGlossaryData + glossary_template.eta join buildIndexData + index_template.eta. Same plain-data-to-template split, same normalizeWhitespace idempotency contract |
| Surface | Amends D-H7FS-op-substrate-surface: the index | generate row becomes docs | generate. Visible-noun count stays 11; the caller codemod is small because the op is young |
Wiki artifacts:
- One pass writes
docs/index.md,docs/glossary.md, anddocs/references.md; one invocation point means the artifacts can never drift apart. index.mdgains aglossaryrow in Start here — a pointer, not an inlined table.- The
indexskill stays the LLM head, renamed alongside the noun; its SKILL.md names the artifact family.
Site as deterministic downstream:
- Site generation joins the same family as first-class substrate
code, not a script.
site/scripts/regen.mjsgenerates the site’s reference pages deterministically today but is a script straggler — stdlib Node outsideplugin/lib, unregistered, untested, invoked directly by a skill. It is ported into the docs service (TypeScript, tests, registry-exposed as thesiteartifact) and the.mjsscript is deleted — the same script-to-substrate move the op-substrate sweep applied toplugin/scripts/. - First foray: a generated glossary page in the site. The site renders no Obsidian transclusions, so the site generator inlines each term’s Definition text from the entity — same source, second projection.
- Boundary:
docs generateowns page generation (writing content files); the Astro build (bun run build:doc-site) stays a build step./dev-update-docsremains the LLM head for semantic drift in hand-written pages; its deterministic tail becomesdocs generate.
Options considered
Section titled “Options considered”| Choice | Options | Taken | Why |
|---|---|---|---|
| Type name | definition (B-T2DB’s working name), glossary, term | term | ”Definition” collides with the S-0005 companion doc definition.md — one word, two meanings inside the same package dir; D/DEF prefix awkward. A glossary instance isn’t a glossary, it’s a term. term parses single-pass |
| Prefix | TM, TE, GL | TM | Reads as “term”; single letters adjacent are taken (T Task) or reserved (G Goal) |
| Numbering | incrementing, base-36 | incrementing | Curated, low-volume, human-cited — D-0002’s incrementing criteria; Capability (C-NNNN) precedent |
| Artifact location | keep docs/planning/glossary.md, move to docs/glossary.md | move | Build artifacts out of source-of-truth space; index.md precedent; basename-resolved wikilinks make the move free |
| Generator surface | new glossary generate op; extend index generate; broaden to docs generate | docs generate | One deterministic process for the whole generated-docs family. An index op writing the glossary and site pages stops being truthful (D-H7FS naming rule 2); docs is the honest noun. A second op is a second thing to forget to run |
| Artifact selection | repeatable --artifact flag, variadic positional | positional | The artifact is the verb’s natural object — docs generate glossary reads single-parse and makes scoped test runs cheap |
| Site generation home | keep regen.mjs (op shells out), port into the docs service | port | A script straggler outside plugin/lib — unregistered, untested — is the shape the op-substrate sweep exists to eliminate; site generation becomes lib code with the same idempotency tests as the wiki artifacts |
| Definition rendering | transclude ^summary, inline extracted text | transclude (wiki) + inline (site) | House pattern (index.md summary cells); single source, zero drift. The site projection inlines the same Definition text since it renders no transclusions (§4) |
Consequences
Section titled “Consequences”- Vocabulary changes become entity operations:
sdlc term create, edit the instance, regenerate. Review lands on one term file, not a shared table everyone merge-conflicts on. - Per-term wikilinks become possible (
[substrate](/glossary/substrate/)), giving prose a stable anchor per concept. Not mandated — doc-levelglossarylinks stay valid. - Heading deep-links into the glossary file
(
…) become unsupported; they re-point at term entities in the migration. - Amends D-0002-entity-identifier-shape: prefix table gains
TM | Term | Incrementing. Amends D-ORMG-data-model: roster mirror gains the type.identifier.tsPREFIXESgains the row, which also turns the commit-time identifier gate on fordocs/planning/terms/. - Resolves B-T2DB-definition-entity-for-glossary (under the new name); the backlog item closes as promoted when the migration lands.
- Amends D-H7FS-op-substrate-surface: the
indexnoun becomesdocs(docs generate, positional artifact selection). Noun count unchanged; the rename codemod is small becauseindex generateshipped recently. - Site generation moves onto the substrate:
site/scripts/regen.mjsports into the docs service and is deleted — the last generator script outsideplugin/lib, extending the script-to-substrate principle pastplugin/scripts/intosite/./dev-update-docsnarrows to semantic verification of hand-written pages; its deterministic tail becomessdlc docs generate site. - The index service’s identity broadens from “the index generator” to “the docs-artifacts generator” (and renames to match). Docs that describe it are swept in the migration.
Migration
Section titled “Migration”The dogfood refactor: SDLC’s own glossary moves onto the new model. Phases are ordered by dependency; each is roughly one task, extracted from this section when planning starts. Phases 1–2 are pure addition (no behavior change until 3).
Phase 1 — substrate: the term package
Section titled “Phase 1 — substrate: the term package”-
plugin/lib/model/entities/term/—schema.ts(TM id pattern, status enum,aliases, plus the body contract: Definition required with^summaryguidance, Contrast/Notes/References optional),definition.mdper S-0005,body-template.eta -
ops/create.ts—sdlc term create <slug> --title <term>, incrementing mint, delegating torunCreate -
identifier.tsPREFIXESrow{ TM, Term, terms, incrementing } - Amend D-0002-entity-identifier-shape prefix table and D-ORMG-data-model roster mirror
- Verify zero-wire coverage:
sdlc project setupcreatesdocs/planning/terms/;entities audit/validate/migratediscover the type; project-check package consistency passes
Phase 2 — generator: docs generate
Section titled “Phase 2 — generator: docs generate”- Rename the service (
plugin/lib/services/index/→plugin/lib/services/docs/) and the op (index generate→docs generatewith positional artifact selection, output{ artifacts: [{ name, path, wrote }] }); sweep callers; amend the D-H7FS-op-substrate-surface noun table -
buildGlossaryData(planningDir)(alphabetical by title,bucketForreused) +glossary_template.etawith the fixed preamble -
index_template.etaStart here gains theglossaryrow - Tests: glossary assembly units + byte-stable idempotency on second run (extend the existing generator tests)
-
indexskill renamed/updated to name the artifact family; per-skill doc refreshed
Phase 3 — corpus migration (the dogfood)
Section titled “Phase 3 — corpus migration (the dogfood)”- Mint term instances from today’s rows: Substrate, Harness, Adapter, Op, Registry, Entity, Service, Head, Tail, Deterministic composition, Workflow, OpError, Reoccurring workflow (~13)
- Relocate prose per §3: Contrast sections, Op Notes, reoccurring-workflow body
- Delete
docs/planning/glossary.md; runsdlc docs generate;docs/glossary.mdis born generated - Re-point heading deep-links in P-0001 (×1) and P-0002 (×3) to
reoccurring workflow - Confirm doc-level
glossarylinks (D-0001, D-0007, P-0008 and frontmatterrelated:) resolve to the new location — no edits expected - Close B-T2DB-definition-entity-for-glossary as promoted, pointing here
Phase 4 — site: port regen.mjs, glossary page
Section titled “Phase 4 — site: port regen.mjs, glossary page”- Port
site/scripts/regen.mjsinto the docs service as thesiteartifact implementation (TypeScript underplugin/lib/services/docs/, registry-exposed, byte-stable idempotency tests); delete the.mjsscript - Generated glossary page in the site from term entities (Definition text inlined — the site renders no transclusions); sidebar entry
- Re-point
/dev-update-docs: its deterministic tail becomessdlc docs generate site; the skill keeps semantic verification of hand-written pages and the Astro build - Cross-link audit so the site reflects the new type and artifacts; changelog entry
Out of scope
Section titled “Out of scope”- The other planned-but-unshipped types in D-0002’s table (Goal, Constraint, Contract, …) — this decision ships one type.
- Cross-project term sharing (a consuming project’s glossary) — terms here are SDLC’s own vocabulary; consuming projects get the type for free via the plugin but no migration.
- Mandating per-term wikilinks in existing prose — links migrate only where the old deep-link form breaks.
Open questions
Section titled “Open questions”- Retirement metadata: does
closed/retiredneed aretirement_noteorsuperseded_by-style field? Deferred until a term actually retires.
References
Section titled “References”- B-T2DB-definition-entity-for-glossary — the deferral this resolves.
- D-0002-entity-identifier-shape — identifier grammar; prefix table amended here.
- D-0004-entity-definition-architecture — the entity-package contract the term type follows.
- D-0007-deterministic-op-substrate — the op registry
term createanddocs generatelive in. - D-H7FS-op-substrate-surface — the command surface; its
indexnoun row becomesdocshere. - D-ORMG-data-model — entity roster; gains the type.
- S-0004-sdlc-cli-llm-head-deterministic-tail — the head/tail split the generator follows.
glossary— the artifact this decision re-founds.