All skills
harlan-zw avatar

/glossary

@4d33e4d

Create or audit GLOSSARY.md. Use before naming product concepts, writing user-visible terms, renaming concepts, or checking vocabulary drift and banned terms.

  • 5 files
  • 31.9 KB
  • Updated 12 hours ago
  • GitHub
Use this Skill: https://skilld.dev/gh/harlan-zw/brundlefly/glossary

Nothing lands on disk. Nothing to clean up.

Fork this Skill

Edit a local copy. It keeps the author and licence.

referencesworkflows.md

≈4.1k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Glossary workflows

The relationship map is mandatory

A flat list of terms records what each word means on its own. It cannot record what the reader actually needs: how the terms relate, and therefore which distinctions are load-bearing and which are drift.

Every GLOSSARY.md carries a ## Map section. Produce it during init, refresh it during audit, extend it on add. Never present a term list as finished without one.

The map answers four questions a list cannot:

  1. Production: what makes this thing, and what does it make? A term that is produced by another term is not a synonym for it, however similar the words look.
  2. Storage and ownership: which table, which module or layer. Two terms in two tables owned by two teams are two concepts, whatever the UI calls them.
  3. Cardinality: 1—1, 1—N, N—N, and whether the constraint is enforced (a unique index) or merely conventional. An enforced 1—1 kills entire classes of naming question outright.
  4. Surface crossing: which customer-facing word each internal term surfaces under. This is where drift lives, and it is invisible in a list because the list has one row per term, not one row per crossing.

The table is the mandatory part of the map; the diagram is optional. Filling one Customer word cell per row is what exposes a collision, because the column forces you to answer per term and two identical cells are visible at a glance. Draw the diagram on top only when 3 or more terms converge on one word, or a pipeline branches. Use a diagram only when it makes the relationships easier to understand.

Pick the column set from the repo shape. Only Term and Customer word are fixed; the middle columns answer "where does it live, who owns it, how does it relate" in whatever terms the codebase actually has. Forcing DB columns onto a library produces a table with 11 of 16 cells empty.

Application backed by a database:

| Term | Table | Owner | Cardinality | Customer word |
| --- | --- | --- | --- | --- |
| Finding | `findings` | `pro/audit` | Audit 1—N Finding | "issue" |
| Page Issue | `site_page_issues` | `pro/sites` | Site 1—N, by path | "issue" |
| Ticket | `sprint_tickets` | `pro/sprint` | Sprint 1—N Ticket | "issue" |

Mark an enforced constraint as enforced: Site 1—1 Sprint (uniqueIndex) ends a whole line of questioning that Site 1—1 Sprint leaves open.

Library, CLI, or SDK: a term's switching cost is who imports it, not what stores it. Cardinality is usually a type parameter rather than a constraint, so it earns no column.

| Term | Export path | Stability | Consumers | Customer word |
| --- | --- | --- | --- | --- |
| Analyzer | `@gscdump/analysis/registry` | published subpath | CLI, MCP | "tool" |
| Report | `@gscdump/analysis` | published | CLI | "report" |

For a published package, an exported type name is a customer surface, not an internal one. It is as hard to change as a route.

For any shape, add a second table when published identifiers outnumber concepts: one row per frozen identifier (CLI command and flag, MCP tool name, export subpath, error code, route) mapped to the term it names. In a library that crossing table is where the drift actually lives.

When presenting the map to a human for a naming decision, a rendered diagram can carry the argument: sources on the left, arrows labelled with what actually moves, paths shown splitting and whether they rejoin. Tag each internal box with the customer word it surfaces under. Several differently-shaped boxes carrying an identical tag is the drift argument made visible. Write it as Mermaid per Map syntax below.

Never ask for an ASCII containment tree. A tree cannot render a node with two parents, and a term with two parents is the normal case, not the exception: one real repo had Site owned by both Team and GSC Property, which made the tree impossible to draw at all.

Read the decision records before drawing

A glossary harvested only from schema and UI will misread deliberate decisions as drift. This is the single most expensive mistake in this skill. Before writing the map, read docs/adr/, docs/decisions/, or whatever the project's decision log is called, and grep it for each candidate term.

An umbrella word that an accepted ADR chose on purpose is a ratified umbrella, not accretion, and proposing to "fix" it wastes the user's time and burns credibility. The evidence trail runs: schema says what is stored, UI says what is shown, the decision log says which of those was chosen and why. Only the third distinguishes a deliberate collapse from a silent one.

Check every ban against the schema

Run this against the bans already in the file, not only the ones you are proposing. An inherited ban list rots as the schema grows, and one real repo banned "reports" and "insights" while both were a live route, a table, and a published MCP tool.

