Skip to content

Layer Vocabulary

Audience-and-depth classification, standing on its own layer_vocabulary DB table (admin_ui CRUD) rather than as a column on a content-record table. Captures who the content is for and at what level of detail — sales_brief for benefit-led internal-sales positioning, bid_detail for tender-response factual content, company_reference for product-agnostic corporate documents, and research for background material that feeds guides.

Ownership: the Guides application type (RESOLVED — owner round-4, D5; ID-133 BI-4). layer is the Guides audience/depth axis, matched against guide_sections.expected_layer — see Relationships. This CV predates ID-131 and originally also joined via content_items.layer, dropped with that table at ID-131 M6 (S450 GO); no source_documents.layer or q_a_pairs.layer column exists or is planned (the cocoindex SD/QA pipeline never wrote content_items.layer, flow.py:2515), so layer is retired from the SD/QA homes rather than re-homed onto them. The CV is KEPT, not deprecated — the Guides relationship was already live before this reframe; only the framing of primary ownership changes here.

Layer is orthogonal to domain/subtopic and to the per-item depth axis (Summary / In Depth / Takeaways / Original Text — see the “Four orthogonal axes” Notes bullet); it answers “who reads this?” rather than “how deep does this go?”. Ships as 4 baseline core rows; clients may extend via the admin UI (/settings → Layers section).

None formalised at present.

keylabelprovenance
sales_briefSales Briefcore
bid_detailBid Detailcore
company_referenceCompany Referencecore
researchResearchcore

(Verified 14/05/2026 against layer_vocabulary rows on the live DB — all four rows are is_active=true with display_order 10/20/30/40.)

  • guide_sections.expected_layer — the CV’s live home (Guides application type, RESOLVED D5). Free-text column matched at query time against a record’s classified layer value; the Guides audience/depth filter on a guide section. No FK/CHECK binds it to layer_vocabulary.key directly (supabase/migrations/20260617130000_squash_baseline.sql:6748) — matching happens in the get_guide_content/get_guide_coverage SQL RPCs and in lib/guide-section-mapping.ts’s reverse (item→section) match, not via a schema constraint. See the Notes bullet below on the RPCs’ current unrepaired state.
  • layer_vocabulary — DB table (columns id, key, label, description, display_order, is_active), independent of any content-record table.
  • reference_items.layer — text column, live, validated against layer_vocabulary.key by the shared validate_layer_key() trigger function (trg_validate_reference_items_layer) — the same function also fired (as trg_validate_layer_key) on content_items.layer until that table was dropped at ID-131 M6 (supabase/migrations/20260706110000_id131_drops.sql:121-128,318). Rendered on components/reference/reference-card.tsx and app/reference/[id]/reference-detail-client.tsx.
  • content_items.layer — GONE (pre-ID-131). Was a text column referencing layer_vocabulary.key, carried the trg_validate_layer_key trigger; DROPPED with content_items at ID-131 M6 (S450 GO, supabase/migrations/20260706110000_id131_drops.sql:310). Retired from the SD/QA homes entirely (RESOLVED — owner round-4, D5): no source_documents.layer or q_a_pairs.layer column exists or is planned (the cocoindex SD/QA pipeline never wrote content_items.layer, flow.py:2515).
  • contexts/layer-vocabulary-context.tsx — React context that caches the vocabulary for app-side reads (with FALLBACK_LAYERS from lib/client-config.ts if the DB fetch fails).

