Skip to content

WP6 INV-1 — layervocabulary (CV 03) intent + bidlibrary (CV 13) finding

WP6 INV-1 — layer_vocabulary (CV 03) intent + bid_library (CV 13) finding

Section titled “WP6 INV-1 — layer_vocabulary (CV 03) intent + bid_library (CV 13) finding”

Date: 14/05/2026 · Wave: S236 WP6 INV-1 (sub-agent investigation) · Author: INV-1 sub-agent Branch: content-items-investigation Status: Recommendation — for Liam ratification, then C1 editor cascade. Companion docs: docs/specs/wp6-ontology-harness/verifier-reports/B1-md-vs-db-drift.md (CRITICAL drift on CV 03 + HIGH on CV 13).

This investigation answers two interlocking questions raised by Liam during S236 escalation:

(i) Given current platform direction, what should CV 03 (layer_vocabulary) actually be in the ontology MD? The B1 verifier flagged the CRITICAL drift; it is C1’s job to set the new content. Before C1 acts, INV-1 must rule on whether the live values themselves are still the intended shape.

(ii) What is the right architectural pattern for the underlying problem — content-for-different-audiences + ability-to-utilise-content-for-applications — given that the current layer_vocabulary table is a metadata tag for filtering, not a UI hierarchy, but the Product_KB_Dev_Brief.md (Phew’s request) and kb-hub-gap-analysis-action-tracker.md §B.1 both push the layer concept towards a UI-nesting role, while parallel work has built Summary / In Depth / Takeaways / Original Text tabs and S235 ratifications have introduced corpus-level q_a_pairs driven by scope_tag?

The recommendation is grounded in the wider platform direction (§2 below), not the original Phew brief, per the agent prompt constraint.


For CV 03 today: confirm the four live keys as the v1 baseline — sales_brief, bid_detail, company_reference, research — but reclassify the CV from editable_via: seed_data to editable_via: admin_ui, retain provenance_model: hybrid, set client_extensible: true, and rewrite the Definition prose to describe a layer-as-audience-and-depth tag (not the regulatory/customer/internal model A1 invented). The four keys are the right v1 set because they map directly to Phew’s stated audience axis (Sales / Bid / Corporate / Research) AND survive the new platform shape: layer remains a useful Layer-1 CV that drives filtering + presentation, even after q_a_pairs go corpus-level.

For Q(ii) — architectural pattern: layers, item-detail tabs, and q_a_pairs are doing three different jobs and should not be collapsed. Recommend a hybrid (option d in §6): keep content_items.layer as the audience tag; keep the existing Summary / In Depth / Takeaways / Original Text tabs as the per-item depth surface (already shipping); and treat q_a_pairs + scope_tag as the per-application surfacing layer (per S235 ratification). UI nesting (Phew’s original ask) lands as a renderer rule on top of the layer tag, not a new column — guide_sections.parent_section_id already exists for the section-level case (per kb-hub-gap-analysis-action-tracker.md §B.1 + §B.4).

For CV 13 bid_library: it is the input parameter name to inferLayer() (TS + Python), NOT an ingest_source DB value. Real ingest_source value written by scripts/import_bid_library.py:288 is qa_import. Recommend: do NOT add bid_library to CV 13 baseline_values. Add §Notes paragraph documenting the parameter-vs-DB-value distinction. Rename TS literal bid_library to bid_library_extraction or similar in a separate cleanup PR (out of WP6 scope).


2. Platform direction — what was ratified post-S232

Section titled “2. Platform direction — what was ratified post-S232”

The S234 + S235 closure cycle (consolidated in 00-synthesis-v2.md and 0.9-decision-graph.md §11) materially changes the model in which “layer” is meaningful. The relevant ratifications:

2.1 application_types instance table (Q-OQR1-01, ratified 14/05/2026)

Section titled “2.1 application_types instance table (Q-OQR1-01, ratified 14/05/2026)”

Per 00-synthesis-v2.md §3.4 and phase-b-prerequisite-1-onthology-pipeline.md §4.2: workspaces FK to a new application_types instance table (Option (c) hybrid + provenance). 6 baseline core types ship: procurement, intelligence, sales_proposal, product_guide, competitor_research, training_onboarding.

