Skip to content

Taxonomy Change Runbook

Last validated: 31/03/2026 (Session 134)

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.

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)
  • 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_domains or taxonomy_subtopics
  • Admin role in the Knowledge Hub UI (Settings > Content Organisation requires admin)
  • CLI access with bun installed
  • Environment variables set: SUPABASE_SECRET_KEY (or SUPABASE_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)

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:

OperationRoute
List/create domainsapp/api/taxonomy/domains/route.ts
Update/delete a domainapp/api/taxonomy/domains/[id]/route.ts
List/create subtopicsapp/api/taxonomy/subtopics/route.ts
Update/delete a subtopicapp/api/taxonomy/subtopics/[id]/route.ts
Reorderapp/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.

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”):

  1. Navigate to Settings > Guides.
  2. Click “Add Guide” and fill in the name, slug, domain association, and sections.
  3. 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.

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.

Terminal window
bun run scripts/generate-classification-prompt-taxonomy.ts

Expected output:

Generating classification prompt taxonomy from DB...
Fetched 7 domains, 19 subtopics from DB
UPDATED 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.

This writes a JSON fixture used by TypeScript and Python validation tests.

Terminal window
bun run scripts/generate-taxonomy-snapshot.ts

Expected 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: 6

This updates the plugin skill files with the current taxonomy tree, domain filter guidance table, and content types table.

Terminal window
bun run scripts/sync-plugin-taxonomy.ts

Files updated:

FileWhat changes
.claude/plugins/knowledge-hub/1.0.0/skills/classification/SKILL.mdTaxonomy tree + content types table
.claude/plugins/knowledge-hub/1.0.0/skills/search-strategy/SKILL.mdDomain filter guidance table
.claude/plugins/knowledge-hub/1.0.0/settings.template.jsontaxonomy.primary_domains array

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.

Terminal window
bun run build:plugin

Important: 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.

Steps 4-7 are automated by a single command:

Terminal window
bun run sync:taxonomy

This runs, in sequence:

  1. scripts/generate-classification-prompt-taxonomy.ts (Step 4)
  2. scripts/generate-taxonomy-snapshot.ts (Step 5)
  3. scripts/sync-plugin-taxonomy.ts (Step 6)
  4. bun run build:plugin (Step 7)

You still need to do Steps 1-3 manually before running this.

Run these checks to confirm everything is consistent:

  1. Classification prompt — open docs/reference/classification-prompt.md and confirm the new entries appear between the TAXONOMY markers.
  2. Plugin skill — open .claude/plugins/knowledge-hub/1.0.0/skills/classification/SKILL.md and confirm the taxonomy tree includes the new entries.
  3. Snapshot — check scripts/tests/fixtures/taxonomy_snapshot.json lists the new domain/subtopic.
  4. App UI — load the app and confirm the new domain/subtopic appears in filter dropdowns and the Settings > Content Organisation > Categories view.
  5. Run tests:
Terminal window
bun run test

If taxonomy consistency tests exist, they will catch drift between the snapshot and the generated artefacts.

Stage and commit all modified artefacts together:

Terminal window
git add docs/reference/classification-prompt.md
git add scripts/tests/fixtures/taxonomy_snapshot.json
git add .claude/plugins/knowledge-hub/
git add lib/mcp/plugin-bundle.ts
git add scripts/generate-classification-prompt-taxonomy.ts # if KEY_SIGNALS changed

Step 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):

Terminal window
# Dry run first — preview what would change
bun run scripts/batch-reclassify.ts --limit 20
# Execute reclassification
bun run scripts/batch-reclassify.ts --execute
# Or limit to items currently in a specific domain
bun run scripts/batch-reclassify.ts --execute --domain security --limit 50

This requires ANTHROPIC_API_KEY and makes live API calls to Claude for classification. Use --limit to control cost.

If the taxonomy change affects MCP tool registrations or file counts:

