Skip to content

Phase B Prerequisite 1 — Ontology Pipeline (retrospective mapping)

Phase B Prerequisite 1 — Ontology Pipeline (retrospective mapping)

Section titled “Phase B Prerequisite 1 — Ontology Pipeline (retrospective mapping)”

Audit date: 13/05/2026 (S234) Framework reference: Jessica Talisman, “The Ontology Pipeline” (jessicatalisman.substack.com). Six-layer sequence: Controlled Vocabulary → Metadata Standards → Taxonomy → Thesaurus → Ontology → Knowledge Graph. Data inventory reference: docs/plans/phase-0-investigation/phase-b-prerequisite-1-data-inventory-RAW.md (~145 entities across 19 domains, written S234 sub-agent).

Purpose: retrospective mapping of KH platform data onto the Talisman ontology pipeline. Surfaces what KH currently has at each layer, what’s missing, what’s misplaced. Closes deferred decisions across Themes A/E + Findings 03/05 + Q1.13.

Reading order: §1 (framework) → §2 (current-state per-layer mapping) → §3 (cross-layer findings) → §4 (answers to the foundational questions) → §5 (implications for deferred decisions) → §6 (build order recommendation).


#LayerFunctionInputsOutputsStandards
1Controlled Vocabulary (CV)Disambiguated preferred-term list + synonyms + definitions. “All users + machines share a common understanding.”Raw data needing dedup/mergeCurated term list (preferred + synonym (UF) + acronyms + definitions)NISO Z39.19-2005 (R2010)
2Metadata StandardsSchema-based control encoding the “aboutness” of data.CV (as allowable values)Schema document: each element + type + format + allowable values3 categories: STRUCTURAL / DESCRIPTIVE / ADMINISTRATIVE (+ social/provenance)
3TaxonomyHierarchical parent-child structure on CV terms.CV + metadata standardsValidated hierarchy (depth + granularity + localisation + integration workflow)ISO 25964, SKOS, W3C RDF validator
4ThesaurusAdds associative relationships (BT / NT / SYN / equivalence) beyond hierarchy.Validated taxonomyMachine-readable RDF/SKOS structureSKOS / SKOS-XL
5OntologyClasses, properties, relations, rule bases. Encodes how concepts behave.ThesaurusFormal class/property/relation/rule definitions; semantic disambiguation at scaleRDF/OWL, domain + standard open ontologies
6Knowledge GraphVisualisation + querying layer. The “Rosetta Stone” of the system.All prior layersQueryable + visualisable knowledge surfaceSPARQL, SHACL

Key author framings (preserved verbatim):

  • “Introducing logic becomes nearly impossible when underlying data itself is not logically structured.”
  • Each layer is an “information product” — not just a technical step, but a value-adding business asset.
  • “Cleaning, preparation, reconciliation, modelling, testing, enrichment, enforcement, reporting and measurement — these steps are naturally incorporated” across all layers (iterative, not linear).
  • “Machines love ontologies because of their high-fidelity disambiguation and description, which bring clarity to machine understanding for tasks such as information retrieval, entity management, concept discovery, and RAG implementations for AI systems.”
  • “Building ontologies is like writing a story that defines domains, complex systems and the relationships between all the characters, places, things and concepts.”

For each layer: (a) what KH has today, (b) where it lives, (c) what’s missing, (d) what’s misplaced (i.e. content that belongs in this layer but lives elsewhere). Drawn from data inventory §1 + §3.

What KH has (raw inventory):

KH artefactHomeShapeCoverage
taxonomy_domainsDB tableDomain list (controlled)Top-level domains for primary/secondary classification
taxonomy_subtopicsDB tableSubtopic list under each domainSub-domains
layer_vocabularyDB tableLayer keys (e.g. regulatory, customer, internal)content_items.layer FK
content_type enumcolumn enum on content_items (15 values)article, blog, pdf, note, research, other, q_a_pair, case_study, policy, certification, compliance, methodology, capability, product_description, documentContent-shape vocabulary
platform enumcolumn enum on content_itemsweb, email, manual, upload, extraction, otherSource-channel vocabulary
lifecycle_type enumcolumn enum on content_itemsevergreen, date_bound, regulation, bid_discoveredLifecycle vocabulary
dedup_status enumcolumn enumclean, suspected_duplicate, confirmed_duplicate, confirmed_unique, supersededDedup state vocabulary
freshness enumcolumn enum on content_itemsfresh, aging, stale, expiredFreshness vocabulary
governance_review_status enumcolumn enumpending, approved, reverted, changes_requested, review_overdueGovernance vocabulary
publication_status enumcolumn enumdraft, in_review, published, archivedPublication state vocabulary
change_type enumcolumn enum on content_history11 valid CHECK values: create, edit, ai_update, import, merge, rollback, archive, delete, metadata_change, owner_change, publication_state. 5 in active prod use: archive, create, edit, metadata_change, owner_changeHistory event vocabulary
requirement_type enumcolumn on template_requirements(current values TBD)Template requirement vocabulary
ingest_source (soft-CV)column on content_itemsFree-text column with 11 documented canonical values (manual / url_import / upload / upload_autosplit / mcp_create / rss_feed / bid_outcome_integration / python_url / python_markdown / qa_import / batch_reclassify) — no DB CHECK enforces them (5 distinct values observed in prod). Build Phase 1 must add a CHECK migration to harden the vocabularyIngest provenance vocabulary
BID_STATES const tupletypes/bid.tsdraft, questions_extracted, matching, drafting, in_review, ready_for_export, submitted, won, lost, withdrawnBid workspace state vocabulary
workspaces.type discriminator (CHECK)columnbid, kb_section, intelligence (DB); plus proposal-placeholder in code registry. kb_section flagged as a legacy ERROR per Liam (S235) — slated for removal in schema cleanup; only intelligence observed in prod today.Workspace-type vocabulary
extraction_method enumcolumn on feed_articles (5 values: rss_content, fetch, jina_reader, firecrawl, summary_fallback)Sector-intelligence extraction-route vocabulary — relevant to 0.9-context.md §7.5 “drop Firecrawl” planningExtraction-route vocabulary
diff_mode enumcolumn on source_document_diffs (2 values: qa, full_text)Diff-mode vocabulary — relevant to Theme C edit_intent + Theme D cocoindex ops-DB ledger replacing diffsDiff-mode vocabulary
entity_aliasesDB tableFree-text alias → canonical mappingPerson/org/term aliases (thesaurus-adjacent — see §2.4)
Domain types in types/TypeScript filescontent, bid, bid-metadata, digest, review, template, owner, reorient, unified-gap, filter-presetEngineering-side type contracts
chunk_kind (planned)column on content_chunksheading-section, qa-block, paragraphChunk-shape vocabulary
scope_tag[] / anti_scope_tag[] (planned)columns on content_items + q_a_pairs(to be defined per WP-OPS-X-SCOPE-TAGS)Tenancy + conflict-surfacing vocabulary
origin_kind (planned for q_a_pairs)columnextracted_from_corpus, curated_explicit, derived_from_bid_response, imported_legacyQ&A provenance vocabulary
extractor_kind (planned for q_a_extractions)columnmempalace_miner, cocoindex_pattern_a, cocoindex_pattern_b, llm_extraction, prior_bid_responseExtractor vocabulary
citation_kind (planned)column on citationsverbatim_quote, paraphrase, derived_inference, q_a_matchCitation-shape vocabulary
application_type (NEEDED — not built)new vocabularybid, sector_intelligence, sales_proposal, competitor_research, training, product_guideTheme E — application layer above workspaces
form_type (NEEDED — not built)new vocabularybid, RFP, PQQ, ITT, tender, checklist, questionnaire, sales_proposal_template, …Theme A — generalised form vocabulary
form_format (NEEDED — not built)new vocabularydocx, xlsx, pdf, html, mdTheme A — generalised form-format vocabulary
change_report_category (NEEDED — not built)new vocabularycontent_added, content_edited, content_promoted, content_archived, etc.Change reports (digest rename) needs typed vocabulary
cocoindex_source_kind (planned)column on content_itemslocalfs, sharepoint, notion, google_drive, dropbox, box, manual_uploadExternal-folder canonical vocabulary