Implication for layer: “what application surfaces this content” is now an explicit FK chain (content_itemscontent_item_workspacesworkspacesapplication_types), NOT the layer column. Layer no longer needs to encode application-served-ness — that lives in the relationship. Layer is freed to be an audience-and-depth tag orthogonal to application.

2.2 q_a_pairs corpus-level + scope_tag-driven (Q-OQR1-06, ratified 14/05/2026)

Section titled “2.2 q_a_pairs corpus-level + scope_tag-driven (Q-OQR1-06, ratified 14/05/2026)”

Per 00-synthesis-v2.md §3.6 and phase-b-prerequisite-1-onthology-pipeline-feedback-investigation.md §4: q_a_pairs have NO direct workspace FK; workspace relevance is computed via scope_tag overlap. Nullable source_workspace_id only for provenance audit.

Implication for layer: the “give me the right q_a_pair for this application” problem is now solved by scope_tag matching, NOT by joining on layer. Layer on q_a_pairs (where assigned) becomes a depth/audience hint, not a routing key.

2.3 Core/client/hybrid dimension across hybrid vocabularies (Q-OQR1-11, ratified 14/05/2026)

Section titled “2.3 Core/client/hybrid dimension across hybrid vocabularies (Q-OQR1-11, ratified 14/05/2026)”

Per phase-b-prerequisite-1-onthology-pipeline-feedback-investigation.md §6.2 and §6.3: every hybrid Layer-1 CV carries provenance enum (core / client / recommended). Layer vocabulary is explicitly classified as HYBRID in §6.2 line 629:

Content Layer (Sales Brief / Bid Detail / Company Reference) is HYBRID. The 3-tier layered model is core platform-shaped (the “layered content prevents duplication” architectural principle). But specific layer labels per client may diverge — some clients may want different layer names or a 4-tier model.

Implication for layer: layer must support provenance per row in v1.1+ admin UI (Q-OQR1-13 deferred to v1.1) and ship 4 baseline core rows in v1. The current layer_vocabulary table has NO provenance column today; this is gap #1 to flag.

2.4 Layer-1 CV register expansion (S234 + S235 WP1)

Section titled “2.4 Layer-1 CV register expansion (S234 + S235 WP1)”

Per phase-b-prerequisite-1-onthology-pipeline.md §2.1 + decision graph §11.1 ONT.13/ONT.14/ONT.15/ONT.16: new Layer-1 CVs application_type, form_type, form_format, edit_intent, change_report_category, extraction_method, diff_mode. None of them subsume layer. Layer remains a peer Layer-1 CV.

2.5 What “layer” means in the post-S235 model

Section titled “2.5 What “layer” means in the post-S235 model”

Cross-reading §2.1 + §2.2 + §2.3 + §2.4: layer is a non-routing audience tag. It does not gate workspace assignment (M:N junction does), it does not gate q_a_pair selection (scope_tag does), it does not classify the form/format of a deliverable (form_type/form_format do), and it does not govern the publication state machine (publication_status/governance_review_status do). What it does:

  • Serves as a filter in /browse and admin list views (“show me only Sales Brief content”).
  • Drives expected_layer on guide_sections so a Product Guide’s Security section can preferentially surface bid_detail items.
  • Drives layer-suggestion banners on create flows (lib/layer-inference.ts evaluates 7 rules to suggest a default).
  • Drives per-item layer-switcher nav (components/item-detail/layer-switcher-nav.tsx — surfaces sibling items at other layers when the same topic exists at multiple depths).

It is therefore a legitimate Layer-1 CV with continuing utility in the ratified shape — provided the four keys reflect the audience axis Phew operates on, AND the CV carries provenance so other tenants can extend.


3.1 Live DB rows (verified 14/05/2026 against staging branch turayklvaunphgbgscat)

Section titled “3.1 Live DB rows (verified 14/05/2026 against staging branch turayklvaunphgbgscat)”
key | label | description | order
sales_brief | Sales Brief | Positioning and messaging for internal sales | 10
bid_detail | Bid Detail | Factual content for tender responses | 20
company_reference | Company Reference | Controlled corporate documents | 30
research | Research | Background material and market intelligence | 40