Terminal window
bun run stats
bun run generate:mcp-inventory
  1. In Settings > Content Organisation > Categories, toggle the domain or subtopic to inactive.
  2. Run bun run sync:taxonomy to regenerate all artefacts without the deactivated entry.
  3. Commit the changed files.
  4. Existing content classified under the deactivated entry retains its classification but will show as an unrecognised value in filters until reclassified.
  1. Delete the domain/subtopic via the admin UI (only possible if no content references it).
  2. If content references it, first reclassify those items to a different domain/subtopic.
  3. Run bun run sync:taxonomy and commit.

If the key signal text was wrong, edit scripts/generate-classification-prompt-taxonomy.ts, then re-run bun run sync:taxonomy and commit.

MistakeConsequencePrevention
Forgetting bun run sync:taxonomy after UI changesPlugin and classification prompt show stale taxonomyAlways run sync after any taxonomy DB change
Missing KEY_SIGNALS for a new domainClassification prompt has the domain but no guidance paragraph — AI classifier may misclassifyCheck the KEY_SIGNALS map whenever adding a domain
Not committing lib/mcp/plugin-bundle.tsVercel serves stale plugin to usersbun run build:plugin output reminds you; always git status before pushing
Running sync:plugin-taxonomy without generate-classification-prompt-taxonomy firstPlugin taxonomy is updated but classification prompt is staleUse bun run sync:taxonomy which runs all scripts in the correct order
Editing docs/reference/classification-prompt.md by handNext sync:taxonomy run overwrites manual edits between the TAXONOMY markersOnly edit content outside the markers by hand; taxonomy content is generated
Forgetting to regenerate the taxonomy snapshotValidation tests may fail or pass incorrectlybun run sync:taxonomy includes snapshot generation
TableRole
taxonomy_domainsDomain definitions (name, description, colour, display_order, is_active, provenance)
taxonomy_subtopicsSubtopic definitions (name, description, domain_id, display_order, is_active, provenance)
FileRole
scripts/generate-classification-prompt-taxonomy.tsGenerates taxonomy section in classification prompt; contains KEY_SIGNALS (manual)
scripts/generate-taxonomy-snapshot.tsGenerates JSON snapshot for validation tests
scripts/sync-plugin-taxonomy.tsSyncs taxonomy into plugin skill files and settings
scripts/bundle-plugin.tsBundles plugin directory into committed lib/mcp/plugin-bundle.ts
scripts/batch-reclassify.tsReclassifies existing content against current taxonomy
FileGenerated by
docs/reference/classification-prompt.md (between TAXONOMY markers)generate-classification-prompt-taxonomy.ts
scripts/tests/fixtures/taxonomy_snapshot.jsongenerate-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.jsonsync-plugin-taxonomy.ts
lib/mcp/plugin-bundle.tsbundle-plugin.ts

Runtime injection (no regeneration needed)

Section titled “Runtime injection (no regeneration needed)”
FileRole
lib/ai/skills/classification.mdClassification skill file — uses {TAXONOMY} placeholder injected at runtime by classify.ts
FileRole
contexts/taxonomy-context.tsxClient-side taxonomy provider (reads DB live)
lib/taxonomy/taxonomy.tsStatic exports for non-React code
lib/taxonomy/taxonomy-format.tsDisplay formatting utilities
lib/taxonomy/taxonomy-server.tsServer-side taxonomy loading
lib/validation/schemas.tsCanonical constants (content types, platforms)
components/settings/taxonomy-section.tsxAdmin UI for managing domains/subtopics
components/settings/content-organisation-section.tsxParent settings section (Categories, Tags, Depth Levels tabs)
RouteRole
app/api/taxonomy/domains/route.tsList/create domains
app/api/taxonomy/domains/[id]/route.tsUpdate/delete a domain
app/api/taxonomy/subtopics/route.tsList/create subtopics
app/api/taxonomy/subtopics/[id]/route.tsUpdate/delete a subtopic
app/api/taxonomy/reorder/route.tsReorder domains/subtopics
FileRole
scripts/kb_pipeline/classify.pyClassification module (reads classification-prompt.md at runtime)
scripts/kb_pipeline/config.pyPipeline configuration (loads prompt file path)