Before a word goes on the ban list, or stays on it, grep for it across every frozen surface: stored enum values, column names, status literals, published export paths and subpaths, CLI command and flag names, MCP tool names, error-code literals, route segments, and ADR titles. A word is frozen when changing it breaks someone outside the repo, whether that is a database or an importer. Schema is only the database-shaped half; one library run rejected 10 proposed bans and not one of them was blocked by a stored value. A word the database persists cannot be banned in favour of another term. It is not a synonym; it is a value with behaviour attached, and banning it puts the glossary in conflict with the code rather than with a careless string.

When this fires, the ban list is what is wrong, not the schema. Narrow the ban to prose, or drop it, and record the enum's values with the axis they belong to.

Watch for the same word on two axes

The subtlest finding a map produces: one word used as a value on two independent classification axes of the same entity, meaning something different on each. A list cannot show this, because the word gets one entry and looks consistent. The map shows it as two boxes with one label.

When it appears, do not rename either axis reflexively. Record both, state the axis each belongs to, and only then ask whether the collision is worth the cost of renaming.

Commit scopes

A commit scope is vocabulary, and it drifts the same way every other surface does. Record retired scope spellings only when the project uses them for the same concept.

Record scopes as a ## Scopes table, placed after Banned. It uses the Banned column shape.

## Scopes

| Never | Use instead | Why |
| --- | --- | --- |
| `agent` | `github-agent` | The package, unit and skill all spell it `github-agent` |

List only retired spellings. Never list every allowed scope. An allowlist looks tidier and fails in practice: 40 to 59 percent of scopes in these repositories appear exactly once, so the list churns while the real drift is a handful of synonym pairs. A scope absent from the table is allowed.

Do not derive scopes from the directory tree. A structural list matched between 5 and 70 percent of real scopes, and under 25 percent in every application repository. Roughly half of real scopes name a process lane such as ci or deps, or a subsystem that crosses directories such as crawl or overlay.

A project may have a Git hook that enforces this table. Inspect its behavior before claiming enforcement. Without such a hook, the table is guidance only.

Add a row when audit finds two scopes naming one concept. Pick the winner by the same rule as any other term: weight by surface, never by count. Prefer the spelling used by stable public identifiers over the most frequent prose variant. A frozen surface outranks a tally.

Format

GLOSSARY.md has four sections in this order: Map, Terms, Banned, Open questions. A repository that enforces commit scopes adds Scopes after Banned. Follow the target project's size budgets. Split large term sets by their owning layer when that helps lookup. Read the format reference for the Mermaid map syntax, the term entry shape, and a worked example before writing or auditing the file.

Workflows

Pick by the argument given, defaulting to audit when the user points at a codebase and to init when no GLOSSARY.md exists.

init: bootstrap from an existing codebase

Do not invent the vocabulary. Recover the one already in use, then pick winners.

Harvest three surfaces separately, and do the customer one first. A schema is engineering vocabulary. It is evidence of what the team calls things, not of what the product calls them, and it may have drifted from the business names years ago. Starting from tables produces a tidy glossary that quietly contradicts the UI.

  1. Find the glossary that already exists. Before harvesting anything, grep the repo for an informal one: a Vocabulary, Terminology, Naming, Say/Don't say, or Copy section in COPY.md, CONTEXT.md, STYLE.md, CONTRIBUTING.md, README.md, or the docs tree. Projects that care about wording usually wrote one down without calling it GLOSSARY.md. Missing this ships a third competing list and is the worst outcome this skill can produce. Never silently override a wording decision someone already made, and never write a second list beside an existing one without saying which wins.

    0.5. Decide how you relate to what you found, and record the decision. Three outcomes, and the skill will not choose for you:

    • Point to it: the existing list is complete and better established than anything you would write. Cite it as authoritative, cover only what it omits, and say so in the intro.
    • Fold it in: the existing list is partial or scattered. Move it in verbatim, credit where it came from, and leave a pointer behind in the old file so the two cannot drift.
    • Supersede it: the existing list is stale or contradicted by shipped surfaces. Say which entries you are overriding and why, one line each.

    Put the choice in ## Open questions unless the evidence makes it obvious, because it decides who owns wording from now on.

  2. Customer surface: UI strings in templates, route and page filenames, headings, marketing and docs copy. Plus everything published, which counts here even with no UI in the repo: CLI command names, flag names and positional args (analyze <tool> names a concept), package names and export subpaths (@scope/pkg/registry), MCP tool names and protocol identifiers, stable error-code literals, and exported type names. Watch for a registry id disagreeing with its public path (id: 'decay' behind /tools/content-decay is two names for one thing). This surface is what a term means to the person paying, and for a library it is nearly the whole repo.

  3. Internal surface: table names, stored enum values, protocol contracts, layer and module directory names, unexported helpers. Exported types are not internal in a published package; file them under the customer surface, since an importer feels a rename exactly like a customer feels a changed route.

  4. Decision surface: docs/adr/, docs/decisions/, RFCs, whatever the project's decision log is called. Grep it for every candidate term. Skipping this step is how an agent proposes to "fix" a collapse the team ratified on purpose, which is worse than leaving the drift alone.

  5. Cluster synonyms within each surface, then across them. Look for the same concept appearing under 2 or more words, the exact drift being fixed.

  6. Weight by surface, not by frequency. Note where each variant appears and rank by switching cost: live URL, published protocol or MCP tool name, stored enum value, then UI string, then internal identifier, then prose. Do not spend effort counting occurrences; location decides, counts do not.

  7. Diff the surfaces and lead with the mismatch. Where they disagree, that table is the most valuable output of init, more than the term list. Expect the internal surface to draw distinctions the UI collapses: three tables surfacing under one customer word is the common shape, and one of those words is usually already in a route. Then check the decision log: a ratified umbrella is recorded, never reopened.

  8. Present each cluster with a recommended canonical term and the evidence. Ask before writing. Naming is the user's call, not the agent's, and a surface mismatch is a product decision, not a refactor.

    When you cannot ask (subagent, non-interactive run, no user present): write the draft anyway and put every decision you would have asked about into ## Open questions, with its evidence and options. Never silently pick a winner and present the file as settled. Blocking produces nothing; guessing quietly is the failure this whole skill exists to stop.

  9. Build the map before writing any definitions: the table always, a Mermaid diagram if it earns one. Filling a Customer word cell per row is what exposes a collision; a list will not.

  10. Write GLOSSARY.md with every rejected variant recorded on the Never: line. Where a mismatch is unresolved, record both terms, mark which surface each governs, and put the decision in ## Open questions. Never resolve it by picking the one that reads better; never ban a word that is still the live customer-facing name for something, and never ban a word the database stores as a value.