All 4 rows are is_active=true. The table has columns id, key, label, description, display_order, is_active (per supabase/migrations/20260416102457_pre_squash_reconciliation.sql:3837-3849). No provenance column today.

lib/client-config.ts:159-184 ships an identical 4-key FALLBACK_LAYERS constant (consumed by contexts/layer-vocabulary-context.tsx:43-52 when the DB fetch fails). Not the regulatory/customer/internal model.

Per docs/specs/p1-36-db-driven-layer-validation-spec.md (shipped earlier per lib/validation/layer-schemas.ts:24-33): API handlers fetch active layers from layer_vocabulary at request time. inferLayer() (lib/layer-inference.ts:81-211) hard-codes the 4 keys for inference logic only (out of scope of P1-36 per its §Non-goals). Admin add/remove of layers via app/api/layers/route.ts is live; components/settings/layers-section.tsx exposes the admin UI.

SurfaceFileLayer’s role
Browse filter panelcomponents/browse/filter-panel.tsx, coverage-layer-filter.tsxFilter chips
Content card / row badgescomponents/content/content-card.tsx, content-row.tsxDisplay badge
Item detail navcomponents/item-detail/layer-switcher-nav.tsxSibling-at-other-layer navigation
Guide section rendercomponents/guide/guide-section.tsx:101expected_layer label per section
Create-content layer suggestioncomponents/content/layer-suggestion-banner.tsx, content-layer-selector.tsx, upload-tab-content.tsx, ingestion-success-card.tsxRender inferLayer() suggestion
Settings admincomponents/settings/layers-section.tsx, section-form-dialog.tsxCRUD admin

3.5 Per-item depth tabs (the “alternative” Liam referenced)

Section titled “3.5 Per-item depth tabs (the “alternative” Liam referenced)”

components/item-detail/content-tabs.tsx:307-370 renders horizontal tabs on every item detail page:

  • Summary (brief field, AI or human)
  • In Depth (detail field)
  • Takeaways (summary_data.takeaways JSON array)
  • Full Answer (q_a_pair full answer) OR Original Text
  • Reader (preserved reader-view content)
  • Supporting Detail (reference field)

This is per-item depth gradation, sourced from typed columns on content_items (brief, detail, reference, summary_data). Orthogonal to the layer axis.

3.6 Phew’s original framing (recap, not gospel)

Section titled “3.6 Phew’s original framing (recap, not gospel)”

Product_KB_Dev_Brief.md §“Content Layer Model” defines three layers: Sales Brief, Bid Detail, Company Reference. Per-section tables in §“Section 2: Product Guides” mark each subsection with one layer (e.g. “Sales Brief → Bid Detail” for §12 Security). The brief ALSO requests a layered view UI (“toggle between Sales Brief and Bid Detail for the same topic” — §“Key Platform Features”). kb-hub-gap-analysis-action-tracker.md §B.1 records Liam’s escalation that the live platform “treats layers as metadata tags used for filtering, not for UI hierarchy” and rules Option (ii) UI nesting rule: “each topic section displays Sales Brief at the top and Bid Detail nested beneath, programmatically”.


4. Gap analysis findings — what kb-hub-gap-analysis-action-tracker.md says about the layer disconnect

Section titled “4. Gap analysis findings — what kb-hub-gap-analysis-action-tracker.md says about the layer disconnect”

Quoted verbatim from docs/client-documentation/kb-hub-gap-analysis-action-tracker.md §B.1 (lines 37-60):

Status: Spec pending. Liam decision: Option (ii). Layer becomes a UI nesting rule: each topic section displays Sales Brief at the top and Bid Detail nested beneath, programmatically. Context: currently layers are metadata tags used for filtering, not for UI hierarchy. Advanced Audits §13 “Security & Compliance” is one section at bid_detail layer; mind-map intent was Security (Sales Brief) with Security (Bid Detail) nested.