What’s missing:

  • No consolidated CV artefact. Vocabularies are scattered across DB enums + tables + TypeScript types + const tuples. There’s no single curated list with definitions + synonyms per term. This is the Talisman §1 “spreadsheet-first” stage that KH skipped.
  • No definitions per term. content_type='policy' vs 'compliance' — what’s the distinction? lifecycle_type='regulation' vs 'date_bound' — overlap rules? These distinctions live implicitly in code and tribal knowledge, not in a definition register.
  • No synonym / acronym mappings. SSQ = “standard-selection-questionnaire”; PQQ = “pre-qualification questionnaire”; RFP = “request for proposal”; ITT = “invitation to tender”. These acronyms exist in content but aren’t formalised as vocabulary.
  • application_type vocabulary missing (Theme E — see §4.2 answer).
  • form_type + form_format vocabularies missing (Theme A — see §4.5 answer).

What’s misplaced:

  • entity_aliases is a thesaurus-level concept (SYN relationship — §2.4) — currently sits as a thin alias-mapping table without SKOS encoding.
  • BID_STATES is a CV item but lives in TypeScript code (types/bid.ts) rather than schema enum or vocabulary table. State semantics are tribal.

What KH has (raw inventory):

The KH “metadata standards” surface is the schema of every domain table. Highlights per Talisman’s three categories:

STRUCTURAL (machine readability):

  • All FK relationships (content_items → workspaces via content_item_workspaces, etc.)
  • embedding vector(1024) columns + embedding_model + embedding_tokens (semantic-search shape)
  • content_text_hash GENERATED ALWAYS (content addressing)
  • pipeline_run_id, processing_queue.job_id (orchestration shape)
  • Table-and-column types per database.types.ts

DESCRIPTIVE (context):

  • content_items.{title, content, brief, detail, reference, summary, ai_keywords}
  • content_items.{primary_domain, primary_subtopic, secondary_domain, secondary_subtopic, layer} (classification context)
  • content_items.{user_tags, notes} (user-supplied)
  • bid_questions.question_text, bid_responses.answer_text
  • q_a_pairs.{question_text, answer_standard, answer_advanced} (planned)
  • feed_articles.{title, summary, key_points}
  • entity_mentions.{entity_text, entity_type, normalised_form}

ADMINISTRATIVE (lineage + lifecycle):

  • created_at, updated_at, created_by, updated_by
  • content_owner_id, verified_by, verified_at, archived_at, archived_by, archive_reason
  • governance_review_status, governance_review_due, governance_reviewer_id
  • next_review_date, review_cadence_days
  • version (source_documents), parent_id chain (versioning)
  • classification_* telemetry columns (model, tokens, confidence, reasoning)
  • ingest_source, import_batch, batch_tag (lineage)
  • op_id (planned per Finding 01) — rollback cohesion
  • content_history (immutable audit snapshots)

Social / provenance (extended category — author cites practitioner expansion):

  • content_history.change_summary + change_type
  • verification_history (SME verification events)
  • classification_disputes (dispute records)
  • read_marks (user-read tracking)
  • pipeline_runs + pipeline_failures (planned)
  • audit_log (planned per Finding 01)

What’s missing:

  • No formal metadata standard document. There’s no single artefact saying “every content_item MUST carry these descriptive fields; every workspace MUST carry these administrative fields.” The schema is the de-facto standard but isn’t framed as a metadata standard.
  • Metadata-element-to-CV binding is implicit. content_items.content_type is constrained by the enum, but the binding (and its rationale) lives in the migration that defined the CHECK constraint. No metadata document says “this metadata element draws from this CV.”
  • Application-served axis missing. Per data inventory §4 (application-to-data matrix), most entities serve multiple applications, but there’s no metadata field saying “this content_item was authored for / consumed by application X.”
  • Discriminated metadata for forms missingform_templates would need a metadata standard saying “every form template has form_type + form_format + version + question-count + extraction-status.”

What’s misplaced:

  • domain_metadata JSONB on workspaces is a metadata-elements bucket that lacks schema. Finding 05’s bid_workspaces satellite is the right corrective (Option B in I1) — typed columns reflect a metadata standard.
  • extraction_metadata JSONB on source_documents (page count, table count, etc.) is similar — undefined schema. Worth promoting to typed columns or to a schema-validated JSONB Zod shape.

What KH has (raw inventory):

KH has the concept of hierarchy in several places but no formal SKOS encoding:

HierarchyHomeShapeEncoding
Domain → Subtopictaxonomy_domainstaxonomy_subtopicsParent-child via FKDB schema only
Layerlayer_vocabulary.keyFlat (no hierarchy at present)Flat list
Content_items hierarchycontent_items.parent_id self-FKFree-form (any content can parent any other)Self-referencing FK
content_chunks hierarchycontent_chunks.parent_chunk_id self-FKHeading-based hierarchySelf-referencing FK + heading_level + heading_path[]
Workspace typesworkspaces.type discriminatorFlat (bid / kb_section / intelligence)CHECK constraint
Bid state machineBID_STATES tupleSequential (not hierarchical)Code tuple
Sector intelligence sub-domainstaxonomy_domains rows where domain ∈ sectorFlat (sectors as domain rows)DB rows
guides / guide_sectionstablesSection-within-guide hierarchyFK

What’s missing:

  • No formal SKOS / ISO 25964 encoding. The taxonomy_domains + taxonomy_subtopics relationship is a 2-level hierarchy implemented as FK only — no BT (broader term) or NT (narrower term) markup, no granularity rules, no localisation strategy, no integration workflow for new concepts.
  • No granularity governance. When should a new subtopic be added vs reuse an existing one? Today this is ad-hoc.
  • No taxonomy for applications. application_type (Theme E) needs hierarchical placement — e.g. is sector_intelligence a sibling of bids or a parent (since intelligence informs bid creation)?
  • No taxonomy for forms. form_type (Theme A) needs hierarchy — RFP and PQQ are both pre-tender forms; ITT is a post-shortlist form; checklists are a different shape. Hierarchy informs matching policy.
  • No deprecation policy. When content_type='product_description' becomes obsolete (e.g. if Phew restructures), how does the taxonomy retire it without breaking historic content_items?

What’s misplaced:

  • BID_STATES is a sequence, not a hierarchy — properly Layer 1 CV. Currently lives in code; should move to a workflow_states CV table per workflow.
  • Content_items self-FK hierarchy is not a taxonomy — it’s an asset relationship (parent/child documents). Calling it “hierarchical taxonomy” would be a category error.

What KH has (raw inventory):

KH artefactThesaurus shapeEncoding
entity_aliasesSurface-form aliases for entities (e.g. “Microsoft Corp” → “Microsoft”)Flat text mapping with category ∈ {client, generic} 2-value proto-classification — partial SKOS shape but no SYN/BT/NT markup
entity_mentionsEntity occurrences in content_itemsMentions, not relationships
entity_relationshipsTyped source→target between entitiesHas relationship_type (works_at / acquired / partnered_with / etc.) — but no SKOS encoding
AI-suggest review surface (Finding 06)Synonym suggestions during human curationImplicit
Q&A pair near-duplicate detection (planned)“These two pairs are equivalent”Pending — uses dedup_status state