audit: find drift

  1. Read GLOSSARY.md. Build the search set from every Never: entry and every Banned row.
  2. Validate the ban list before searching against it. For each banned word, grep for it as a stored enum value, column name, or status literal. A word the schema persists is not a synonym to be replaced; the ban is the defect. Report those first, as glossary bugs rather than code bugs, because every hit they generate downstream is noise.
  3. Search the codebase for each surviving term. Prioritise user-visible surfaces: templates, markdown, route names, public exports, error strings. Internal-only variable names are a lower tier; report separately.
  4. Re-walk the map against reality. Confirm each term still has the table, owner, and cardinality recorded, and that no new term has appeared in the tree's territory. A map that has silently gone stale makes every other answer in the file untrustworthy. Redraw it as part of the audit output, not as a follow-up. Every backticked table, file or export in a term block must exist. Check each recorded location, owner, public identifier, and link against the actual project. Report stale entries.
  5. Report as file:line, the offending term, and the canonical replacement:
User-visible (fix now):
  app/pages/runs.vue:14    "Run history"  -> Sprint history    (route also needs /runs -> /sprints)
  docs/guide/setup.md:31   "campaign"     -> Sprint
  server/api/sprint.ts:88  throw new Error('batch failed')     -> 'Sprint failed'

Internal identifiers (lower tier, ripast can rename):
  lib/queue.ts:12  runBatch()  -> runSprint()

Needs a human read (may be ordinary English):
  README.md:6  "run the CLI" : likely fine, not the Sprint noun
  1. For code identifiers, use the project's AST-aware rename tool when available. Update consumers and verify affected behavior. No particular tool or sibling Skill is required. Do not blindly replace names across code.
  2. Prose and template strings need reading in context: a hit can be a legitimate everyday use of the word rather than the product concept ("run the tests" is not the Sprint noun). Never bulk-replace those.

add <term>

Append a term block. If GLOSSARY.md is over its cap, put the term in its layer's Terms section and add only a Map row at the root. Fill the Never: line with the synonyms it displaces, including whatever the code currently calls it. A new term with an empty Never: line is half-recorded, and audit will not catch drift against it.

Then place the term in the map, and treat that as part of adding it rather than as bookkeeping. Give it a parent, a table, an owner, a cardinality, and its customer-facing word. A term that cannot be placed is the useful failure: either it duplicates something already on the tree, or it belongs to a concept nobody has named yet, and both need resolving before the term is written.

Source: SKILL.md on GitHub

No rule matched.

skilld matched fixed text patterns in SKILL.md and file names. Patterns miss obfuscated code.

skilld run checks every file with the same patterns. It asks for approval before it loads a Skill with a behavior marked Needs approval.

No third-party reports yet.

Signed by skilld at 4d33e4d. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 9 hours ago.

Activeupdated 12 hours ago

README badge

README badge for harlan-zw/brundlefly/glossary