Taxonomy Change Runbook
Taxonomy Change Runbook
Section titled “Taxonomy Change Runbook”Last validated: 31/03/2026 (Session 134)
Overview
Section titled “Overview”Knowledge Hub uses a two-level, database-driven taxonomy for classifying all content:
- Level 1 — Domains (e.g. Security, Compliance, Corporate): the broad category. Every content item gets exactly one domain.
- Level 2 — Subtopics (e.g. Access Control, Data Protection): finer classification within a domain. Every content item gets exactly one subtopic.
The database (taxonomy_domains and taxonomy_subtopics tables) is the
single source of truth. Multiple downstream artefacts must be regenerated
whenever the taxonomy changes. Missing any step causes silent drift between what
the app shows, what the AI classifies against, and what the plugin exposes.
Architecture at a glance
Section titled “Architecture at a glance”taxonomy_domains / taxonomy_subtopics (DB — source of truth) │ ├── App UI ← contexts/taxonomy-context.tsx (live DB fetch, no regeneration needed) │ ├── Classification prompt ← docs/reference/classification-prompt.md │ (generated by scripts/generate-classification-prompt-taxonomy.ts) │ KEY_SIGNALS map in that script is MANUAL │ ├── Taxonomy snapshot ← scripts/tests/fixtures/taxonomy_snapshot.json │ (generated by scripts/generate-taxonomy-snapshot.ts) │ ├── Plugin taxonomy ← .claude/plugins/knowledge-hub/1.0.0/skills/classification/SKILL.md │ ← .claude/plugins/knowledge-hub/1.0.0/skills/search-strategy/SKILL.md │ ← .claude/plugins/knowledge-hub/1.0.0/settings.template.json │ (generated by scripts/sync-plugin-taxonomy.ts) │ ├── Plugin bundle ← lib/mcp/plugin-bundle.ts (committed artefact) │ (generated by scripts/bundle-plugin.ts / bun run build:plugin) │ └── Python pipeline ← scripts/kb_pipeline/classify.py (reads classification-prompt.md at runtime — no separate regeneration)When to Use This Runbook
Section titled “When to Use This Runbook”- Adding a new domain
- Adding a new subtopic to an existing domain
- Renaming or deactivating a domain or subtopic
- Changing a domain description or subtopic description
- After any direct SQL change to
taxonomy_domainsortaxonomy_subtopics
Prerequisites
Section titled “Prerequisites”- Admin role in the Knowledge Hub UI (Settings > Content Organisation requires admin)
- CLI access with
buninstalled - Environment variables set:
SUPABASE_SECRET_KEY(orSUPABASE_URL/NEXT_PUBLIC_SUPABASE_URL) — the scripts use the service role key to bypass RLS - ANTHROPIC_API_KEY set if you plan to reclassify existing content (Step 7)
Step-by-Step Procedure
Section titled “Step-by-Step Procedure”Step 1: Create or Modify in the Admin UI
Section titled “Step 1: Create or Modify in the Admin UI”Navigate to Settings > Content Organisation > Categories.
- Add a domain: Click the “Add Domain” button. Provide a name, display colour, and optional description.
- Add a subtopic: Expand a domain card, then click “Add Subtopic”. Provide a name and optional description.
- Rename/deactivate: Use the edit or toggle controls on the domain/subtopic card.
- Reorder: Use the drag handles or reorder controls — display order affects the classification prompt.
The API routes that handle these operations:
| Operation | Route |
|---|---|
| List/create domains | app/api/taxonomy/domains/route.ts |
| Update/delete a domain | app/api/taxonomy/domains/[id]/route.ts |
| List/create subtopics | app/api/taxonomy/subtopics/route.ts |
| Update/delete a subtopic | app/api/taxonomy/subtopics/[id]/route.ts |
| Reorder | app/api/taxonomy/reorder/route.ts |
After saving, the app UI updates immediately (it reads from the DB via
contexts/taxonomy-context.tsx). Everything below regenerates the
offline/static artefacts.
Step 2: Add KEY_SIGNALS (Manual)
Section titled “Step 2: Add KEY_SIGNALS (Manual)”Open scripts/generate-classification-prompt-taxonomy.ts and find the
KEY_SIGNALS constant (around line 49). This is a Record<string, string>
keyed by lowercase domain name.
Each entry is a short editorial paragraph that tells the AI classifier what distinguishes this domain from similar ones. It appears in the generated classification prompt under the domain heading.
If you added a new domain, add a corresponding entry:
const KEY_SIGNALS: Record<string, string> = { // ... existing entries ... 'your-new-domain': '**Key signal:** Content about [what makes this domain distinct]. ' + 'Answers the question "[the question this domain answers]."',};If you only added a subtopic to an existing domain, this step is usually not needed — subtopic descriptions come from the DB. However, if the new subtopic changes the boundary of what belongs in the domain, consider updating the domain’s key signal text.
Why this is manual: Key signals are editorial content that require human judgement about classification boundaries. They change rarely and are not stored in the database (Option B per the original spec).
Step 3: Add Guide Definitions (Manual, if applicable)
Section titled “Step 3: Add Guide Definitions (Manual, if applicable)”Guides are domain-specific reference documents shown alongside content. If the new domain or subtopic warrants its own guide (e.g. a compliance domain might need a “Compliance Requirements Guide”):
- Navigate to Settings > Guides.
- Click “Add Guide” and fill in the name, slug, domain association, and sections.
- Publish the guide.
This step is only needed for new domains or when the taxonomy change introduces a topic area that needs its own structured reference material. Most subtopic additions do not require a new guide.
Step 4: Regenerate Classification Prompt
Section titled “Step 4: Regenerate Classification Prompt”This reads all active domains and subtopics from the DB, combines them with the
KEY_SIGNALS paragraphs, and injects the result between <!-- TAXONOMY_START -->
and <!-- TAXONOMY_END --> markers in
docs/reference/classification-prompt.md.
bun run scripts/generate-classification-prompt-taxonomy.tsExpected output:
Generating classification prompt taxonomy from DB... Fetched 7 domains, 19 subtopics from DBUPDATED docs/reference/classification-prompt.md(If the taxonomy has not actually changed, it prints SKIPPED instead of
UPDATED.)
Verify: Open docs/reference/classification-prompt.md and confirm the new
domain/subtopic appears in the TAXONOMY REFERENCE section.
Step 5: Generate Taxonomy Snapshot
Section titled “Step 5: Generate Taxonomy Snapshot”This writes a JSON fixture used by TypeScript and Python validation tests.
bun run scripts/generate-taxonomy-snapshot.tsExpected output:
Taxonomy snapshot written to scripts/tests/fixtures/taxonomy_snapshot.json Domains: 7 (4 baseline, 0 client, 3 recommended) Subtopics: 19 Content types: 15 Platforms: 6Step 6: Sync Plugin Taxonomy
Section titled “Step 6: Sync Plugin Taxonomy”This updates the plugin skill files with the current taxonomy tree, domain filter guidance table, and content types table.
bun run scripts/sync-plugin-taxonomy.tsFiles updated:
| File | What changes |
|---|---|
.claude/plugins/knowledge-hub/1.0.0/skills/classification/SKILL.md | Taxonomy tree + content types table |
.claude/plugins/knowledge-hub/1.0.0/skills/search-strategy/SKILL.md | Domain filter guidance table |
.claude/plugins/knowledge-hub/1.0.0/settings.template.json | taxonomy.primary_domains array |
Step 7: Rebuild Plugin Bundle
Section titled “Step 7: Rebuild Plugin Bundle”The plugin bundle is a committed artefact (lib/mcp/plugin-bundle.ts) that
packages the plugin directory into a base64 ZIP for Vercel deployment. It must
be rebuilt after any plugin file changes.
bun run build:pluginImportant: The rebuilt lib/mcp/plugin-bundle.ts must be committed. The
/api/plugin/download endpoint serves this bundle — if it is stale, users
downloading the plugin get an outdated taxonomy.
Shortcut: bun run sync:taxonomy
Section titled “Shortcut: bun run sync:taxonomy”Steps 4-7 are automated by a single command:
bun run sync:taxonomyThis runs, in sequence:
scripts/generate-classification-prompt-taxonomy.ts(Step 4)scripts/generate-taxonomy-snapshot.ts(Step 5)scripts/sync-plugin-taxonomy.ts(Step 6)bun run build:plugin(Step 7)
You still need to do Steps 1-3 manually before running this.
Step 8: Verify
Section titled “Step 8: Verify”Run these checks to confirm everything is consistent:
- Classification prompt — open
docs/reference/classification-prompt.mdand confirm the new entries appear between the TAXONOMY markers. - Plugin skill — open
.claude/plugins/knowledge-hub/1.0.0/skills/classification/SKILL.mdand confirm the taxonomy tree includes the new entries. - Snapshot — check
scripts/tests/fixtures/taxonomy_snapshot.jsonlists the new domain/subtopic. - App UI — load the app and confirm the new domain/subtopic appears in filter dropdowns and the Settings > Content Organisation > Categories view.
- Run tests:
bun run testIf taxonomy consistency tests exist, they will catch drift between the snapshot and the generated artefacts.
Step 9: Commit All Changed Files
Section titled “Step 9: Commit All Changed Files”Stage and commit all modified artefacts together:
git add docs/reference/classification-prompt.mdgit add scripts/tests/fixtures/taxonomy_snapshot.jsongit add .claude/plugins/knowledge-hub/git add lib/mcp/plugin-bundle.tsgit add scripts/generate-classification-prompt-taxonomy.ts # if KEY_SIGNALS changedStep 10: Reclassify Existing Content (Optional)
Section titled “Step 10: Reclassify Existing Content (Optional)”If existing content should be re-evaluated against the updated taxonomy (e.g. a new domain was added that some existing items should move to):
# Dry run first — preview what would changebun run scripts/batch-reclassify.ts --limit 20
# Execute reclassificationbun run scripts/batch-reclassify.ts --execute
# Or limit to items currently in a specific domainbun run scripts/batch-reclassify.ts --execute --domain security --limit 50This requires ANTHROPIC_API_KEY and makes live API calls to Claude for
classification. Use --limit to control cost.
Step 11: Update Stats and Inventory
Section titled “Step 11: Update Stats and Inventory”If the taxonomy change affects MCP tool registrations or file counts:
bun run statsbun run generate:mcp-inventoryRollback Procedure
Section titled “Rollback Procedure”Deactivating (soft rollback)
Section titled “Deactivating (soft rollback)”- In Settings > Content Organisation > Categories, toggle the domain or subtopic to inactive.
- Run
bun run sync:taxonomyto regenerate all artefacts without the deactivated entry. - Commit the changed files.
- Existing content classified under the deactivated entry retains its classification but will show as an unrecognised value in filters until reclassified.
Hard rollback (delete)
Section titled “Hard rollback (delete)”- Delete the domain/subtopic via the admin UI (only possible if no content references it).
- If content references it, first reclassify those items to a different domain/subtopic.
- Run
bun run sync:taxonomyand commit.
Reverting KEY_SIGNALS
Section titled “Reverting KEY_SIGNALS”If the key signal text was wrong, edit
scripts/generate-classification-prompt-taxonomy.ts, then re-run
bun run sync:taxonomy and commit.
Common Mistakes
Section titled “Common Mistakes”| Mistake | Consequence | Prevention |
|---|---|---|
Forgetting bun run sync:taxonomy after UI changes | Plugin and classification prompt show stale taxonomy | Always run sync after any taxonomy DB change |
| Missing KEY_SIGNALS for a new domain | Classification prompt has the domain but no guidance paragraph — AI classifier may misclassify | Check the KEY_SIGNALS map whenever adding a domain |
Not committing lib/mcp/plugin-bundle.ts | Vercel serves stale plugin to users | bun run build:plugin output reminds you; always git status before pushing |
Running sync:plugin-taxonomy without generate-classification-prompt-taxonomy first | Plugin taxonomy is updated but classification prompt is stale | Use bun run sync:taxonomy which runs all scripts in the correct order |
Editing docs/reference/classification-prompt.md by hand | Next sync:taxonomy run overwrites manual edits between the TAXONOMY markers | Only edit content outside the markers by hand; taxonomy content is generated |
| Forgetting to regenerate the taxonomy snapshot | Validation tests may fail or pass incorrectly | bun run sync:taxonomy includes snapshot generation |
Files Involved
Section titled “Files Involved”Database tables
Section titled “Database tables”| Table | Role |
|---|---|
taxonomy_domains | Domain definitions (name, description, colour, display_order, is_active, provenance) |
taxonomy_subtopics | Subtopic definitions (name, description, domain_id, display_order, is_active, provenance) |
Scripts
Section titled “Scripts”| File | Role |
|---|---|
scripts/generate-classification-prompt-taxonomy.ts | Generates taxonomy section in classification prompt; contains KEY_SIGNALS (manual) |
scripts/generate-taxonomy-snapshot.ts | Generates JSON snapshot for validation tests |
scripts/sync-plugin-taxonomy.ts | Syncs taxonomy into plugin skill files and settings |
scripts/bundle-plugin.ts | Bundles plugin directory into committed lib/mcp/plugin-bundle.ts |
scripts/batch-reclassify.ts | Reclassifies existing content against current taxonomy |
Generated artefacts (committed)
Section titled “Generated artefacts (committed)”| File | Generated by |
|---|---|
docs/reference/classification-prompt.md (between TAXONOMY markers) | generate-classification-prompt-taxonomy.ts |
scripts/tests/fixtures/taxonomy_snapshot.json | generate-taxonomy-snapshot.ts |
.claude/plugins/knowledge-hub/1.0.0/skills/classification/SKILL.md (between inject markers) | sync-plugin-taxonomy.ts |
.claude/plugins/knowledge-hub/1.0.0/skills/search-strategy/SKILL.md (between inject markers) | sync-plugin-taxonomy.ts |
.claude/plugins/knowledge-hub/1.0.0/settings.template.json | sync-plugin-taxonomy.ts |
lib/mcp/plugin-bundle.ts | bundle-plugin.ts |
Runtime injection (no regeneration needed)
Section titled “Runtime injection (no regeneration needed)”| File | Role |
|---|---|
lib/ai/skills/classification.md | Classification skill file — uses {TAXONOMY} placeholder injected at runtime by classify.ts |
App code (no regeneration needed)
Section titled “App code (no regeneration needed)”| File | Role |
|---|---|
contexts/taxonomy-context.tsx | Client-side taxonomy provider (reads DB live) |
lib/taxonomy/taxonomy.ts | Static exports for non-React code |
lib/taxonomy/taxonomy-format.ts | Display formatting utilities |
lib/taxonomy/taxonomy-server.ts | Server-side taxonomy loading |
lib/validation/schemas.ts | Canonical constants (content types, platforms) |
components/settings/taxonomy-section.tsx | Admin UI for managing domains/subtopics |
components/settings/content-organisation-section.tsx | Parent settings section (Categories, Tags, Depth Levels tabs) |
API routes
Section titled “API routes”| Route | Role |
|---|---|
app/api/taxonomy/domains/route.ts | List/create domains |
app/api/taxonomy/domains/[id]/route.ts | Update/delete a domain |
app/api/taxonomy/subtopics/route.ts | List/create subtopics |
app/api/taxonomy/subtopics/[id]/route.ts | Update/delete a subtopic |
app/api/taxonomy/reorder/route.ts | Reorder domains/subtopics |
Python pipeline
Section titled “Python pipeline”| File | Role |
|---|---|
scripts/kb_pipeline/classify.py | Classification module (reads classification-prompt.md at runtime) |
scripts/kb_pipeline/config.py | Pipeline configuration (loads prompt file path) |