Skip to content

Term entity type, generated glossary, and the docs generate surface

Status: open/accepted

  • A new term entity type (prefix TM, 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.md becomes a build artifact — generated from term entities, never hand-edited — and moves from docs/planning/glossary.md to docs/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, with site named explicitly (D-0010-deterministic-site-assembly). Subsumes index generate; amends the D-H7FS-op-substrate-surface noun table (indexdocs). (§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.mjs ports into the docs service as first-class lib code and is deleted. (§4, Migration)
  • A term’s Definition section carries the ^summary block; the glossary table transcludes it, so the table never drifts from the entity — the mechanism index.md already uses. (§2)
  • The type is named term, not definition — “definition” already names the per-entity companion doc (definition.md, S-0005), and the D prefix 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)

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.

AspectChoice
Type nameterm — one instance is one vocabulary term
Packageplugin/lib/model/entities/term/
Instances dirdocs/planning/terms/ (pluralize("term") — zero wiring)
PrefixTM (T taken by Task; G reserved for Goal per D-0002-entity-identifier-shape)
NumberingIncrementing (TM-0001) — curated, low-volume catalog per D-0002’s split
FilenameTM-NNNN-<slug>.md, e.g. TM-0001-substrate.md
WikilinkTM-NNNN-<slug>, piped for display: substrate
Create opsdlc term create [<slug>] --title <term> (mirrors standard create; slug derived from title when omitted)

Frontmatter — common fields per the shared CommonFrontmatter schema (plugin/lib/model/entities/_common.ts), plus:

FieldRequired?ShapeDefaultNotes
typerequiredtermValidator-dispatch tag
idrequired^TM-[0-9A-Z]{4}$Immutable; matches filename
titlerequiredstringThe term itself, canonical capitalization (Substrate)
aliasesoptionallist of strings[]Alternate names (protocol adapter for Adapter, deterministic tail for Tail); rendered beside the term in the glossary
statusrequiredenum (see Lifecycle)open/active
relatedoptionallist of wikilinks[]The Source column: the decisions/standards/principles where the term is normative

Body grammar (lenient section order, unknown sections allowed):

SectionRequired?Notes
Definitionrequired1–3 sentences; the defining paragraph carries ^summary for transclusion. Table-cell discipline: one paragraph, no lists, no headings
ContrastoptionalWhat the term is not (adapter ≠ harness)
NotesoptionalElaboration that doesn’t fit one cell (the reoccurring-workflow session-bounds prose)
ReferencesoptionalInbound/outbound links

Lifecycle — values chosen so the index service’s existing buckets apply verbatim (bucketFor unchanged):

StatusMeaningGlossary table
open/activeAdopted vocabulary; the default — a term is captured because it is in useActive
open/draftProposed vocabulary, not yet settledEmerging
closed/retiredNo longer used; kept for link stabilityObsoleted

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 by title (case-insensitive; ids carry identity, not order). Term cell is a piped wikilink, aliases parenthetical after the term: [Substrate](/glossary/substrate/). Definition cell transcludes TM-0001-substrate. Source cell renders related:.
  • Active terms in the main table; open/draft / closed/retired in 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:

TodayDestination
Adapter-vs-harness warning paragraphContrast sections of the Adapter and Harness terms; one-line pointer stays in the template preamble
”Op classes” paragraphNotes on the Op term
”reoccurring workflow” H2Its own term instance (TM-NNNN-reoccurring-workflow)

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.

AspectChoice
Opsdlc 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)
Artifactsindex (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
Serviceplugin/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
SurfaceAmends 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, and docs/references.md; one invocation point means the artifacts can never drift apart.
  • index.md gains a glossary row in Start here — a pointer, not an inlined table.
  • The index skill 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.mjs generates the site’s reference pages deterministically today but is a script straggler — stdlib Node outside plugin/lib, unregistered, untested, invoked directly by a skill. It is ported into the docs service (TypeScript, tests, registry-exposed as the site artifact) and the .mjs script is deleted — the same script-to-substrate move the op-substrate sweep applied to plugin/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 generate owns page generation (writing content files); the Astro build (bun run build:doc-site) stays a build step. /dev-update-docs remains the LLM head for semantic drift in hand-written pages; its deterministic tail becomes docs generate.
ChoiceOptionsTakenWhy
Type namedefinition (B-T2DB’s working name), glossary, termterm”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
PrefixTM, TE, GLTMReads as “term”; single letters adjacent are taken (T Task) or reserved (G Goal)
Numberingincrementing, base-36incrementingCurated, low-volume, human-cited — D-0002’s incrementing criteria; Capability (C-NNNN) precedent
Artifact locationkeep docs/planning/glossary.md, move to docs/glossary.mdmoveBuild artifacts out of source-of-truth space; index.md precedent; basename-resolved wikilinks make the move free
Generator surfacenew glossary generate op; extend index generate; broaden to docs generatedocs generateOne 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 selectionrepeatable --artifact flag, variadic positionalpositionalThe artifact is the verb’s natural object — docs generate glossary reads single-parse and makes scoped test runs cheap
Site generation homekeep regen.mjs (op shells out), port into the docs serviceportA 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 renderingtransclude ^summary, inline extracted texttransclude (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)
  • 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-level glossary links 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.ts PREFIXES gains the row, which also turns the commit-time identifier gate on for docs/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 index noun becomes docs (docs generate, positional artifact selection). Noun count unchanged; the rename codemod is small because index generate shipped recently.
  • Site generation moves onto the substrate: site/scripts/regen.mjs ports into the docs service and is deleted — the last generator script outside plugin/lib, extending the script-to-substrate principle past plugin/scripts/ into site/. /dev-update-docs narrows to semantic verification of hand-written pages; its deterministic tail becomes sdlc 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.

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

  • plugin/lib/model/entities/term/schema.ts (TM id pattern, status enum, aliases, plus the body contract: Definition required with ^summary guidance, Contrast/Notes/References optional), definition.md per S-0005, body-template.eta
  • ops/create.tssdlc term create <slug> --title <term>, incrementing mint, delegating to runCreate
  • identifier.ts PREFIXES row { 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 setup creates docs/planning/terms/; entities audit/validate/migrate discover the type; project-check package consistency passes
  • Rename the service (plugin/lib/services/index/plugin/lib/services/docs/) and the op (index generatedocs generate with positional artifact selection, output { artifacts: [{ name, path, wrote }] }); sweep callers; amend the D-H7FS-op-substrate-surface noun table
  • buildGlossaryData(planningDir) (alphabetical by title, bucketFor reused) + glossary_template.eta with the fixed preamble
  • index_template.eta Start here gains the glossary row
  • Tests: glossary assembly units + byte-stable idempotency on second run (extend the existing generator tests)
  • index skill 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; run sdlc docs generate; docs/glossary.md is born generated
  • Re-point heading deep-links in P-0001 (×1) and P-0002 (×3) to reoccurring workflow
  • Confirm doc-level glossary links (D-0001, D-0007, P-0008 and frontmatter related:) 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.mjs into the docs service as the site artifact implementation (TypeScript under plugin/lib/services/docs/, registry-exposed, byte-stable idempotency tests); delete the .mjs script
  • 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 becomes sdlc 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
  • 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.
  • Retirement metadata: does closed/retired need a retirement_note or superseded_by-style field? Deferred until a term actually retires.

← Back to Decisions