What’s missing:

  • No SKOS BT/NT/SYN markup. Aliases are flat strings without ontological encoding.
  • No equivalence semantics on Q&A pairs. When two q_a_pairs are equivalent, the relationship is dedup_status='confirmed_duplicate' + superseded_by — but this is a supersession relationship, not a thesaurus equivalence.
  • No translation / localisation thesaurus. All KH content is English (specifically UK English per CLAUDE.md), but no thesaurus mapping for cross-language equivalences if KH ever localises.
  • No machine-readable export. Even where alias data exists, there’s no RDF/SKOS export.

What’s misplaced:

  • entity_relationships is closer to Layer 5 (Ontology) than Layer 4 (Thesaurus) — it expresses domain relationships (works_at, acquired), not associative/equivalence relationships between terms.

What KH has (raw inventory):

KH has the components of an ontology distributed across schema + code, but no formal RDF/OWL encoding:

Classes (entities):

  • content_items, q_a_pairs (planned), workspaces, bid_questions, bid_responses, source_documents, templates, template_fields, template_requirements, feed_articles, feed_sources, feed_prompts, entity_mentions, citations, bid_question_matches (planned), coverage_targets, governance_config, guides, guide_sections, audit_log (planned), bid_workspaces (planned), etc.

Properties (attributes):

  • Every column on every table is a property.

Relations (between classes):

  • All FK relationships are typed relations.
  • M:N junction tables (content_item_workspaces) are relations with their own metadata.
  • Polymorphic relations (citations.citing_entity typed enum + citing_entity_id FK — see relationship map §2 in inventory) — partial ontology shape.
  • entity_relationships (source→target with relationship_type) is the most ontology-like primitive.

Rule bases:

  • DB CHECK constraints
  • Triggers (content_history INSERT-only; updated_at update; superseded_by enforcement)
  • RLS predicates (per get_user_role())
  • Application-level: lib/bid/bid-state-machine.ts enforces BID_STATES transitions
  • Zod schemas in lib/validation/schemas.ts

What’s missing:

  • No formal class taxonomy. “What is a workspace? What is a content_item? How do they relate to an application?” — these definitions are tribal + schema-implicit.
  • No formal RDF/OWL export. Cocoindex’s meeting_notes_graph_neo4j example (per Prereq 2a) is the closest thing to ontology export — its Person/Organisation/Topic nodes + WORKS_AT/MENTIONED_IN/DISCUSSED_AT edges are graph-shaped ontology elements but not currently materialised in KH.
  • No rule base for application semantics. When does a content_item promote to a Phew product-guide? When does a q_a_pair generalise across applications? Today this is encoded in TS code + workflows, not in a rule base.
  • No upper ontology adopted. SKOS is taxonomy/thesaurus-level; FOAF (Friend of a Friend) for people + organisations; PROV-O for provenance. None adopted.

What’s misplaced:

  • entity_relationships is partial ontology (typed relations between entities). Promoting it to be the substrate of the future Knowledge Map (CX.32 RESOLVED-as-cocoindex) is the right move per Prereq 2a.
  • bid_questions.matched_content_ids UUID[] array (legacy) is a poor relation shape — Finding 02’s bid_question_matches table (proper relation with metadata) is the correct ontology shape.

