Skill: glossary
Lazy utility that fixes the meaning of a domain term in CONTEXT.md the moment it first surfaces, so its sense doesn't drift across the pipeline. For each term it captures a one-sentence canonical definition and — when the word is ambiguous — a NOT-reference naming the concept it's confused with. Runs anytime, with no upstream gate: a single term mid-interview, an undefined-term finding clarify resolves mid-sweep, or a batch handed over by specify. The output feeds specify (role + domain-term names) and design (invariants), which treat ## Glossary as canonical and override anything that contradicts it.
This is a capture utility, not a Socratic stage — it does not run the shared Socratic loop or critic. The one shared dependency is question phrasing:
→ ../_shared/ask-style.md
Term definitions follow artifact_language — the ## Glossary heading and the other H2s stay English, the frontmatter stays the template's (status: Living + updated_at: — never write artifact_language or any other settings key into the file) → ../_shared/artifact-language.md.
Owner
Whoever drives the conversation — anyone who spots ambiguity. Tech Lead approves the canonical form when a term is contested.
Inputs
<term> — the domain word/phrase to fix. If not given, ask for it.
- (Optional)
<slug> — feature slug; targets docs/features/<slug>/CONTEXT.md. Absent → repo-root CONTEXT.md.
- (Optional)
pending_glossary_terms — a batch handed over by specify after it writes the spec. Process each term in turn.
- (Optional) an
undefined-term finding from clarify — clarify invokes this skill mid-stream, one term at a time, the moment such a finding is Resolved (it never invents a meaning inline; the canonical definition lands here).
- (Optional) proposed phrasings already surfaced in an interview/brainstorm — offer them as definition options instead of asking blank.
Protocol
- Pick the target (the two-level contract). Root
CONTEXT.md = project-wide terms (meaningful across features); docs/features/<slug>/CONTEXT.md = feature-scoped terms (meaningful only inside that feature). If <slug> is given → the per-feature file; otherwise → repo-root. A term lives in exactly one of the two files — never split or duplicate it across levels. Readers (specify, clarify, design, api) read both files; on a conflict the per-feature entry wins.
- Generic-term filter. Reject words that name infrastructure or transport rather than the business domain (e.g. HTTP, queue, cache, the datastore, the broker, a framework). Refuse: «
<term> is technical, not a domain word — its choice belongs in the SAD or an ADR, not the glossary». Continue only for genuine domain vocabulary.
- Bootstrap (lazy).
test -f <target>. Missing → copy ./templates/CONTEXT.md to <target>. Present → read it.
- Conflict check — both levels.
grep -i "^- <term>" <target> AND the other level's file when it exists (root CONTEXT.md ↔ docs/features/<slug>/CONTEXT.md).
- Found (either level), identical sense → STOP, report «already in the glossary» + which file holds it — never duplicate a term across levels.
- Found (either level), different sense → escalate via
AskUserQuestion (phrasing per ../_shared/ask-style.md): «<term> is already defined as <existing> in <file> — same concept or a different one?». If different: a genuinely feature-scoped narrowing goes to the per-feature file (readers let it win there); otherwise propose a disambiguating pair of names (e.g. a billing-scoped vs a runtime-scoped variant) and fix both.
- Not found in either → continue.
- Ask the canonical definition — one
AskUserQuestion: «Define <term> in this domain, in one sentence». Offer interview/brainstorm phrasings as options when available; otherwise free text.
- Ask the NOT-reference — one
AskUserQuestion: «Which concept does <term> get confused with, so a future reader doesn't mix them up?». No plausible homonym → None.
- Compose one line.
- <term> — <one-sentence definition>. NOT <confused concept + how it differs>. — or, when step 6 = None, - <term> — <definition>.
- Append under
## Glossary. Read the file, insert the line in ## Glossary (alphabetical if the section is already sorted, else at the end). Never rewrite existing entries.
- Prune empty H2s. On a fresh bootstrap, delete
## Invariants / ## Out of scope if they hold no real content — only ## Glossary is mandatory. (A genuine invariant or out-of-scope note goes in its section, never as implementation detail.)
- Structural self-check — per
../_shared/self-check.md: re-read the target file(s) from disk and verify 3 items: (1) the term appears exactly once across both levels combined (grep root CONTEXT.md + docs/features/<slug>/CONTEXT.md — no duplicate, no split); (2) the entry matches the format - <term> — <definition>. (with the optional NOT … tail); (3) no empty H2 sections remain. Fix + re-check ≤2 cycles; surface anything unresolved.
- Stamp + commit + handoff. Set
updated_at: <today> in the frontmatter. Propose context: + <term> (or context: + <term>, <term2> for a batch). May fold into the caller's intake/spec commit. Then emit the stage-handoff block per ../_shared/handoff.md (utility variant) — What I did (incl. «self-check: 3/3 pass») + Review (CONTEXT.md) + Run next: resume your backbone stage (e.g. /sdd:design <slug>); /clear optional.
Definition of Done
<target> exists and contains <term> under ## Glossary in the «one-sentence canonical + optional NOT-reference» format.
- Any conflict with an existing entry is resolved (reported as duplicate, or disambiguated into distinct names).
- Generic tech words are refused, not stored.
- Empty H2 sections are pruned on bootstrap;
## Glossary remains.
updated_at reflects today; a commit is proposed.
Anti-patterns
- Glossary as spec or scratch pad. Implementation detail («counter stored with a 1-minute TTL») belongs in the SAD/ADR, not here.
- Empty H2 «for completeness». A heading with no bullets — prune it.
- Silent edits. Adding a term without confirming the definition; the author must control the glossary.
- Batched «I'll add them later». Capture each term the moment it surfaces — deferral loses it.
- Storing generic tech words. HTTP, queue, the datastore name — refuse them.
- Rewriting on re-run. The skill reads and appends; it never overwrites the file.
- Ambiguous term with no NOT-reference. A homonym without «NOT …» is a six-month confusion waiting to happen.
References & template
./templates/CONTEXT.md — output scaffold; inline comments are the per-section contract (Glossary mandatory, Invariants/Out-of-scope pruned when empty).
../_shared/ask-style.md — phrasing for the definition / NOT-reference / conflict questions.
Example invocation
User: «add term tenant for rate-limiting-per-user»
Skill: <slug> given → target docs/features/rate-limiting-per-user/CONTEXT.md. Generic filter: tenant is domain → continue. File missing → copy template. grep "^- tenant" → not found. Definition Q → «a billable customer organisation owning 1+ users». NOT-reference Q → «NOT user — a user is one person inside a tenant». Compose - tenant — a billable customer organisation owning 1+ users. NOT user (a user is one person inside a tenant). → append under ## Glossary → prune empty ## Invariants/## Out of scope → updated_at: 2026-05-28 → commit context: + tenant.
1---2name: glossary3description: Use to capture or update domain terms in CONTEXT.md before their meaning drifts — whenever a fuzzy word shows up in an interview, spec, or review and you want one canonical definition plus a NOT-reference so a homonym can't bite you in six months. Triggers on "add term {X}", "what is {X} in our domain", "add to CONTEXT", "fix the glossary", "define {X}", "/sdd:glossary {term}", "додай термін", "онови глосарій", "що означає {X}". Two-level contract: repo-root CONTEXT.md holds project-wide terms, docs/features/{slug}/CONTEXT.md holds feature-scoped ones; readers read both and the per-feature entry wins. Lazy-bootstraps the target from a template, checks BOTH levels for a conflicting existing entry, asks for a one-sentence definition + the concept it's confused with, and appends one line to ## Glossary. Skip generic tech words (HTTP, queue, cache) — those are not domain terms. Output: created/edited CONTEXT.md. Runs anytime, no input gate; specify, clarify, design and api read its ## Glossary as the canonical so4---56# Skill: glossary78Lazy utility that fixes the meaning of a domain term in `CONTEXT.md` the moment it first surfaces, so its sense doesn't drift across the pipeline. For each term it captures a one-sentence canonical definition and — when the word is ambiguous — a **NOT-reference** naming the concept it's confused with. Runs anytime, with no upstream gate: a single term mid-interview, an `undefined-term` finding `clarify` resolves mid-sweep, or a batch handed over by `specify`. The output feeds `specify` (role + domain-term names) and `design` (invariants), which treat `## Glossary` as canonical and override anything that contradicts it.910This is a capture utility, not a Socratic stage — it does **not** run the shared Socratic loop or critic. The one shared dependency is question phrasing:11→ [`../_shared/ask-style.md`](../_shared/ask-style.md)1213Term definitions follow `artifact_language` — the `## Glossary` heading and the other H2s stay English, the frontmatter stays the **template's** (`status: Living` + `updated_at:` — never write `artifact_language` or any other settings key into the file) → [`../_shared/artifact-language.md`](../_shared/artifact-language.md).1415## Owner1617Whoever drives the conversation — anyone who spots ambiguity. Tech Lead approves the canonical form when a term is contested.1819## Inputs2021- `<term>` — the domain word/phrase to fix. If not given, ask for it.22- (Optional) `<slug>` — feature slug; targets `docs/features/<slug>/CONTEXT.md`. Absent → repo-root `CONTEXT.md`.23- (Optional) `pending_glossary_terms` — a batch handed over by `specify` after it writes the spec. Process each term in turn.24- (Optional) an `undefined-term` finding from `clarify` — `clarify` invokes this skill **mid-stream**, one term at a time, the moment such a finding is Resolved (it never invents a meaning inline; the canonical definition lands here).25- (Optional) proposed phrasings already surfaced in an interview/brainstorm — offer them as definition options instead of asking blank.2627## Protocol28291. **Pick the target (the two-level contract).** Root `CONTEXT.md` = **project-wide** terms (meaningful across features); `docs/features/<slug>/CONTEXT.md` = **feature-scoped** terms (meaningful only inside that feature). If `<slug>` is given → the per-feature file; otherwise → repo-root. A term lives in exactly **one** of the two files — never split or duplicate it across levels. Readers (`specify`, `clarify`, `design`, `api`) read **both** files; on a conflict the per-feature entry wins.302. **Generic-term filter.** Reject words that name infrastructure or transport rather than the business domain (e.g. HTTP, queue, cache, the datastore, the broker, a framework). Refuse: «`<term>` is technical, not a domain word — its choice belongs in the SAD or an ADR, not the glossary». Continue only for genuine domain vocabulary.313. **Bootstrap (lazy).** `test -f <target>`. Missing → copy [`./templates/CONTEXT.md`](./templates/CONTEXT.md) to `<target>`. Present → read it.324. **Conflict check — both levels.** `grep -i "^- <term>" <target>` AND the other level's file when it exists (root `CONTEXT.md` ↔ `docs/features/<slug>/CONTEXT.md`).33 - Found (either level), identical sense → STOP, report «already in the glossary» + which file holds it — never duplicate a term across levels.34 - Found (either level), different sense → escalate via `AskUserQuestion` (phrasing per [`../_shared/ask-style.md`](../_shared/ask-style.md)): «`<term>` is already defined as `<existing>` in `<file>` — same concept or a different one?». If different: a genuinely feature-scoped narrowing goes to the per-feature file (readers let it win there); otherwise propose a disambiguating pair of names (e.g. a billing-scoped vs a runtime-scoped variant) and fix both.35 - Not found in either → continue.365. **Ask the canonical definition** — one `AskUserQuestion`: «Define `<term>` in this domain, in one sentence». Offer interview/brainstorm phrasings as options when available; otherwise free text.376. **Ask the NOT-reference** — one `AskUserQuestion`: «Which concept does `<term>` get confused with, so a future reader doesn't mix them up?». No plausible homonym → `None`.387. **Compose one line.** `- <term> — <one-sentence definition>. NOT <confused concept + how it differs>.` — or, when step 6 = None, `- <term> — <definition>.`398. **Append under `## Glossary`.** Read the file, insert the line in `## Glossary` (alphabetical if the section is already sorted, else at the end). Never rewrite existing entries.409. **Prune empty H2s.** On a fresh bootstrap, delete `## Invariants` / `## Out of scope` if they hold no real content — only `## Glossary` is mandatory. (A genuine invariant or out-of-scope note goes in its section, never as implementation detail.)4110. **Structural self-check** — per [`../_shared/self-check.md`](../_shared/self-check.md): re-read the target file(s) from disk and verify **3 items**: (1) the term appears **exactly once across both levels combined** (grep root `CONTEXT.md` + `docs/features/<slug>/CONTEXT.md` — no duplicate, no split); (2) the entry matches the format `- <term> — <definition>.` (with the optional `NOT …` tail); (3) no empty H2 sections remain. Fix + re-check ≤2 cycles; surface anything unresolved.4211. **Stamp + commit + handoff.** Set `updated_at: <today>` in the frontmatter. Propose `context: + <term>` (or `context: + <term>, <term2>` for a batch). May fold into the caller's intake/spec commit. Then **emit the stage-handoff block** per [`../_shared/handoff.md`](../_shared/handoff.md) (utility variant) — *What I did* (incl. «self-check: 3/3 pass») + *Review* (`CONTEXT.md`) + *Run next*: resume your backbone stage (e.g. `/sdd:design <slug>`); `/clear` optional.4344## Definition of Done4546- `<target>` exists and contains `<term>` under `## Glossary` in the «one-sentence canonical + optional NOT-reference» format.47- Any conflict with an existing entry is resolved (reported as duplicate, or disambiguated into distinct names).48- Generic tech words are refused, not stored.49- Empty H2 sections are pruned on bootstrap; `## Glossary` remains.50- `updated_at` reflects today; a commit is proposed.5152## Anti-patterns5354- **Glossary as spec or scratch pad.** Implementation detail («counter stored with a 1-minute TTL») belongs in the SAD/ADR, not here.55- **Empty H2 «for completeness».** A heading with no bullets — prune it.56- **Silent edits.** Adding a term without confirming the definition; the author must control the glossary.57- **Batched «I'll add them later».** Capture each term the moment it surfaces — deferral loses it.58- **Storing generic tech words.** HTTP, queue, the datastore name — refuse them.59- **Rewriting on re-run.** The skill reads and appends; it never overwrites the file.60- **Ambiguous term with no NOT-reference.** A homonym without «NOT …» is a six-month confusion waiting to happen.6162## References & template6364- [`./templates/CONTEXT.md`](./templates/CONTEXT.md) — output scaffold; inline comments are the per-section contract (Glossary mandatory, Invariants/Out-of-scope pruned when empty).65- [`../_shared/ask-style.md`](../_shared/ask-style.md) — phrasing for the definition / NOT-reference / conflict questions.6667## Example invocation6869> **User:** «add term tenant for rate-limiting-per-user»70> **Skill:** `<slug>` given → target `docs/features/rate-limiting-per-user/CONTEXT.md`. Generic filter: `tenant` is domain → continue. File missing → copy template. `grep "^- tenant"` → not found. Definition Q → «a billable customer organisation owning 1+ users». NOT-reference Q → «NOT user — a user is one person inside a tenant». Compose `- tenant — a billable customer organisation owning 1+ users. NOT user (a user is one person inside a tenant).` → append under `## Glossary` → prune empty `## Invariants`/`## Out of scope` → `updated_at: 2026-05-28` → commit `context: + tenant`.