Next action (spec): author docs/specs/p0-layer-nesting-spec.md:

  • Phase 1 — data model: determine whether nesting uses existing guide_sections.parent_section_id or a new layer_priority_order column.
  • Phase 2 — resolver: extend get_guide_content RPC to return layer-grouped buckets.
  • Phase 3 — UI: Product/Sector Guide section-renderer groups items by layer within each section, Sales-Brief first, Bid-Detail beneath, Reference collapsible.
  • Migration: zero-row — uses existing columns. Per-guide display order remains unchanged.
  • Accessibility: WCAG 2.1 AA — semantic nesting (<section><h3>…<section><h4>…), not just indentation.

docs/specs/p0-layer-nesting-spec.md does not yet exist (verified via find docs/specs -name "*layer*" — only p1-36-db-driven-layer-validation-spec.md exists). So the §B.1 ruling is spec-pending, not yet shipped. §B.4 (Advanced Audits dual-tier) recommends Option (i) guide_sections.parent_section_id as the section-hierarchy primitive; §B.1 explicitly flags this as composing with B.4.

Consequence for INV-1: Phew’s ask + Liam’s gap-analysis ruling both want layer to do double duty (a) filter tag (already shipping) AND (b) UI nesting driver (spec pending). The post-S235 platform direction does not contradict this — but it splits the implementation: (a) is layer-the-CV; (b) is a renderer rule on top of layer + guide_sections.parent_section_id. The two should not be merged into one column.


5. Original Phew requirement vs new platform shape — divergences

Section titled “5. Original Phew requirement vs new platform shape — divergences”
Original Phew brief (28/02/2026)Current ratified shape (post-S235)Divergence and resolution
3 layers: Sales Brief / Bid Detail / Company Reference4 layers in live DB: + ResearchPhew added research later (per gap-analysis Research Feed shipping S189 WP5). Live model is correct; brief is stale. Keep the 4-key live shape.
Layer = content_layer enum, ranks 1/2/3Layer = FK key into layer_vocabulary table with display_orderLive shape is more flexible and admin-extensible. Honors Q-OQR1-13 admin-UI direction (Phew’s brief implicitly asks for client-customisability via the “Admin user role”).
”Layered view: toggle between Sales Brief and Bid Detail for the same topic” — UI featureToggle currently realised as filter chips + layer-switcher-nav for sibling items. UI nesting (Phew’s intent per gap-analysis §B.1) is spec-pending.Diverges. Renderer-level work needed; not a CV change. CV must support the use-case (4 keys + ordering + provenance) but does not implement it.
Schema sketch shows section / guide / subsection columns, treats layer as one of many tag-shaped fields. Single content column for the body.Live schema has typed brief / detail / reference / summary_data.takeaways / content fields driving the Summary / In Depth / Takeaways / Original Text tabs.Diverges. Live shape is a superset — same one-record-many-views principle (CLAUDE.md), but realised as typed depth columns instead of layer-encoded duplicates. The platform now provides BOTH per-item depth (via tabs) AND per-section audience (via layer). The brief’s “Bid Detail always sits underneath the Sales Brief — never duplicates it” rule is honored by typed columns + render hierarchy, not by separate items per layer.
Per-section-table marks single Content Layer per subsectionguide_sections.expected_layer is a single-value column todayMatches. Where a section needs both layers (e.g. §12 Security “Sales Brief → Bid Detail”), the resolution is: child sections via parent_section_id (per gap-analysis §B.4 Option (i)) OR multi-value via array (not currently planned).
No mention of q_a_pairs, scope_tag, application_types, form_typesAll four are central to the new platform shapeOriginal brief is pre-platform-architecture. Layer fits cleanly alongside these new primitives, not in their place.
No mention of provenance / core-vs-clientAll hybrid vocabularies carry provenance per Q-OQR1-11Live layer_vocabulary table lacks provenance. Add as a v1.1 admin-UI dependency (or v1 schema migration if appetite exists).

Bottom line: Phew’s brief and the live platform are compatible on layers, but the live platform is a richer realisation. The ratified S235 model neither retires layer nor demotes it — it sits comfortably as a Layer-1 CV among 29 others.


6. Two questions to answer (Liam’s framing)

Section titled “6. Two questions to answer (Liam’s framing)”

6.1 Q(i) — what is required today for the CV 03 MD to accurately reflect intent?

