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).
§1 — The Talisman framework (recap)
Section titled “§1 — The Talisman framework (recap)”| # | Layer | Function | Inputs | Outputs | Standards |
|---|---|---|---|---|---|
| 1 | Controlled Vocabulary (CV) | Disambiguated preferred-term list + synonyms + definitions. “All users + machines share a common understanding.” | Raw data needing dedup/merge | Curated term list (preferred + synonym (UF) + acronyms + definitions) | NISO Z39.19-2005 (R2010) |
| 2 | Metadata Standards | Schema-based control encoding the “aboutness” of data. | CV (as allowable values) | Schema document: each element + type + format + allowable values | 3 categories: STRUCTURAL / DESCRIPTIVE / ADMINISTRATIVE (+ social/provenance) |
| 3 | Taxonomy | Hierarchical parent-child structure on CV terms. | CV + metadata standards | Validated hierarchy (depth + granularity + localisation + integration workflow) | ISO 25964, SKOS, W3C RDF validator |
| 4 | Thesaurus | Adds associative relationships (BT / NT / SYN / equivalence) beyond hierarchy. | Validated taxonomy | Machine-readable RDF/SKOS structure | SKOS / SKOS-XL |
| 5 | Ontology | Classes, properties, relations, rule bases. Encodes how concepts behave. | Thesaurus | Formal class/property/relation/rule definitions; semantic disambiguation at scale | RDF/OWL, domain + standard open ontologies |
| 6 | Knowledge Graph | Visualisation + querying layer. The “Rosetta Stone” of the system. | All prior layers | Queryable + visualisable knowledge surface | SPARQL, 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.”
§2 — Current-state per-layer mapping
Section titled “§2 — Current-state per-layer mapping”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.
2.1 — Layer 1: Controlled Vocabulary
Section titled “2.1 — Layer 1: Controlled Vocabulary”What KH has (raw inventory):
| KH artefact | Home | Shape | Coverage |
|---|---|---|---|
taxonomy_domains | DB table | Domain list (controlled) | Top-level domains for primary/secondary classification |
taxonomy_subtopics | DB table | Subtopic list under each domain | Sub-domains |
layer_vocabulary | DB table | Layer keys (e.g. regulatory, customer, internal) | content_items.layer FK |
content_type enum | column enum on content_items (15 values) | article, blog, pdf, note, research, other, q_a_pair, case_study, policy, certification, compliance, methodology, capability, product_description, document | Content-shape vocabulary |
platform enum | column enum on content_items | web, email, manual, upload, extraction, other | Source-channel vocabulary |
lifecycle_type enum | column enum on content_items | evergreen, date_bound, regulation, bid_discovered | Lifecycle vocabulary |
dedup_status enum | column enum | clean, suspected_duplicate, confirmed_duplicate, confirmed_unique, superseded | Dedup state vocabulary |
freshness enum | column enum on content_items | fresh, aging, stale, expired | Freshness vocabulary |
governance_review_status enum | column enum | pending, approved, reverted, changes_requested, review_overdue | Governance vocabulary |
publication_status enum | column enum | draft, in_review, published, archived | Publication state vocabulary |
change_type enum | column enum on content_history | 11 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_change | History event vocabulary |
requirement_type enum | column on template_requirements | (current values TBD) | Template requirement vocabulary |
ingest_source (soft-CV) | column on content_items | Free-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 vocabulary | Ingest provenance vocabulary |
BID_STATES const tuple | types/bid.ts | draft, questions_extracted, matching, drafting, in_review, ready_for_export, submitted, won, lost, withdrawn | Bid workspace state vocabulary |
workspaces.type discriminator (CHECK) | column | bid, 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 enum | column 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” planning | Extraction-route vocabulary |
diff_mode enum | column 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 diffs | Diff-mode vocabulary |
entity_aliases | DB table | Free-text alias → canonical mapping | Person/org/term aliases (thesaurus-adjacent — see §2.4) |
Domain types in types/ | TypeScript files | content, bid, bid-metadata, digest, review, template, owner, reorient, unified-gap, filter-preset | Engineering-side type contracts |
chunk_kind (planned) | column on content_chunks | heading-section, qa-block, paragraph | Chunk-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) | column | extracted_from_corpus, curated_explicit, derived_from_bid_response, imported_legacy | Q&A provenance vocabulary |
extractor_kind (planned for q_a_extractions) | column | mempalace_miner, cocoindex_pattern_a, cocoindex_pattern_b, llm_extraction, prior_bid_response | Extractor vocabulary |
citation_kind (planned) | column on citations | verbatim_quote, paraphrase, derived_inference, q_a_match | Citation-shape vocabulary |
application_type (NEEDED — not built) | new vocabulary | bid, sector_intelligence, sales_proposal, competitor_research, training, product_guide | Theme E — application layer above workspaces |
form_type (NEEDED — not built) | new vocabulary | bid, RFP, PQQ, ITT, tender, checklist, questionnaire, sales_proposal_template, … | Theme A — generalised form vocabulary |
form_format (NEEDED — not built) | new vocabulary | docx, xlsx, pdf, html, md | Theme A — generalised form-format vocabulary |
change_report_category (NEEDED — not built) | new vocabulary | content_added, content_edited, content_promoted, content_archived, etc. | Change reports (digest rename) needs typed vocabulary |
cocoindex_source_kind (planned) | column on content_items | localfs, sharepoint, notion, google_drive, dropbox, box, manual_upload | External-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_typevocabulary missing (Theme E — see §4.2 answer).form_type+form_formatvocabularies missing (Theme A — see §4.5 answer).
What’s misplaced:
entity_aliasesis a thesaurus-level concept (SYN relationship — §2.4) — currently sits as a thin alias-mapping table without SKOS encoding.BID_STATESis a CV item but lives in TypeScript code (types/bid.ts) rather than schema enum or vocabulary table. State semantics are tribal.
2.2 — Layer 2: Metadata Standards
Section titled “2.2 — Layer 2: Metadata Standards”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.)
embeddingvector(1024) columns +embedding_model+embedding_tokens(semantic-search shape)content_text_hashGENERATED 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_textq_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_bycontent_owner_id,verified_by,verified_at,archived_at,archived_by,archive_reasongovernance_review_status,governance_review_due,governance_reviewer_idnext_review_date,review_cadence_daysversion(source_documents),parent_idchain (versioning)classification_*telemetry columns (model, tokens, confidence, reasoning)ingest_source,import_batch,batch_tag(lineage)op_id(planned per Finding 01) — rollback cohesioncontent_history(immutable audit snapshots)
Social / provenance (extended category — author cites practitioner expansion):
content_history.change_summary+change_typeverification_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_typeis 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 missing —
form_templateswould need a metadata standard saying “every form template has form_type + form_format + version + question-count + extraction-status.”
What’s misplaced:
domain_metadataJSONB on workspaces is a metadata-elements bucket that lacks schema. Finding 05’sbid_workspacessatellite is the right corrective (Option B in I1) — typed columns reflect a metadata standard.extraction_metadataJSONB 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.
2.3 — Layer 3: Taxonomy
Section titled “2.3 — Layer 3: Taxonomy”What KH has (raw inventory):
KH has the concept of hierarchy in several places but no formal SKOS encoding:
| Hierarchy | Home | Shape | Encoding |
|---|---|---|---|
| Domain → Subtopic | taxonomy_domains → taxonomy_subtopics | Parent-child via FK | DB schema only |
| Layer | layer_vocabulary.key | Flat (no hierarchy at present) | Flat list |
| Content_items hierarchy | content_items.parent_id self-FK | Free-form (any content can parent any other) | Self-referencing FK |
| content_chunks hierarchy | content_chunks.parent_chunk_id self-FK | Heading-based hierarchy | Self-referencing FK + heading_level + heading_path[] |
| Workspace types | workspaces.type discriminator | Flat (bid / kb_section / intelligence) | CHECK constraint |
| Bid state machine | BID_STATES tuple | Sequential (not hierarchical) | Code tuple |
| Sector intelligence sub-domains | taxonomy_domains rows where domain ∈ sector | Flat (sectors as domain rows) | DB rows |
guides / guide_sections | tables | Section-within-guide hierarchy | FK |
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. issector_intelligencea sibling ofbidsor 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_STATESis a sequence, not a hierarchy — properly Layer 1 CV. Currently lives in code; should move to aworkflow_statesCV 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.
2.4 — Layer 4: Thesaurus
Section titled “2.4 — Layer 4: Thesaurus”What KH has (raw inventory):
| KH artefact | Thesaurus shape | Encoding |
|---|---|---|
entity_aliases | Surface-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_mentions | Entity occurrences in content_items | Mentions, not relationships |
entity_relationships | Typed source→target between entities | Has relationship_type (works_at / acquired / partnered_with / etc.) — but no SKOS encoding |
| AI-suggest review surface (Finding 06) | Synonym suggestions during human curation | Implicit |
| 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_relationshipsis closer to Layer 5 (Ontology) than Layer 4 (Thesaurus) — it expresses domain relationships (works_at, acquired), not associative/equivalence relationships between terms.
2.5 — Layer 5: Ontology
Section titled “2.5 — Layer 5: Ontology”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_entitytyped enum +citing_entity_idFK — 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.tsenforcesBID_STATEStransitions - 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_neo4jexample (per Prereq 2a) is the closest thing to ontology export — itsPerson/Organisation/Topicnodes +WORKS_AT/MENTIONED_IN/DISCUSSED_ATedges 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_relationshipsis 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_idsUUID[] array (legacy) is a poor relation shape — Finding 02’sbid_question_matchestable (proper relation with metadata) is the correct ontology shape.
2.6 — Layer 6: Knowledge Graph
Section titled “2.6 — Layer 6: Knowledge Graph”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_neo4jexample (Prereq 2a §summary) confirmed as the substrate pattern. entity_resolutionfrom cocoindex (Prereq 2a top-recommendation #2) is the dedup primitive.- Substrate decision still open: Neo4j target vs PG
entity_relationshipsvs 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.
§3 — Cross-layer findings
Section titled “§3 — Cross-layer findings”Cross-cutting observations from §2.
3.1 — Vocabulary scatter
Section titled “3.1 — Vocabulary scatter”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.
3.2 — Layer-skip pattern
Section titled “3.2 — Layer-skip pattern”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_typetag) - 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_workspacesetc. 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_fieldsschema 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_idper Finding 02) - Its match-targets form a polymorphic relation (
question_matches.matched_entity_idper 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.”
3.7 — Cocoindex Layer mapping
Section titled “3.7 — Cocoindex Layer mapping”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_itemsis a Layer-5 class representing “knowledge artefacts” (citation-bearing facts, references, guides, intelligence-derived knowledge).workspacesis 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_integrationingest_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_pairs↔workspacesvia assignment (which app uses this pair?)q_a_pairs↔bid_questionsvia 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):
| Option | Shape | Pros | Cons |
|---|---|---|---|
| (a) Vocabulary-only | application_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 table | application_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 provenance ★ RATIFIED | Option (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. |
application_type = workspace.type | Current 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 currentapplication_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
workspacestable withapplication_type_idFK (Shape B holds — extended) - Finding 03 §3.2 inventory of 5 bid-specific satellites maps to
procurement_workspacesShape 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_templatesrename:form_templatesper §4.5 (form_type as own CV) with workspace.application_type binding which form_templates are relevant per workspace workspaces.typetext column retires — replaced byworkspaces.application_type_idFK (NOT a dual-axis subtype per Q-OQR1-04 — KISS single discriminator)kb_sectionretires 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-classguides+guide_sectionstables already exist. The data inventory §4 matrix shows guides serving multiple applications as primary producer (training/onboardingC+P,product-guidesC+P) and consumer across all apps. -
As APPLICATION (Layer 1 application_type CV):
product_guideis 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 (
guidestable populated). They likely don’t have product_guide APPLICATION workspaces yet (noapplication_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
guidesrows) 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_pairsis 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 relation —
question_matches(generalisingbid_question_matchesper OQ-Q113-C agreed in principle). - Workspace relevance is COMPUTED, not DECLARED — via
q_a_pairs.scope_tag && workspace.scope_tagoverlap (withanti_scope_tagexclusion).
Implications for schema (post-S235 ratified shape):
| Concern | Current shape | Ratified form-agnostic shape (S235) |
|---|---|---|
| Q&A pair identity | content_items.content_type='q_a_pair' row | q_a_pairs row (planned) |
| Pair → workspace | M: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 question | bid_questions.matched_content_ids UUID[] (legacy) | question_matches table with question_kind discriminator (Theme A) |
| Citers of the pair | bid_responses only (via implicit source_content_ids) | Polymorphic citations.citing_entity per Finding 02 |
| Pair lifecycle | content_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/a | Deferred 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:
- Empirical — today’s 395 prod q_a_pair rows are unassigned to any workspace. They function as a corpus the bid matcher draws from.
- 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.
- “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.
- 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.— empirically refuted (0/395 prod assignment), conflicts with “one record many views”.workspace_id UUID NOT NULLper0.9-intended-architecture.md§4.3— heavier than scope_tag; doesn’t match empirical state.q_a_pair_workspacesM:N junction
4.5 — Form-type = type + format split
Section titled “4.5 — Form-type = type + format split”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:
| Concept | Layer | Shape |
|---|---|---|
form_type | Layer 1 CV | Vocabulary table: bid, RFP, PQQ, ITT, tender, checklist, questionnaire, sales_proposal_template, … + definitions + synonyms (SSQ=standard-selection-questionnaire, etc.) + acronym mappings |
form_format | Layer 1 CV | Vocabulary table: docx, xlsx, pdf, html, md |
form_templates | Layer 5 instance | Carries form_type FK + form_format FK + version + question_count + extraction_status + cataloguer metadata |
form_template_fields | Layer 5 instance | Per-question detail within a form_template |
form_response | Layer 5 instance | A 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 name | Renamed to | Notes |
|---|---|---|
templates | form_templates (NOT bid_templates) | Theme A — form-type-agnostic |
template_fields | form_template_fields | Theme A |
template_requirements | form_template_requirements | Closes 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 discriminator | OQ-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 discriminatorform_type='bid' | 'rfp' | 'pqq' | 'itt' | 'framework' | 'dps' | 'gcloud'onquestion_matchesdistinguishes within the procurement workspace)bid_responses→ keep name (per above — the response is to a bid_question regardless of form_type)bid_state_machine→procurement_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_STATESconst tuple →PROCUREMENT_WORKFLOW_STATESlib/bid/bid-state-machine.ts→lib/procurement/procurement-workflow.tsper Q-OQR1-05templates→form_templates(per S234 — unchanged)template_fields→form_template_fields(per S234 — unchanged)template_requirements→form_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 inapplication_types.state_machine_configjsonb (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 = NULLuntil ready.
4.6 — Closing the foundational-question loop (S235 ratified)
Section titled “4.6 — Closing the foundational-question loop (S235 ratified)”| Question | Answer summary | Closes |
|---|---|---|
| 4.1 content_items ↔ workspaces M:N purpose | Knowledge-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 layer | Option (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 classification | Both content (content_type=‘guide’ + guides table) and application (application_type=‘product_guide’ workspace as core baseline) — orthogonal axes | Theme E sub-question |
| 4.4 Q&A form-agnostic implications | q_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 split | Two 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 item | Resolution from this ontology mapping |
|---|---|
| Applications layer above workspaces | RE-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 definitions | RESOLVED — 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-based | RESOLVED — 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 type | RESOLVED — both, orthogonal axes per §4.3. product_guide ships as core baseline application_type. |
| Citer space for citations | RESOLVED-DIRECTIONAL — citations.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 target | RESOLVED — form_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 addition | RESOLVED — 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 implications | RESOLVED-DIRECTIONAL — procurement_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 pattern | RESOLVED — satellite per application_type vocabulary entry; not every workspace.type. Closes OQ-Q113-A. |
edit_intent typed concept for re-classification | RESOLVED-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 cardinality | RE-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 (bid → procurement application_type) | RESOLVED-S235: rename pre-launch per Q-OQR1-02. BID_STATES → PROCUREMENT_WORKFLOW_STATES. lib/bid/bid-state-machine.ts → lib/procurement/procurement-workflow.ts. bid_workspaces → procurement_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 vocabularies | RESOLVED-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 vocabularies | RESOLVED-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 path | source_document created? | content_item.source_document_id set? |
|---|---|---|
Manual upload (/api/upload) | YES | YES (after extraction) |
URL ingest (/api/ingest/url) | NO | NULL |
RSS feed (feed_articles) | NO | NULL |
Manual content create (/api/items POST) | NO | NULL |
Bid library import (scripts/qa-migration/) | YES (historical Phew DOCX) | YES — planned |
MCP create (/api/mcp) | NO | NULL |
Under Option α (S234 ratified — slim-and-keep source_documents):
source_documentsretains 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_idbecomes NULLABLE to support admin-shared binaries (e.g. one Phew bid library DOCX serving multiple procurement workspaces).content_itemscontinues to have NO direct workspace FK; relationships viacontent_item_workspacesM: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_idanswers ownership orthogonally.
Under Option (c) application_types adoption:
source_documents.workspace_idremains the FK route toapplication_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.
§6 — Build order recommendation
Section titled “§6 — Build order recommendation”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”- 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. - Promote scattered enums to vocabulary tables with
provenanceenum (per Q-OQR1-11 —core/client/recommended). Candidates:BID_STATES→PROCUREMENT_WORKFLOW_STATESconst tuple (NOT a DB table — state-machine code stays code-driven per Q-OQR1-14 v1);requirement_types→ DB table with provenance. - 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) +provenanceenum. Baselinecorerows (Q-OQR1-03): procurement, intelligence, sales_proposal, product_guide, competitor_research, training_onboarding. Seed migration includes 6 core rows.form_types— vocab withapplicable_application_typestext[] (e.g.{procurement}for bid/rfp/pqq) +provenanceenum.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.
- Procurement rename (Q-OQR1-02 + Q-OQR1-05):
lib/bid/bid-state-machine.ts→lib/procurement/procurement-workflow.ts;BID_STATES→PROCUREMENT_WORKFLOW_STATES;bid_workspaces→procurement_workspaces. Lands in combined PR per Q-OQR1-16. - kb_section retirement (Q-OQR1-S235): drop from
workspaces_type_check; clean uplib/workspace-types.ts:88+app/api/workspaces/route.ts:80default +components/workspace/workspace-create-dialog.tsx:34+ MCP tool fallback. Same combined PR. - Define acronym/synonym mappings. SSQ, PQQ, ITT, RFP, DPS, G-Cloud, etc. — into
entity_aliases(renamingcategory→provenanceper Q-OQR1-11).
Phase 2: Formalise Layer 2 (Metadata Standards)
Section titled “Phase 2: Formalise Layer 2 (Metadata Standards)”- Author a
metadata-standards.mddocument (one section per domain). - Document the structural / descriptive / administrative / social-provenance split explicitly per entity.
- Bind each metadata element to its CV (e.g. content_items.content_type → content_type vocabulary).
- Promote application_type to a metadata element on content_items, workspaces, pipeline_runs (Theme E delivery).
- Promote form_type + form_format to metadata elements on form_templates (Theme A delivery).
Phase 3: Formalise Layer 3 (Taxonomy)
Section titled “Phase 3: Formalise Layer 3 (Taxonomy)”- Audit taxonomy_domains + taxonomy_subtopics for SKOS encoding opportunity. Either add
broader_term_id/narrower_term_idcolumns OR materialise SKOS RDF export. - Author application_type taxonomy. Is
sector_intelligencea sibling ofbidsor a parent? Decision needed (recommendation: peer at app-type level; intelligence informs bids via M:N content references, not via taxonomy hierarchy). - Author form_type taxonomy. Hierarchy: RFP / PQQ / ITT / tender all under “procurement_forms”. Checklist / questionnaire under “compliance_forms”. Etc.
Phase 4: Layer 4 (Thesaurus)
Section titled “Phase 4: Layer 4 (Thesaurus)”- Extend entity_aliases with SKOS BT/NT/SYN columns — or migrate to a SKOS-shaped
thesaurus_entriestable. - Author equivalence rules for q_a_pairs — when two pairs are equivalent (not just superseded).
- 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”- Schema is the de-facto ontology — formalise as a class-and-relation document.
- Adopt
application_type_idFK as the top-level class discriminator on workspaces (replacesworkspaces.typetext column per Option (c)). - 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_idfor provenance audit per Q-OQR1-07). - Extend citations as polymorphic relation (Finding 02).
citing_entityenum extended for sales_proposal_response + competitor_research_finding + training_unit + mcp_search_response per OQ-Q111-A. - Add
question_matchestable with discriminator (OQ-Q113-C).question_kindenum aligns withform_typesvocabulary. - Add per-application_type satellites following Finding 05 Option B pattern. Initial satellites:
procurement_workspaces(wasbid_workspaces— renamed S235), thensales_proposal_workspacesas that application ships. Each satellite carries typed columns promoted fromworkspaces.domain_metadataJSONB. - Make
source_documents.workspace_idNULLABLE per Q-OQR1-09 (admin-shared binaries). - Apply provenance enum across hybrid vocabularies per Q-OQR1-11 —
entity_aliases.categoryrename toprovenance; addprovenancecolumn toform_template_requirements,guides,coverage_targets.
Phase 6: Layer 6 (Knowledge Graph)
Section titled “Phase 6: Layer 6 (Knowledge Graph)”- Implement Knowledge Map UI surface per CX.32 RESOLVED-as-cocoindex.
- Substrate decision (Neo4j vs PG entity_relationships vs mempalace) — defer until S7 per Prereq 2a flagged uncertainty.
- Query patterns per application — what does a bid workspace query the KG for? What does a sales-proposals workspace query?
- 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”| Phase | Closes |
|---|---|
| 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.
§7 — What this prereq does NOT settle
Section titled “§7 — What this prereq does NOT settle”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.