What KH has (raw inventory):

  • Planned only. Knowledge Map (CX.32 RESOLVED-as-cocoindex per S232) is the user-facing primitive.
  • Cocoindex meeting_notes_graph_neo4j example (Prereq 2a §summary) confirmed as the substrate pattern.
  • entity_resolution from cocoindex (Prereq 2a top-recommendation #2) is the dedup primitive.
  • Substrate decision still open: Neo4j target vs PG entity_relationships vs mempalace (Prereq 2a flagged uncertainty).
  • SPARQL / SHACL: not adopted.

What’s missing:

  • Entire surface. No KG UI today. No SPARQL endpoint. No SHACL validation.
  • Visualisation strategy undefined. What does a user see when they look at the Knowledge Map? Per CX.32 RESOLVED, the answer is “cocoindex-driven view of workspace entities + relationships + temporal validity” — but the UI/UX shape is not designed.
  • Query patterns undefined. What queries does the KG serve? Per data inventory §4 matrix, the Knowledge Map serves every application as a Consumer (C) — but the question shapes per application are not enumerated.

What’s misplaced:

  • Some early Knowledge-Map-adjacent features exist (entity_mentions, entity_relationships, entity_aliases) — these are KG building blocks but not currently surfaced as a KG.

Cross-cutting observations from §2.

KH’s controlled vocabularies (Layer 1) are scattered across 29 artefacts (post-S235 WP1 additions of extraction_method + diff_mode) in 5 different homes (DB enums, DB tables, TS const tuples, TS type unions, Zod schemas). There’s no single “vocabulary register” that:

  • Lists every term across the platform
  • Defines each term
  • Maps synonyms + acronyms (SSQ, PQQ, ITT, RFP — none formally defined)
  • Records lifecycle (active / deprecated / future)
  • Cross-references to where the vocabulary is consumed

This is the Talisman Layer-1 starting artefact KH never built. Building it retrospectively is the foundation for closing the deferred decisions.

KH currently jumps from Layer 1 (enums + CV-shaped tables) directly to Layer 5 (application schema with typed relations like bid_questions → bid_responses → citations). Layers 2 (Metadata Standards), 3 (Taxonomy), and 4 (Thesaurus) are de-facto encoded in schema but not formalised as products.

Consequence: when a new application emerges (sales_proposals next), there’s no template — every application has to re-encode metadata + taxonomy + thesaurus + ontology shape in raw schema, repeating work and risking divergence. The Talisman ROI claim (“scoping, scaled, positioned as both a product and unique domain”) is unrealised.

3.3 — Missing “application_type” vocabulary blocks Theme E

Section titled “3.3 — Missing “application_type” vocabulary blocks Theme E”

The “applications layer above workspaces” question (user’s foundational question, Theme E) cannot be cleanly resolved until application_type exists as Layer-1 vocabulary. Once that lands:

  • It binds Layer 2 metadata (every content_item / workspace / pipeline_run can carry an application_type tag)
  • It binds Layer 3 taxonomy (hierarchical placement of applications — siblings vs parent/child)
  • It binds Layer 5 ontology (per-application satellite tables like bid_workspaces, proposal_workspaces etc. follow a Shape B pattern)

3.4 — Missing “form_type + form_format” vocabulary blocks Theme A

Section titled “3.4 — Missing “form_type + form_format” vocabulary blocks Theme A”

Same shape as 3.3 but for forms. Once form_type + form_format exist as Layer-1 vocabulary:

  • form_templates + form_template_fields schema follows from Layer-2 metadata
  • The form-extraction pipeline (Theme B) gets clear inputs (the cataloguer’s job is to identify form_type + form_format)
  • The 6-step form pipeline becomes vocabulary-driven not code-driven

3.5 — Q&A pairs as Layer-5 ontology entity

Section titled “3.5 — Q&A pairs as Layer-5 ontology entity”

q_a_pairs is the canonical “citable fact” entity. Once it lands:

  • It’s a Layer-5 class
  • Its citers form a polymorphic relation (citations.citing_entity_id per Finding 02)
  • Its match-targets form a polymorphic relation (question_matches.matched_entity_id per Theme A generalisation)
  • Form-agnostic per Theme A — the q_a_pair is the noun; the form-type discriminator lives on the match relation, not the pair itself

3.6 — Knowledge Map as Layer-6 deliverable

Section titled “3.6 — Knowledge Map as Layer-6 deliverable”

CX.32 RESOLVED-as-cocoindex is the substrate-level decision. The user-facing surface (Layer 6) is undefined. Bottom-up: once Layers 1-5 are formalised, the Knowledge Map’s surface becomes “render the formalised ontology + thesaurus + taxonomy as a queryable graph.”

From Prereq 2 sub-agents:

  • Cocoindex source-binding (postgres_source + localfs.walk_dir + content-hash) → Layer 1 + 2 substrate (content identity + administrative metadata)
  • Cocoindex memoisation + lineage (@coco.fn(memo=True)) → Layer 2 administrative metadata (provenance trail)
  • Cocoindex ExtractByLlm (typed Python output_type) → Layer 5 ontology extraction (Prereq 2a top recommendation #1 — subsumes Theme B step 4)
  • Cocoindex entity_resolution → Layer 4 thesaurus + Layer 5 ontology (Prereq 2a top recommendation #2 — named-entity dedup)
  • CocoInsight → Layer 2 administrative (developer-pipeline-observability; NOT compliance audit per Prereq 2b)
  • Cocoindex freshness → Layer 2 administrative (ingest-latency; orthogonal to KH governance freshness per Prereq 2b)

Implication: cocoindex affordances slot into KH’s planned ontology pipeline cleanly — they don’t redefine KH’s ontology, they provide ingest + extraction primitives that respect KH’s ontology choices.


§4 — Answers to the foundational questions

Section titled “§4 — Answers to the foundational questions”

The user’s original phase-b-prerequisite-1-onthology-pipeline.md listed five questions the ontology pipeline should answer. Each is addressed below with the ontology pipeline as the lens.

4.1 — What should the relationship between content_items and workspaces be?

Section titled “4.1 — What should the relationship between content_items and workspaces be?”

Original question: “Currently content_item_workspaces table links content_item_id and workspace_id but for what purpose? ‘2.4 content_items ↔ workspaces M:N pattern’ states ‘a single content_item can belong to multiple workspaces of different types’ — what does that mean in practice?”

Ontology-lens answer:

  • content_items is a Layer-5 class representing “knowledge artefacts” (citation-bearing facts, references, guides, intelligence-derived knowledge).
  • workspaces is a Layer-5 class representing “application instances” — concrete containers where a user (or AI agent) does task-work.
  • The M:N relation (content_item_workspaces, 81 prod rows) is a Layer-5 relation expressing assignment / scope — “this knowledge artefact is relevant to this application instance.”

Why M:N (not 1:N): the same knowledge artefact serves multiple applications. Examples from data inventory §4 matrix:

  • A Phew capability statement (content_item) cited in a bid response (bid workspace) + referenced in an onboarding guide (training workspace) + summarised in a sales proposal (sales-proposals workspace)
  • A sector-intelligence article (content_item) flagged in an intelligence workspace + promoted as evidence in a bid (bid workspace) via bid_outcome_integration ingest_source

Theme A nuance: Q&A pairs (currently content_items.content_type='q_a_pair', planned as standalone q_a_pairs table) follow the same M:N pattern — but their match-time relation is bid_question_matches (Finding 02) not direct workspace assignment. So:

  • q_a_pairsworkspaces via assignment (which app uses this pair?)
  • q_a_pairsbid_questions via match (which question in which bid did this pair answer?)

Both relations are valid; they answer different questions.

4.2 — Should we introduce an applications layer above workspaces?

Section titled “4.2 — Should we introduce an applications layer above workspaces?”

Original question: “Is it that we need to introduce a layer above workspaces? For example, application.”

Ontology-lens answer (RE-OPENED + RE-RESOLVED per S235 WP-ONTO-R1 + Liam ratification 14/05/2026):

The S234 ratification of Option (a) under-weighted the multi-tenant + “all bid workspaces share this template set” benefits Liam called out. S235 WP-ONTO-R1 re-investigated; Liam ratified Option (c) hybrid with provenance.

Three options considered (revised framing per WP-ONTO-R1 §2.1):

OptionShapeProsCons
(a) Vocabulary-onlyapplication_types CV table (key + label + definition). workspaces.application_type FK to it. Per-app config (defaults, route, features) stays in code (lib/workspace-types.ts).LOWEST cost (~1d). Simplest mental model.App-level config cannot be client-customised without code change. “Share this template set” benefit unsupported.
(b) Instance tableapplication_types table with rich config columns (label, icon, route, default templates, state_machine_name). Workspaces FK to applications. App config moves from code to DB.Client-configurable. Stronger Talisman schema-as-product framing.+3-5d cost. Clients can mis-configure base platform types. Loses code-as-source-of-truth reassurance.
(c) Hybrid with provenanceRATIFIEDOption (b) shape + provenance enum (core / client / recommended) per row. Mirrors existing taxonomy_domains.provenance precedent. Core types ship as baseline seed rows; clients add via admin UI without touching core.Multi-tenant-ready + upgrade-safe + matches taxonomy_domains.provenance precedent already in KH.+3-5d cost (marginal over Option b). Extra complexity of dual-source handling.
Collapse: application_type = workspace.typeCurrent state.Zero migration.Conflates application-type with workspace-type. Retained as null option only.

Ratified recommendation (S235): Option (c) hybrid with provenance. Add application_types table (rich config columns) + workspaces.application_type_id FK. The provenance enum mirrors taxonomy_domains.provenance (baseline / client / recommended). Code-registered types from lib/workspace-types.ts seed the table on first migration; the registry stays as type-system-level contract but the DB table becomes authoritative for runtime config (templates, state-machine references, route hints, default colour/icon).

Baseline core-provenance application_types (per Q-OQR1-03 ratification 14/05/2026):

  • procurement (replaces current application_type='bid' per §4.5 procurement rename below)
  • intelligence (sector-intelligence — already in prod)
  • sales_proposal (next-application per state-of-product)
  • product_guide (Phew use case + future SMB onboarding)
  • competitor_research (future)
  • training_onboarding (future)

All 6 ship as core-provenance stubs (state-machine wiring lands per use case readiness; the application_type row exists from day 1 to make the platform story complete). Client-added types use client provenance; platform-recommended-but-not-default use recommended.

Implications:

  • Theme E resolved via Option (c)
  • Q1.13 (workspace types as separate domains) — answer: NO, single workspaces table with application_type_id FK (Shape B holds — extended)
  • Finding 03 §3.2 inventory of 5 bid-specific satellites maps to procurement_workspaces Shape B per Finding 05 §5.3 — the satellite pattern generalises to other application_types (each application_type may have a satellite table)
  • Q3.11 bid_templates rename: form_templates per §4.5 (form_type as own CV) with workspace.application_type binding which form_templates are relevant per workspace
  • workspaces.type text column retires — replaced by workspaces.application_type_id FK (NOT a dual-axis subtype per Q-OQR1-04 — KISS single discriminator)
  • kb_section retires entirely (Q-OQR1-S235 — zero prod rows; not in the baseline core list)
  • Admin UI for client-added application_types lands in v1.1 per Q-OQR1-13 — extends existing settings-page admin pattern (taxonomy / tags / guides / layers / entities / governance already use this surface)

4.3 — Is product_guide an application, content, or both?

Section titled “4.3 — Is product_guide an application, content, or both?”

Original question: “Phew builds and maintains software products, and wanted the concept of a product guide. Clients won’t all ‘sell software’ necessarily, but they could still have products and/or services, and so a concept of a guide would still be valuable, especially for things like onboarding new employees, or for selling to clients.”

Ontology-lens answer: BOTH (different layers).

  • As CONTENT (Layer 5 instance + Layer 1 content_type CV): content_items.content_type='guide' + first-class guides + guide_sections tables already exist. The data inventory §4 matrix shows guides serving multiple applications as primary producer (training/onboarding C+P, product-guides C+P) and consumer across all apps.

  • As APPLICATION (Layer 1 application_type CV): product_guide is an application context — typical workspace shape might be a “product-guide workspace” that bundles related guide content_items + their authoring workflow + their delivery channels (in-platform vs HITL onboarding).

Distinction matters for downstream:

  • Phew has product_guide CONTENT today (guides table populated). They likely don’t have product_guide APPLICATION workspaces yet (no application_type='product_guide' workspace).
  • Future client onboarding scenarios may want a product_guide APPLICATION (workspace.application_type=product_guide) that aggregates: source product_guide content + onboarding-specific Q&A pairs derived from the guide + delivery-state machine (drafted / reviewed / published / used / archived).
  • Same content (the guides rows) feeds both Phew’s internal use + the application-level delivery.

No conflict — they’re orthogonal axes (what is it? vs how is it used?).

4.4 — Implications of Q&A pairs becoming form-agnostic

Section titled “4.4 — Implications of Q&A pairs becoming form-agnostic”

Original question: “What are the implications of making Q&A pairs form-agnostic?”

Ontology-lens answer (RE-OPENED + RE-RESOLVED per S235 WP-ONTO-R1 + Liam ratification 14/05/2026):

The S234 “peer class therefore needs new junction” rationale was rejected — “peer class to content_items” is a Layer-5 placement claim, not a workspace-cardinality claim. WP-ONTO-R1 §4 rebuilt the reasoning on three axes (identity / usage / Mempalace Shape B). Liam ratified q_a_pairs as corpus-level + scope_tag-driven workspace relevance (NO direct workspace FK; nullable source_workspace_id for provenance audit only).

Empirical foundation: prod has 395 q_a_pair rows (S234 verified). ZERO are assigned to any workspace via content_item_workspaces. q_a_pairs function as a corpus today, not as workspace-scoped data.

  • q_a_pairs is a Layer-5 entity class — a point-in-time citable fact (data inventory §1 Q&A domain) — with workspace-INDEPENDENT identity.
  • Form-agnosticism means the q_a_pair has NO FK to a form/bid/RFP. The pair exists in the corpus independently.
  • Forms MATCH against the pair corpus via a relationquestion_matches (generalising bid_question_matches per OQ-Q113-C agreed in principle).
  • Workspace relevance is COMPUTED, not DECLARED — via q_a_pairs.scope_tag && workspace.scope_tag overlap (with anti_scope_tag exclusion).

Implications for schema (post-S235 ratified shape):

ConcernCurrent shapeRatified form-agnostic shape (S235)
Q&A pair identitycontent_items.content_type='q_a_pair' rowq_a_pairs row (planned)
Pair → workspaceM:N via content_item_workspaces (current — but 0 of 395 prod rows actually assigned)NO direct FK. Nullable q_a_pairs.source_workspace_id (provenance audit only — e.g. captures originating workspace for origin_kind='derived_from_bid_response' pairs). Workspace relevance computed via scope_tag overlap + anti_scope_tag exclusion at query time. Per WP-ONTO-R1 §4.4 ratified reasoning — supersedes both 0.9-intended-architecture.md §4.3 N:1 (workspace_id NOT NULL FK) AND the prior onto-doc §4.4 M:N-junction recommendation.
Pair → form questionbid_questions.matched_content_ids UUID[] (legacy)question_matches table with question_kind discriminator (Theme A)
Citers of the pairbid_responses only (via implicit source_content_ids)Polymorphic citations.citing_entity per Finding 02
Pair lifecyclecontent_items lifecycle (governance + freshness + dedup)Inherits all of those + form-agnostic provenance (origin_kind) + temporal validity (valid_from / valid_to)
Pair privacy (workspace-private corpus content)n/aDeferred to v1.1 per Q-OQR1-08 ratification. Single Phew tenant for v1; no private_to_workspace_id column. Re-evaluate when second tenant joins.

Generalisation choice (per user’s Q1.12 free-form notes): ONE table question_matches with question_kind discriminator (DECIDED-IN-PRINCIPLE per OQ-Q113-C) — NOT per-form-type tables — unless evidence emerges that matching algorithms genuinely diverge per form (today no such evidence).

Why scope_tag-driven, not M:N junction:

  1. Empirical — today’s 395 prod q_a_pair rows are unassigned to any workspace. They function as a corpus the bid matcher draws from.
  2. Mental model — evidence doesn’t “belong to” a single workspace; evidence is referenced by many. A Q&A about “ISO 27001 certification status” is ONE record cited by bid + sales proposal + competitor research workspaces.
  3. “One record, many views” (CLAUDE.md) — explicit junction declaration would force per-workspace assignment as a curation step, when scope_tag relevance lets the matcher do the work.
  4. Anti_scope_tag exclusion — looser than junction (workspace can express “exclude pairs about [internal_only_topic]” via anti-scope without curating every pair).

Rejected rationales (for the record):

  • “peer class to content_items therefore needs new junction” — non-sequitur; peer class is Layer-5 placement, not workspace cardinality.
  • workspace_id UUID NOT NULL per 0.9-intended-architecture.md §4.3 — empirically refuted (0/395 prod assignment), conflicts with “one record many views”.
  • q_a_pair_workspaces M:N junction — heavier than scope_tag; doesn’t match empirical state.

Original question: “Form has a type (bid, tender, RFP, PQQ, any form with questions+answers) and a format (.docx, .xlsx, .pdf, .html). Not always, but a completed form will likely have a response.”

Ontology-lens answer (refined per S235 WP-ONTO-R1 §3 + Liam ratification 14/05/2026):

The S234 framing was directionally right but conflated application_type with form_type. Per WP-ONTO-R1 §3 + Liam ratification (Q-OQR1-02): application_type='procurement' becomes the umbrella; form_type discriminates bid / rfp / pqq / itt / tender / framework / dps / gcloud within it. This rename lands pre-launch per Q-OQR1-16 (combined PR with kb_section retire + application_types introduce + project_id→workspace_id + digests rename).

Two separate Layer-1 controlled vocabularies + an instance class:

ConceptLayerShape
form_typeLayer 1 CVVocabulary table: bid, RFP, PQQ, ITT, tender, checklist, questionnaire, sales_proposal_template, … + definitions + synonyms (SSQ=standard-selection-questionnaire, etc.) + acronym mappings
form_formatLayer 1 CVVocabulary table: docx, xlsx, pdf, html, md
form_templatesLayer 5 instanceCarries form_type FK + form_format FK + version + question_count + extraction_status + cataloguer metadata
form_template_fieldsLayer 5 instancePer-question detail within a form_template
form_responseLayer 5 instanceA completed form. Generalises bid_responses. Carries form_template_id FK + application_workspace_id FK + outcome data

Key insights:

  • Same form_type may exist in multiple form_formats (XLSX SSQ + PDF SSQ + DOCX SSQ all map to form_type=SSQ)
  • Per Theme B form pipeline: cataloguer reads form_format → applies right extraction approach + reads form_type → drives question-extraction policy
  • Per Finding 04 §3.5 + 1B-3 historical: Pattern A/B parsers retire as one-shot Phew migration helpers; cocoindex Pattern A/B adapter implicit in Finding 02 round-trip diagram becomes a typed cocoindex flow per Prereq 2a top-recommendation #1 (ExtractByLlm-based)

Schema rename mapping (closes Q3.11 + I2 in 00-synthesis):

Current nameRenamed toNotes
templatesform_templates (NOT bid_templates)Theme A — form-type-agnostic
template_fieldsform_template_fieldsTheme A
template_requirementsform_template_requirementsCloses I2 — generalised, not bid_template_requirements
bid_questions(kept as-is OR form_questions with form_type discriminator)Theme A. Decision: keep bid_questions for now; introduce form_questions if/when non-bid form-types ship.
bid_question_matches (planned per Finding 02)question_matches with question_kind discriminatorOQ-Q113-C agreed-in-principle
bid_responses(kept; or rename to form_responses)Theme A. Same decision as bid_questions.

Recommendation on conditional renames (S235 ratified — supersedes S234 conditional position):

Per Q-OQR1-02 + Q-OQR1-16 combined PR ratification, ALL of the following land together pre-launch:

  • bid_questions → keep name (the form_type discriminator form_type='bid' | 'rfp' | 'pqq' | 'itt' | 'framework' | 'dps' | 'gcloud' on question_matches distinguishes within the procurement workspace)
  • bid_responses → keep name (per above — the response is to a bid_question regardless of form_type)
  • bid_state_machineprocurement_workflow_state_machine (state-machine sits at application_type level; form_type-specific behaviour via policy injection per Q-OQR1-14 code-driven for v1, data-driven for v2)
  • BID_STATES const tuple → PROCUREMENT_WORKFLOW_STATES
  • lib/bid/bid-state-machine.tslib/procurement/procurement-workflow.ts per Q-OQR1-05
  • templatesform_templates (per S234 — unchanged)
  • template_fieldsform_template_fields (per S234 — unchanged)
  • template_requirementsform_template_requirements (per S234 — unchanged)
  • bid_workspaces (Finding 05 satellite) → procurement_workspaces (Finding 05 Option B pattern, renamed to align with procurement umbrella)

State-machine sub-state semantics per form_type (per WP-ONTO-R1 §3.2 + Q-OQR1-15):

  • Terminal states (won / lost / withdrawn) carry sub-state semantics per form_type (e.g. framework form_type uses “framework awarded” / “framework rejected”; PQQ uses “shortlisted” / “not shortlisted”). Sub-state label override lives in application_types.state_machine_config jsonb (per WP-ONTO-R1 §3.2 implementation pattern); state set stays uniform.
  • Non-procurement application_types (sales_proposal, competitor_research, training_onboarding) get their own state-machine names — wired when each ships; the application_type row exists from day 1 with state_machine_name = NULL until ready.

4.6 — Closing the foundational-question loop (S235 ratified)

Section titled “4.6 — Closing the foundational-question loop (S235 ratified)”
QuestionAnswer summaryCloses
4.1 content_items ↔ workspaces M:N purposeKnowledge-artefact-to-application-instance assignment. M:N because same artefact serves multiple applications. Q&A pairs are corpus-level (scope_tag relevance), NOT workspace-bound. See §5.7 for content_items ↔ source_documents mapping (filled per WP-ONTO-R1 §5).Q1.2, Q1.13 (workspace types)
4.2 applications layerOption (c) hybrid with provenance (S235 ratified). application_types instance table + provenance enum. 6 baseline core types ship. Workspaces FK to application_types. workspaces.type text column retires.Theme E, OQ-Q113-A, kb_section retire
4.3 product_guide classificationBoth content (content_type=‘guide’ + guides table) and application (application_type=‘product_guide’ workspace as core baseline) — orthogonal axesTheme E sub-question
4.4 Q&A form-agnostic implicationsq_a_pairs as Layer-5 corpus-level class. NO direct workspace FK. Nullable source_workspace_id (provenance audit). Workspace relevance via scope_tag overlap. Match via question_matches with discriminator. Supersedes both 0.9-intended-architecture.md §4.3 N:1 AND prior onto-doc §4.4 M:N-junction recommendations.Theme A, OQ-Q113-C, Q1.12, q_a_pair cardinality contradiction
4.5 form-type = type + format splitTwo Layer-1 CVs (form_type + form_format) + Layer-5 form_templates/form_template_fields/form_response. application_type='procurement' is the umbrella; bid/rfp/pqq/itt/framework/dps/gcloud are form_type discriminators within it. Combined-PR renames per Q-OQR1-16.Theme A, Q3.11, I2 in 00-synthesis, procurement rename

§5 — Implications for deferred decisions in feedback-findings-review.md

Section titled “§5 — Implications for deferred decisions in feedback-findings-review.md”

Cross-reference back to feedback-findings-review.md §3.1 (ontology prereq input matrix). For each item, state RESOLVED / RESOLVED-DIRECTIONAL / STILL-OPEN.

Open itemResolution from this ontology mapping
Applications layer above workspacesRE-RESOLVED-S235: Option (c) hybrid with provenance per §4.2 (was: RESOLVED-S234 Option (a) vocab-only). 6 core baseline application_types: procurement, intelligence, sales_proposal, product_guide, competitor_research, training_onboarding. Per Q-OQR1-01 + Q-OQR1-03.
form / form-type / form-format / question / answer / response entity definitionsRESOLVED — per §4.5. Refined S235: application_type='procurement' umbrella + form_type discriminators (bid/rfp/pqq/itt/framework/dps/gcloud) per Q-OQR1-02.
source_documents vs cocoindex source-key (Option α/β)RESOLVED-DIRECTIONAL: Option α (slim-and-keep). Ontology rationale: source_documents is a Layer-5 entity expressing “binary identity + custody trail + workspace binding” — replacing it entirely with a content_items column means losing that entity. Cocoindex source-key is a Layer-2 administrative metadata element, not a Layer-5 entity. Option α retains the entity (manifest only) + adds cocoindex source-key as a Layer-2 column. S235 refinement (Q-OQR1-09): source_documents.workspace_id becomes NULLABLE to support admin-shared binaries (one Phew bid library DOCX serving multiple procurement workspaces). Closes B2 + N5 in 00-synthesis (upload-route silent-fail fix applies under α).
content_items vs q_a_pairs boundary when application isn’t Q&A-basedRESOLVED — q_a_pairs is a peer class to content_items (Layer 5). content_items handles all non-Q&A knowledge artefacts. q_a_pairs handles citable facts. S235 refinement (Q-OQR1-06): both classes use scope_tag-driven workspace relevance — content_items via content_item_workspaces M:N + scope_tag, q_a_pairs via scope_tag-only (no junction). See §5.7 content_items↔source_documents mapping (filled per WP-ONTO-R1 §5).
product-guide as application vs content typeRESOLVED — both, orthogonal axes per §4.3. product_guide ships as core baseline application_type.
Citer space for citationsRESOLVED-DIRECTIONALcitations.citing_entity enum: bid_response (today) + sales_proposal_response + competitor_research_finding + training_unit + mcp_search_response. Per Finding 02 polymorphic shape. Closes OQ-Q111-A.
template_requirements rename targetRESOLVEDform_template_requirements (closes I2 in 00-synthesis with the 3-table inventory: form_templates + form_template_fields + form_template_requirements)
Form requirement_type='checklist' ENUM additionRESOLVED — NO. The existing 7-value enum (policy, statement, evidence, data, narrative, declaration, reference — verified S234) is sufficient; adding checklist is not needed. Checklist is a form_FORMAT discriminator, not a requirement_TYPE. Closes OQ-Q35-C.
Finding 05 schema implicationsRESOLVED-DIRECTIONALprocurement_workspaces satellite per Finding 05 Option B (closes I1; renamed S235 from bid_workspaces). Per-application_type satellites follow this Shape B pattern.
Per-type satellite registry patternRESOLVED — satellite per application_type vocabulary entry; not every workspace.type. Closes OQ-Q113-A.
edit_intent typed concept for re-classificationRESOLVED-DIRECTIONAL — add edit_intent Layer-1 CV (cosmetic / data / structural) on content_history.edit_intent column. Drives re-classification trigger policy per Theme C. Cocoindex re-extract gates on edit_intent ∈ {data, structural} per Prereq 2b natural fit.
q_a_pair ↔ workspace cardinalityRE-RESOLVED-S235: corpus-level + scope_tag-driven (was: M:N junction). NO direct workspace FK on q_a_pairs. Nullable source_workspace_id for provenance audit only (origin_kind='derived_from_bid_response' case). Workspace relevance computed via scope_tag overlap + anti_scope_tag exclusion. Supersedes both 0.9-intended-architecture.md §4.3 N:1 (NOT NULL workspace FK) AND prior onto-doc §4.4 M:N-junction. Per Q-OQR1-06 ratification.
Workspace-private q_a_pairs (v1 vs v1.1)RESOLVED-S235: defer to v1.1 per Q-OQR1-08. No private_to_workspace_id column in v1 (single Phew tenant).
Procurement rename (bidprocurement application_type)RESOLVED-S235: rename pre-launch per Q-OQR1-02. BID_STATESPROCUREMENT_WORKFLOW_STATES. lib/bid/bid-state-machine.tslib/procurement/procurement-workflow.ts. bid_workspacesprocurement_workspaces. Lands in combined PR (Q-OQR1-16) with kb_section retire + application_types introduce + project_id→workspace_id + digests→change_reports rename.
Provenance pattern adoption across hybrid vocabulariesRESOLVED-S235: adopt across all hybrid vocabs per Q-OQR1-11. Every hybrid Layer-1 CV carries provenance enum (core / client / recommended). entity_aliases.category renamed provenance. application_types, form_types, requirement_types, form_template_requirements, guides, coverage_targets all carry provenance. Direct fit for SMB onboarding-cleanup product story.
Admin UI for client-managed vocabulariesRESOLVED-S235: v1.1 per Q-OQR1-13 — extends existing settings-page admin pattern (current sections: profile/organisation/team, connections, taxonomy, tags, guides, layers, governance, entities, content-organisation, content-owner-management, duplicate-review, notification-preferences). v1 ships with SQL-seeded baseline; v1.1 adds settings-page sections for application_types / form_types / scope_tags etc.
kb_section retirement (S235 schema cleanup)RESOLVED-S235 — retire. Liam confirmed (S235): kb_section was added to workspaces.type CHECK in error. Zero prod rows currently carry this type. Schema-cleanup work: drop value from CHECK constraint + update any code references + audit workspaces rows for stragglers. Cross-cuts into WP3 decision-graph + WP-ONTO-R1 workspace/application re-investigation (kb_section was a workspace_subtype-style use of the type column; its removal simplifies the Option (a) vs Option (b) comparison).
Typed columns over JSONB (S235 platform standard)RESOLVED-S235 — standard. Liam (S235): “where there is an opportunity to use typed columns in our schema, we should always do so.” Cascades into: bid_workspaces Option B (typed columns promoted from domain_metadata JSONB) RATIFIED; source_documents.extraction_metadata JSONB → promote to typed columns or Zod-validated shape per §2.2 misplaced-items note; SKOS columns (BT/NT/SYN) on a future thesaurus_entries table over JSONB encoding. Applies platform-wide going forward.

Still-open items routed to ontology that this mapping does NOT close:

  • Pattern A/B parser fate (B1 in 00-synthesis): the recurring Q&A markdown adapter ownership question. Closes via Prereq 2a top-recommendation #1 (cocoindex ExtractByLlm) — NOT via this ontology mapping. Ontology mapping confirms q_a_extractions is the correct Layer-5 derived-cache entity.

§5.7 — content_items ↔ source_documents relationship (S235 — gap fill)

Section titled “§5.7 — content_items ↔ source_documents relationship (S235 — gap fill)”

Liam asked in S235 review: “what is the dynamic/relationship between content_items -> source_documents, including how any changes based on the workspace - application feedback would impact this.” The S234 doc didn’t map this explicitly. Filled per WP-ONTO-R1 §5; key points distilled here.

Layer placement:

  • source_documents — Layer 5 ontology class. Represents binary identity + custody trail. Each row = one binary file (DOCX, PDF, XLSX) with versions, storage path, workspace binding.
  • content_items — Layer 5 ontology class. Represents a unit of usable knowledge. May derive FROM a source_document or be born digital (RSS feed, manual input, MCP create).

Cardinality: N:1 optional. content_items.source_document_id FK → source_documents.id (SET NULL on delete). NULLABLE — many content_items have no source_document (URL ingest, RSS, manual). Empirical state: 0 prod content_items today have non-null source_document_id (source_documents table itself is 0 rows; platform not live for binary upload).

Lifecycle paths:

Ingest pathsource_document created?content_item.source_document_id set?
Manual upload (/api/upload)YESYES (after extraction)
URL ingest (/api/ingest/url)NONULL
RSS feed (feed_articles)NONULL
Manual content create (/api/items POST)NONULL
Bid library import (scripts/qa-migration/)YES (historical Phew DOCX)YES — planned
MCP create (/api/mcp)NONULL

Under Option α (S234 ratified — slim-and-keep source_documents):

  • source_documents retains entity status (Layer 5) but versioning cols retire (cocoindex ops-DB ledger absorbs version history).
  • Two FKs on content_items serve different purposes:
    • source_document_id → “what binary did this come from?” (KH-side typed manifest)
    • cocoindex_source_key → “what is the engine’s stable handle for re-runs?”
  • Both retained; not in conflict.

Workspace scoping (S235 refined per Q-OQR1-09):

  • source_documents.workspace_id becomes NULLABLE to support admin-shared binaries (e.g. one Phew bid library DOCX serving multiple procurement workspaces).
  • content_items continues to have NO direct workspace FK; relationships via content_item_workspaces M:N (which IS workspace-scoped) + scope_tag.
  • A content_item can be assigned to workspaces DIFFERENT from its source_document’s workspace (intentional — corpus reuse). content_owner_id answers ownership orthogonally.

Under Option (c) application_types adoption:

  • source_documents.workspace_id remains the FK route to application_type (via workspace → application_types).
  • No change to content_items.source_document_id semantics.
  • Transitive application_type derivable via either path: source_document → workspace → application_type OR content_item → content_item_workspaces → workspace → application_type. Disagreements between paths = soft warning condition.

Provenance chain for q_a_pairs:

source_document → content_item → content_chunk → q_a_pair
↳ nullable source_workspace_id (audit only, per Q-OQR1-07)

q_a_pairs DO NOT have a direct source_document_id FK; chain reaches back through content_items per 0.9-intended-architecture.md §4.3 source_content_item_id + source_chunk_id.

Open question on shared-binary RLS: if source_documents.workspace_id is NULL (admin-shared), what RLS pattern? Recommendation: RLS via EXISTS (SELECT 1 FROM content_item_workspaces WHERE content_item.source_document_id = source_documents.id) (any workspace whose content references the binary can see it). Implementation defers to migration writing.


To deliver the ontology pipeline retrospectively, in dependency order:

Phase 1: Consolidate Layer 1 (Controlled Vocabularies) — S235 refined

Section titled “Phase 1: Consolidate Layer 1 (Controlled Vocabularies) — S235 refined”
  1. Author the WP6 Markdown ontology register (docs/ontology/*.md) per Q-OQR1-12. Each CV file carries frontmatter: cv_name, layer, provenance_model, client_extensible, editable_via, core_seed_path, baseline_values. Multi-agent sequential authorship (drafter → verifier → editor → ratifier). GitBook + cocoindex pipeline keep markdown auto-fresh.
  2. Promote scattered enums to vocabulary tables with provenance enum (per Q-OQR1-11 — core / client / recommended). Candidates: BID_STATESPROCUREMENT_WORKFLOW_STATES const tuple (NOT a DB table — state-machine code stays code-driven per Q-OQR1-14 v1); requirement_types → DB table with provenance.
  3. Add new vocabularies (S235 ratified):
    • application_types — DB instance table (Option (c) per Q-OQR1-01). Rich config columns (label, icon, route, default_template_ids[], state_machine_name, state_machine_config jsonb, default_color, default_icon) + provenance enum. Baseline core rows (Q-OQR1-03): procurement, intelligence, sales_proposal, product_guide, competitor_research, training_onboarding. Seed migration includes 6 core rows.
    • form_types — vocab with applicable_application_types text[] (e.g. {procurement} for bid/rfp/pqq) + provenance enum.
    • form_formats — closed CORE vocab: docx/xlsx/pdf/html/md.
    • edit_intents — closed CORE vocab: cosmetic/data/structural.
    • change_report_categories — closed CORE vocab (digest rename to change_reports per Q-OQR1-16 combined PR).
    • scope_tags — CLIENT-DEFINED open vocab. Empty by default; admin UI manages.
  4. Procurement rename (Q-OQR1-02 + Q-OQR1-05): lib/bid/bid-state-machine.tslib/procurement/procurement-workflow.ts; BID_STATESPROCUREMENT_WORKFLOW_STATES; bid_workspacesprocurement_workspaces. Lands in combined PR per Q-OQR1-16.
  5. kb_section retirement (Q-OQR1-S235): drop from workspaces_type_check; clean up lib/workspace-types.ts:88 + app/api/workspaces/route.ts:80 default + components/workspace/workspace-create-dialog.tsx:34 + MCP tool fallback. Same combined PR.
  6. Define acronym/synonym mappings. SSQ, PQQ, ITT, RFP, DPS, G-Cloud, etc. — into entity_aliases (renaming categoryprovenance per Q-OQR1-11).

Phase 2: Formalise Layer 2 (Metadata Standards)

Section titled “Phase 2: Formalise Layer 2 (Metadata Standards)”
  1. Author a metadata-standards.md document (one section per domain).
  2. Document the structural / descriptive / administrative / social-provenance split explicitly per entity.
  3. Bind each metadata element to its CV (e.g. content_items.content_type → content_type vocabulary).
  4. Promote application_type to a metadata element on content_items, workspaces, pipeline_runs (Theme E delivery).
  5. Promote form_type + form_format to metadata elements on form_templates (Theme A delivery).
  1. Audit taxonomy_domains + taxonomy_subtopics for SKOS encoding opportunity. Either add broader_term_id / narrower_term_id columns OR materialise SKOS RDF export.
  2. Author application_type taxonomy. Is sector_intelligence a sibling of bids or a parent? Decision needed (recommendation: peer at app-type level; intelligence informs bids via M:N content references, not via taxonomy hierarchy).
  3. Author form_type taxonomy. Hierarchy: RFP / PQQ / ITT / tender all under “procurement_forms”. Checklist / questionnaire under “compliance_forms”. Etc.
  1. Extend entity_aliases with SKOS BT/NT/SYN columns — or migrate to a SKOS-shaped thesaurus_entries table.
  2. Author equivalence rules for q_a_pairs — when two pairs are equivalent (not just superseded).
  3. Author acronym mappings as thesaurus entries (SSQ ↔ standard-selection-questionnaire as a SYN relation).

Phase 5: Layer 5 (Ontology) — S235 refined

Section titled “Phase 5: Layer 5 (Ontology) — S235 refined”
  1. Schema is the de-facto ontology — formalise as a class-and-relation document.
  2. Adopt application_type_id FK as the top-level class discriminator on workspaces (replaces workspaces.type text column per Option (c)).
  3. Promote q_a_pairs to peer class of content_items (S16 spike — corpus-level, scope_tag-driven per Q-OQR1-06; NO direct workspace FK; nullable source_workspace_id for provenance audit per Q-OQR1-07).
  4. Extend citations as polymorphic relation (Finding 02). citing_entity enum extended for sales_proposal_response + competitor_research_finding + training_unit + mcp_search_response per OQ-Q111-A.
  5. Add question_matches table with discriminator (OQ-Q113-C). question_kind enum aligns with form_types vocabulary.
  6. Add per-application_type satellites following Finding 05 Option B pattern. Initial satellites: procurement_workspaces (was bid_workspaces — renamed S235), then sales_proposal_workspaces as that application ships. Each satellite carries typed columns promoted from workspaces.domain_metadata JSONB.
  7. Make source_documents.workspace_id NULLABLE per Q-OQR1-09 (admin-shared binaries).
  8. Apply provenance enum across hybrid vocabularies per Q-OQR1-11 — entity_aliases.category rename to provenance; add provenance column to form_template_requirements, guides, coverage_targets.
  1. Implement Knowledge Map UI surface per CX.32 RESOLVED-as-cocoindex.
  2. Substrate decision (Neo4j vs PG entity_relationships vs mempalace) — defer until S7 per Prereq 2a flagged uncertainty.
  3. Query patterns per application — what does a bid workspace query the KG for? What does a sales-proposals workspace query?
  4. SPARQL / SHACL — defer (heavy adoption cost; revisit if a clear use case emerges).

Phase ordering vs feedback-findings-review.md deferred decisions

Section titled “Phase ordering vs feedback-findings-review.md deferred decisions”
PhaseCloses
Phase 1 (CV consolidation)Theme A, Theme E, Q3.11, Q1.12 generalisation, OQ-Q113-C
Phase 2 (Metadata Standards)I2, Q5.5 + rename inventory, Q1.13 satellites
Phase 5 (Layer-5 ontology)I1, Finding 05 schema impacts, q_a_pairs S16 migration
Phase 6 (KG)CX.32 user-facing scope

Phases 1+2 are foundational and unblock everything else. Phases 3-4 are nice-to-have for formal SKOS encoding but not strictly required for Phase 5 schema decisions to land.


Items deferred to the cocoindex prereq, or to Liam-only operational rulings:

  • Pattern A/B parser fate (B1 in 00-synthesis): ontology-mapping confirms q_a_extractions as the correct entity, but the recurring adapter ownership is a cocoindex-prereq question (Prereq 2a top-recommendation #1 closes it).
  • S9 spike status (I3): unaffected by ontology mapping; pending empirical spike per Prereq 2b §5.
  • op_id propagation pattern (OQ-Q24-C): trigger vs app-stamped is a Layer-2 admin metadata implementation choice; ontology doesn’t pick.
  • MCP-action review pass: orthogonal to ontology pipeline; lands in WP4 06-mcp-tooling.md.
  • N3 combined-vs-sequenced naming-cleanup PR scope: operational, not ontology.

End of Phase B Prerequisite 1. Closes Theme E (Option (c) hybrid + provenance), Theme A (procurement umbrella + form_type discriminator), Q1.13, Q3.11, OQ-Q113-A, OQ-Q113-C, OQ-Q35-C, OQ-Q111-A, I1, I2, B2 (directional), N5, OQ-CX33-E. Build order in §6 sequences the combined schema-restructure PR per Q-OQR1-16 (application_types + procurement rename + kb_section retire + project_id→workspace_id + digests→change_reports).

S235 Liam rulings cascade (14/05/2026): Q-OQR1-01 Option (c); Q-OQR1-02 procurement rename now; Q-OQR1-03 6 core baseline application_types; Q-OQR1-06 corpus-level q_a_pairs + scope_tag-driven; Q-OQR1-08 defer private q_a_pairs to v1.1; Q-OQR1-11 provenance pattern across all hybrid vocabs; Q-OQR1-13 v1.1 admin UI via existing settings-page pattern; Q-OQR1-16 combined PR per OQ-Q55-A. Implicit/recommendation-default: Q-OQR1-04 single discriminator (KISS); Q-OQR1-05 lib/procurement/ rename in combined PR; Q-OQR1-07 nullable source_workspace_id audit column on q_a_pairs; Q-OQR1-09 nullable source_documents.workspace_id; Q-OQR1-10 multi-wing semantics defer to impl; Q-OQR1-12 WP6 markdown carries provenance fields; Q-OQR1-14 code-driven v1, data-driven v2; Q-OQR1-15 sub-state semantics via state_machine_config jsonb override; Q-OQR1-17 migration sequencing defer to migration writing.