Section titled “6.1 Q(i) — what is required today for the CV 03 MD to accurately reflect intent?”

Concrete recommendation for docs/ontology/03-layer-vocabulary.md frontmatter and prose:

---
cv_name: layer_vocabulary
layer: 1
provenance_model: hybrid
client_extensible: true
editable_via: admin_ui
core_seed_path: scripts/seed/baseline-layer-vocabulary.sql
related_layers: [2, 5]
status: active
baseline_values:
- key: sales_brief
label: Sales Brief
provenance: core
definition: Positioning and messaging for internal sales
- key: bid_detail
label: Bid Detail
provenance: core
definition: Factual content for tender responses
- key: company_reference
label: Company Reference
provenance: core
definition: Controlled corporate documents
- key: research
label: Research
provenance: core
definition: Background material and market intelligence
---

Key points C1 needs to act on:

  1. Replace the entire baseline_values: block (currently regulatory/customer/internal — invented by A1) with the 4 live keys above.
  2. Change editable_via from seed_data to admin_ui. Layers ARE admin-CRUD-able today via app/api/layers/* + components/settings/layers-section.tsx. They are not seed_data.
  3. Keep provenance_model: hybrid and client_extensible: true — both are correct per §6.2 of WP-ONTO-R1 (lines 629-630).
  4. Set core_seed_path to a real path (e.g. scripts/seed/baseline-layer-vocabulary.sql) — even though no such file exists yet, the path commits the build-Phase-1 work to creating one. Alternatively set to null and add a §Notes flag “core_seed_path TBD — baseline migration required for v1.1 multi-tenant”.
  5. Rewrite §Definition to describe the layer-as-audience-and-depth tag (matches the four description columns from the live DB). Drop the regulatory/customer/internal model entirely — that was source-doc speculation A1 took as gospel.
  6. Rewrite §Baseline values table to mirror the new frontmatter.
  7. Notes block additions (TBD-Liam flags):
    • “Live layer_vocabulary table has no provenance column today; v1.1 admin UI per Q-OQR1-13 will need an ALTER TABLE migration to add it before client-extension can be safely tagged.”
    • “UI nesting per kb-hub-gap-analysis-action-tracker.md §B.1 is spec-pending — docs/specs/p0-layer-nesting-spec.md not yet authored. CV does not gate that spec; renderer reads layer + guide_sections.parent_section_id to compose the nested view.”

6.2 Q(ii) — what approach for content-for-different-audiences + ability-to-utilise-content-for-applications?

Section titled “6.2 Q(ii) — what approach for content-for-different-audiences + ability-to-utilise-content-for-applications?”

Compare the four candidates Liam framed:

(a) Layer column on content_items (current). Single-value tag. Works as a filter. Does NOT do UI hierarchy on its own. Cannot natively express “this same item is the Sales-Brief view of that other Bid-Detail item” (would need an item↔item link).

(b) Summary / In Depth / Takeaways / Original Text tabs (existing alt). Typed depth columns on content_items (brief, detail, reference, summary_data). Per-item, not per-section. Solves “show me deeper detail of THIS item” but does NOT solve “show me the Bid-Detail view of THIS topic” (different items, same topic).

(c) q_a_pairs extraction + corpus-level + scope_tag (new platform shape per S235 Q-OQR1-06). Solves “give me the right answer for this question/application” via scope_tag overlap. Does NOT solve audience-tagged content browse, and DOES NOT solve UI nesting of richer documents (q_a_pairs are atomic).

(d) Hybrid: layers for UI nesting + tabs for granularity + q_a_pairs for application-specific surfacing. Each primitive does what it is structurally suited for. Three orthogonal axes that compose:

AxisPrimitiveRealisation
Audience (“who reads this?”)content_items.layer FK to layer_vocabularyFilter chips, nav badges, layer-suggestion-banner, expected_layer on guide sections
Depth / progressive disclosure (“how much detail?“)typed columns brief / detail / reference / summary_dataSummary / In Depth / Takeaways / Original Text tabs (already shipping)
Application surfacing (“which workflow consumes this?”)q_a_pairs + scope_tag overlap, plus content_item_workspaces M:Nscope_tag-driven query for q_a_pairs (S235); workspace-scoped feeds for content_items (already shipping)
UI nesting at the section level (“Sales-Brief item above its sibling Bid-Detail item”)guide_sections.parent_section_id + layer-grouped rendererSpec-pending per gap-analysis §B.1; composes with B.4 dual-tier

Recommend (d). Specifically:

  • Do NOT add a new column to encode UI nesting. The renderer should compose layer + parent_section_id per gap-analysis §B.1 Phase 1.
  • Do NOT collapse layer into application_types or form_types. Layer is audience-axis; application_type is workflow-axis; form_type is deliverable-shape axis. Three different things.
  • Do NOT replace the depth tabs with layer. A single content_item with full progressive depth (brief + detail + reference + takeaways) at layer=bid_detail is more useful than four separate items at four layers — and matches the CLAUDE.md “one record, many views” principle.
  • Do NOT add layer to q_a_pairs beyond what’s already there. q_a_pairs route via scope_tag in the new model; layer on a q_a_pair is a hint, not a key.

The original Phew brief (3-layer model + UI nesting view) is satisfied by (d). Tabs satisfy “read deeper detail of the same item without duplication”; layer + parent_section_id satisfy “Sales Brief at top, Bid Detail nested beneath” at the section-renderer level; corpus-level q_a_pairs satisfy “the right answer for this application” without per-application duplication.


7. CV 13 bid_library finding (sub-investigation)

Section titled “7. CV 13 bid_library finding (sub-investigation)”

Verified via grep:

SiteFileRole
TS type literallib/layer-inference.ts:21ingestionSource parameter type on LayerInferenceInput
TS rule branchlib/layer-inference.ts:94-101Rule 2 — “Q&A pairs imported from bid documents are bid-level detail”
TS adapterlib/queue/handlers/batch-reclassify.ts:908-913platformToSource() maps platform='extraction' to ingestionSource='bid_library' for the inferLayer() call (DB column platform, not ingest_source)
Python pipelinescripts/import_bid_library.py:297ingestion_source="bid_library" passed to infer_layer() (Python parallel)
Python pipelinescripts/kb_pipeline/layer_inference.py:96Same Rule 2 branch
Python testsscripts/tests/test_layer_inference.py:80,89,277,285Rule 2 coverage

scripts/import_bid_library.py:288 writes "ingest_source": "qa_import" to the actual content_items.ingest_source column. The string bid_library NEVER lands in content_items.ingest_source. Confirmed by live prod query (only upload observed; documented values include qa_import, never bid_library).

The other bid-library-adjacent values in import_bid_library.py:726, 850 are bid_library_import (with the _import suffix), again only used in pipeline logic — not necessarily reaching the DB column either.

bid_library is a layer-inference input parameter, NOT an ingest_source vocabulary value. It exists to let the inference function distinguish “this Q&A pair came from a bid library import (so it’s bid-level detail)” from “this Q&A pair came from manual creation”. The right home for it is the inferLayer() parameter shape, which is platform-internal pipeline logic — NOT the ingest_source CV.

Concrete recommendation for docs/ontology/13-ingest-source.md:

  1. Do NOT add bid_library to the baseline_values: block. It is not a DB value.
  2. Add a §Notes paragraph (verbatim suggested wording):

    Pipeline parameter vs DB value distinction. lib/layer-inference.ts:21 and the Python parallel scripts/kb_pipeline/layer_inference.py declare 'bid_library' as a literal value of the ingestionSource parameter to inferLayer(). This is not an ingest_source DB value — scripts/import_bid_library.py:288 writes the string 'qa_import' to the actual content_items.ingest_source column when ingesting bid library Q&A. The bid_library parameter literal exists only to drive Rule 2 of layer inference (“Q&A pairs from bid documents → bid_detail layer”). Treat bid_library as inference-function-internal, NOT as a CV term. Renaming to e.g. bid_library_extraction would reduce confusion and is a separate cleanup PR (out of WP6 scope).

  3. Retain the existing 11-value baseline_values list as drafted by A1; the prod-vs-documented drift the verifier flagged is a separate hardening problem (Phase 1 build needs CHECK migration to enforce; see §3 of B1 verifier report). That CHECK migration is the right home for ratifying which values stay and which retire.
  4. Add a §Notes bullet on the column-name drift (DB column ingest_source, Zod field ingestion_source, Python pipeline parameter ingestion_source) flagging Liam decision required — independent of bid_library.

Section titled “8. Recommended C1 actions (concrete instructions for the editor wave)”

8.1 docs/ontology/03-layer-vocabulary.md — REWRITE

Section titled “8.1 docs/ontology/03-layer-vocabulary.md — REWRITE”

Frontmatter changes:

  • editable_via: seed_dataeditable_via: admin_ui
  • Replace baseline_values: block with the 4 live keys per §6.1 above (each carries provenance: core + definition: <DB description column>).
  • Keep provenance_model: hybrid, client_extensible: true, related_layers: [2, 5], status: active.
  • core_seed_path: set to scripts/seed/baseline-layer-vocabulary.sql (Notes flag below covers the file’s non-existence).

Body changes:

  • Rewrite §Definition to describe layer-as-audience-and-depth tag. Suggested opening: “Cross-cutting audience-and-depth classification on content_items. 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, research for background material that feeds guides. Ships as 4 baseline core rows; clients may extend via the admin UI (/settings → Layers section).”
  • Rewrite §Baseline values table to mirror the new frontmatter.
  • Update §Relationships to add: “guide_sections.expected_layer text column — layer_vocabulary.key referenced by section-level expected-audience hints.”
  • Update §Consumers — list at minimum: contexts/layer-vocabulary-context.tsx, components/browse/filter-panel.tsx, components/item-detail/layer-switcher-nav.tsx, components/guide/guide-section.tsx, components/content/layer-suggestion-banner.tsx, components/settings/layers-section.tsx, lib/layer-inference.ts (read), lib/validation/layer-schemas.ts (validation), app/api/layers/* (admin CRUD).
  • Replace §Notes wholesale with:
    • Live layer_vocabulary table has 4 rows + admin CRUD. No provenance column today; v1.1 admin UI per Q-OQR1-13 will need an ALTER TABLE migration to add it before client-extension can be safely tagged.
    • UI nesting per kb-hub-gap-analysis-action-tracker.md §B.1 (Liam ruling 22/04/2026: layer becomes a UI nesting rule with Sales Brief → Bid Detail nested) is spec-pendingdocs/specs/p0-layer-nesting-spec.md not yet authored. The CV does not gate that spec; renderer composes layer + guide_sections.parent_section_id to produce the nested view.
    • 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.

8.2 docs/ontology/13-ingest-source.md — APPEND, no baseline_values change

Section titled “8.2 docs/ontology/13-ingest-source.md — APPEND, no baseline_values change”
  • Do NOT add bid_library to baseline_values.
  • Append a §Notes paragraph (text in §7.3 step 2 above) explaining the parameter-vs-DB-value distinction.
  • Append §Notes bullet on column-name drift per §7.3 step 4.
  • Leave the 11-value baseline_values list as A1 drafted; Liam decisions on (a) trimming retired values and (b) hardening with a CHECK constraint are scoped to a separate Phase-1 build migration, not the CV doc.

8.3 No other CV files require changes for this investigation.

Section titled “8.3 No other CV files require changes for this investigation.”

CV 22 (origin_kind) already declares extracted_from_corpus / curated_explicit / derived_from_bid_response / imported_legacy per A1 — that captures the bid-library origin semantics correctly without conflating ingest_source. CV 25 (application_type) ships independently per Q-OQR1-01.


9. Open items needing further Liam input (max 3)

Section titled “9. Open items needing further Liam input (max 3)”
  1. Layer provenance column migration timing. layer_vocabulary table needs ALTER TABLE … ADD COLUMN provenance text before v1.1 admin UI can mark client-added layers as client-provenance. Land in (a) the Q-OQR1-16 combined PR alongside the entity_aliases category→provenance rename, (b) a separate v1.1 schema migration, or (c) defer until first multi-tenant onboarding. Recommend (a) — keeps all provenance-pattern work in one cross-table PR, but Liam to confirm scope appetite.

  2. UI nesting spec ownership. docs/specs/p0-layer-nesting-spec.md is Spec-pending per gap-analysis §B.1. The §B.1 Phase-1 question (“guide_sections.parent_section_id vs new layer_priority_order column”) is unresolved. INV-1’s view: prefer parent_section_id for composability with B.4 dual-tier; do not add layer_priority_order. Liam to confirm direction before the spec is authored, so WP6 does not have to revisit CV 03 if the spec lands a column-add.

  3. bid_library literal cleanup PR scope. The bid_library parameter literal in lib/layer-inference.ts:21 (and Python parallel) is a maintenance hazard — anyone reading the type signature will reasonably assume it is an ingest_source value. Recommend a separate one-line cleanup PR that renames it to bid_library_extraction (TS + Python + tests). Liam to confirm whether to schedule this for the Q-OQR1-16 combined PR or as standalone post-WP6.


Every recommendation traces to a source.

RecommendationPrimary sourceSecondary source
4 live keys are the right v1 baselineLive DB query 14/05/2026 (staging branch turayklvaunphgbgscat)lib/client-config.ts:159-184 FALLBACK_LAYERS
editable_via: admin_uiapp/api/layers/route.ts, app/api/layers/[id]/route.ts, app/api/layers/reorder/route.ts, components/settings/layers-section.tsxdocs/specs/p1-36-db-driven-layer-validation-spec.md
provenance_model: hybrid + client_extensible: truephase-b-prerequisite-1-onthology-pipeline-feedback-investigation.md §6.2 lines 629-630 (HYBRID classification)00-synthesis-v2.md §3.7 (provenance pattern adoption)
3 orthogonal axes (layer / depth / application)§2.1, §2.2, §2.3 above00-synthesis-v2.md §3.4 + §3.6
Depth tabs are the right primitive for “deeper detail of THIS item”components/item-detail/content-tabs.tsx:307-370 (live tabs)app/item/[id]/page.tsx, app/item/[id]/loading.tsx:28
q_a_pairs corpus-level + scope_tag is the right primitive for “right answer for this application”phase-b-prerequisite-1-onthology-pipeline-feedback-investigation.md §4 + §4.400-synthesis-v2.md §3.6 + 0.9-decision-graph.md §11.1 ONT.5
UI nesting is renderer + parent_section_id, not CV changekb-hub-gap-analysis-action-tracker.md §B.1 + §B.4Live guide_sections.parent_section_id FK (per supabase/migrations/20260416102457_pre_squash_reconciliation.sql:3792)
bid_library is a layer-inference parameter, not an ingest_source valuelib/layer-inference.ts:21,94-101 + scripts/import_bid_library.py:288,297 (compare DB write vs parameter)scripts/kb_pipeline/layer_inference.py:96
Live DB CHECK for ingest_source does not existphase-b-prerequisite-1-onthology-pipeline.md §2.1 line 56 (“no DB CHECK enforces them”)B1 verifier report §2 row 13
Phew brief is compatible but pre-platform-architecturedocs/client-documentation/markdown/Product_KB_Dev_Brief.md §“Content Layer Model” + §“Database Schema” (lines 38-58 + 182-209)00-synthesis-v2.md (S235 ratifications post-date brief by 2.5 months)
Liam’s gap-analysis ruling on UI nestingdocs/client-documentation/kb-hub-gap-analysis-action-tracker.md §B.1 lines 37-60Spec docs/specs/p0-layer-nesting-spec.md does NOT exist (verified via find docs/specs -name "*layer*")
Verifier flagged CRITICAL driftdocs/specs/wp6-ontology-harness/verifier-reports/B1-md-vs-db-drift.md §3 row “CV 03” + §6 row 1Same report, §1 severity rollup

End of INV-1. C1 editor should consume §8 directly. WP-ONTO-R1 ratification should consume §9 (3 open items) before C1 begins. Q(ii) recommendation (option (d) hybrid) should be cross-checked against any future WP4 04-workspace-types.md and 05-qa-flow.md drafting — it is consistent with the §4 readiness map in 00-synthesis-v2.md but has not yet been ratified as a separate decision.