Guides application type (live):

  • contexts/layer-vocabulary-context.tsx — React context that caches the vocabulary for app-side reads.
  • components/guide/guide-section.tsxexpected_layer rendering hint on a guide section.
  • components/guide/guide-section-banner.tsx — surfaces suggestGuideSections() (lib/guide-section-mapping.ts) match results to editors.
  • components/settings/layers-section.tsx, section-form-dialog.tsx — admin CRUD surface (the layer_vocabulary table itself, and a guide section’s expected_layer filter).
  • lib/layer-inference.tsinferLayer(), called from lib/guide-section-mapping.ts and lib/queue/handlers/batch-reclassify.ts.
  • lib/validation/layer-schemas.ts — DB-driven validation; app/api/layers/* — admin CRUD endpoints.

reference_items.layer (live, separate consumer — see Relationships):

  • components/reference/reference-card.tsx, app/reference/[id]/reference-detail-client.tsx — display badge.
  • components/create-content/ingestion-success-card.tsx — layer-suggestion <Select> on the reference-ingestion success flow (useLayerVocabulary()); its PATCH target /api/items/${itemId}/metadata does not currently resolve to any route under app/api/items/ — flagged, not fixed, by this Subtask (see the {133.14} out-of-scope routing).
  • components/content/layer-suggestion-banner.tsx — the Accept/Change layer-suggestion banner (rendered from components/guide/guide-section-banner.tsx).

Retired with content_items (pre-ID-131, dropped at ID-131 M6 — no longer live):

  • components/browse/filter-panel.tsx — the browse-filter chip; dropped. components/browse/coverage-layer-filter.tsx survives on disk but is unreferenced by any caller today (orphaned, not wired to any route/component) — not removed by this Subtask.
  • components/content/content-card.tsx, content-row.tsx — display badges; both dropped.
  • components/item-detail/layer-switcher-nav.tsx — sibling-at-other-layer navigation; dropped.
  • components/content/content-layer-selector.tsx — dropped. components/create-content/upload-tab-content.tsx (relocated from components/content/upload-tab-content.tsx) survives but no longer references layer at all.
  • get_guide_content/get_guide_coverage — the SQL RPCs behind the Guides live-home relationship above — still LEFT JOIN content_items, unrepaired after that table’s drop at ID-131 M6 (supabase/migrations/20260617130000_squash_baseline.sql:3094-3176; the M6 migration 20260706110000_id131_drops.sql handles every other content_items dependent — six api views, citations/feed_articles columns, four child tables, retiring functions — but does not mention “guide” anywhere). Postgres does not track a hard dependency for LANGUAGE sql/plpgsql function bodies on tables referenced in the body, so DROP TABLE content_items succeeded without touching these two functions — they are now expected to error at call time. app/api/guides/[slug]/route.ts:64 still calls get_guide_content. Flagged for the owner/ID-131-successor by this Subtask, not fixed (docs-only worktree) — routed as an out-of-scope finding in the {133.14} journal.
  • Live shape verified 14/05/2026: the 4 keys above are the live layer_vocabulary rows on staging/prod; the earlier “regulatory / customer / internal” model was never live (it appeared as a stale example in source §2.1 and was inadvertently transcribed by the Drafter wave). The audience-and-depth model superseded it before this register was authored.
  • Editable via admin UI today. Layers are administered through app/api/layers/* and components/settings/layers-section.tsx per docs/specs/p1-36-db-driven-layer-validation-spec.md. editable_via: admin_ui reflects current behaviour; the SQL seed at core_seed_path is intended to formalise the baseline rows for multi-tenant onboarding (file does not yet exist — Phase 1 build creates it).
  • Provenance column ALTER (Ratified-S237): the live layer_vocabulary table has no provenance column today. Liam ratified S237 that the ALTER TABLE … ADD COLUMN provenance text migration lands in the Q-OQR1-16 combined PR — alongside the entity_aliases categoryprovenance rename, the application_types introduce, kb_section retire, procurement rename, project_id → workspace_id, digests → change_reports, form_templates rename, and seed-file emission for CV 01/02. v1.1 admin UI per Q-OQR1-13 can safely tag client-extended rows once the column lands.
  • UI nesting spec (Deferred-S238+): Liam’s gap-analysis ruling (docs/client-documentation/kb-hub-gap-analysis-action-tracker.md §B.1) makes layer a UI nesting rule with Sales Brief at the top and Bid Detail nested beneath. The supporting spec docs/specs/p0-layer-nesting-spec.md is not yet authored — Liam ratified S237 that authorship is deferred to S238+ when the UI nesting implementation work begins. The CV does not gate that spec; the renderer composes layer + guide_sections.parent_section_id to produce the nested view at runtime, independent of the spec. v1 spec and implementation confirmed in scope per Liam direction; this CV provides the data-side contract.
  • Four orthogonal axes (architectural pattern, INV-1 §6.2): layer is the audience axis; the per-item Summary / In Depth / Takeaways / Original Text tabs were the depth axis (typed columns brief / detail / reference / summary_data on content_items, pre-ID-131 — dropped with the table at ID-131 M6, no source_documents equivalent exists); q_a_pairs + scope_tag overlap is the application-surfacing axis (per Q-OQR1-06); and the OKF storage-layer axis (l_raw / l_records / l_concepts) is a separate CV, 36-three-layer-model.md. Do NOT collapse these into one column — and in particular do NOT conflate THIS layer_vocabulary audience axis with the three_layer_model storage axis, despite both being called “layer”.
  • core_seed_path points to a SQL seed file that does not yet exist; create as part of Phase 1 build per phase-b-prerequisite-1-onthology-pipeline.md §6.
  • Inference rule logic (lib/layer-inference.ts) hard-codes the 4 keys for its 7 rule branches per docs/specs/p1-36-db-driven-layer-validation-spec.md §Non-goals; admin add/remove is decoupled from inference.