Skip to content

Field-Consumer Dependency Map

DRIFT WARNING — added 2026-05-06. The Phase 0 schema investigation (docs/plans/content-items-cleanup-phase-0-investigation.md) found writer/ reader mappings that are stale or incomplete for several content_items columns (notably the 7 AI-telemetry columns documented as never-written by any of 10 ingest paths, the source_documents/source_document_id wiring gap, and feed_articles.updated_by being written but listed as not-written). Per-path findings cited in docs/plans/phase-0-investigation/0.1-*.md. Phase 0.4 (drift correction) is PAUSED pending Phase 0.6 synthesis output and re-ingest design decision. Treat the dependency tables as directionally-correct but not fully accurate until refresh complete.

Maps which database fields are consumed by which UI components, API routes, MCP tools, hooks, and scripts. Excludes test files. Focus on production consumers only.

Last updated: 05/05/2026 (S223 WP3 — feed_sources §6 extended for S222 W3-A native website scraping: API consumers added for POST/PATCH /api/intelligence/workspaces/:id/sources, FeedSourceCreateSchema async superRefine + parseBodyAsync chokepoint, etag/last_modified web parity); 28/04/2026 (S208 — ingest_source typed column added per S207 WP-A4; content_owner_id ingest-schema wiring noted per S206; S205C priors retained: summary rename; publication_status, superseded_by, review_cadence_days, next_review_date)

Note on Q&A pairs: There is no separate qa_pairs table. Q&A items are stored in content_items with content_type = 'q_a_pair' and use the answer_standard / answer_advanced columns.


Two reusable column constants (types/content.ts) govern most reads:

  • CONTENT_LIST_COLUMNS (used by browse, library, review): id, title, suggested_title, summary, primary_domain, primary_subtopic, content_type, platform, author_name, source_domain, thumbnail_url, captured_date, ai_keywords, classification_confidence, priority, freshness, user_tags, governance_review_status, metadata, verified_at, verified_by, source_document, brief, content, answer_standard, answer_advanced, content_owner_id, quality_score, source_document_id, citation_count, source_file, layer, starred

  • CONTENT_DETAIL_COLUMNS (extends LIST): adds source_url, file_path, secondary_domain, secondary_subtopic, classification_reasoning, classified_at, summary_data, created_at, updated_at, created_by, updated_by, source_bid, detail, reference, governance_review_status, governance_review_due, governance_reviewer_id, source_document_id, expiry_date, lifecycle_type

Source of truth: types/content.ts lines 207-228. The S164b ai_summary → summary rename (content_items.summary) is fully reflected; feed_articles.ai_summary is intentionally kept as a separate column.


The core table with ~60 columns. By far the most-consumed table in the system.

FieldUI ComponentsAPI RoutesMCP Tools/ResourcesHooksScriptsNotes
idAll content componentsAll content routesAll content toolsAll content hooksAll scriptsPrimary key, universal
titleContentCard, ReviewCard, ReaderView, EditorView, ItemDetail, RelatedByTags, RelatedByEntities, SearchResult/api/items, /api/review/queue, /api/governance/reviewget_content_item, search_knowledge_base, get_quality_briefing, all resourcesuseBrowseData, useLibraryData, useItemDetailData, useReviewQueueDatabatch-reclassify, batch-generate-summaries, kb-searchAlways present
suggested_titleSame as title (fallback display)Same as titleSame as titleSame as titleSame as titleAI-generated; displayed when title is generic
contentEditorView, ReaderView, ContentBody/api/items/[id], /api/review/queue, /api/uploadget_content_item, search_knowledge_baseuseBrowseData, useItemDetailDatabatch-reclassify, batch-generate-summariesFull raw text
content_typeContentCard, FilterPanel, ReviewCard/api/items, /api/review/queue, /api/review/cadenceget_content_item, get_content_items, search_knowledge_baseuseBrowseData, useLibraryDataAll ingestion scriptsCHECK constraint
FieldUI ComponentsAPI RoutesMCP Tools/ResourcesHooksScriptsNotes
primary_domainContentCard, FilterPanel, ReviewCard, RelatedByEntities, OwnedContentHealth/api/review/queue, /api/governance/review, /api/cron/freshness-transitions, /api/cron/quality-scoreget_content_item, get_quality_briefing, kb://coverage, search_knowledge_baseuseBrowseData, useLibraryDatabatch-reclassify, calibrate-coverage-thresholdsDomain filter everywhere
primary_subtopicContentCard, FilterPanel/api/review/queueget_content_item, get_quality_briefinguseBrowseData, useTopicLayerContentbatch-reclassify
secondary_domainItemDetail/api/review/queueuseItemDetailDatabatch-reclassifyDetail view only
secondary_subtopicItemDetail/api/review/queueuseItemDetailDatabatch-reclassifyDetail view only
classification_confidenceContentCard, ReviewCard, AiProcessingIndicators/api/review/queueget_content_item, get_quality_briefinguseBrowseDatabatch-reclassify0-1 range
classification_reasoningItemDetailuseItemDetailDatabatch-reclassifyDetail view only
classified_atItemDetailuseItemDetailDatabatch-reclassifyDetail view only
FieldUI ComponentsAPI RoutesMCP Tools/ResourcesHooksScriptsNotes
summaryContentCard, ReviewCard, ReaderView/api/items, /api/review/queueget_content_item, search_knowledge_base, get_quality_briefing, kb://qa/{id}useBrowseData, useLibraryDatabatch-generate-summaries, kb-searchClaude-generated; renamed from ai_summary S164b
ai_keywordsContentCard, FilterPanel/api/itemsget_content_item, search_knowledge_baseuseBrowseData, useLibraryDatabatch-reclassify-keywords, batch-reclassifyArray of strings
embedding/api/items/[id] (for related items)search_knowledge_base (via RPC)kb-search, batch scriptsvector(1024); never sent to UI directly
summary_dataItemDetail (structured summary)useItemDetailDatabatch-generate-summariesJSONB; structured executive/detailed/takeaways from generateSummary
FieldUI ComponentsAPI RoutesMCP Tools/ResourcesHooksScriptsNotes
source_urlItemDetail, ReaderView/api/items/[id]get_content_itemuseItemDetailDataingestion scriptsOriginal URL
source_domainContentCard/api/itemsuseBrowseDataExtracted domain
source_documentContentCard/api/review/queueuseBrowseDataImport origin name
source_document_idItemDetail, SourceDocumentInfo/api/source-documents/[id]get_document_versions, get_document_diffuseItemDetailDatabackfill-source-documentsFK to source_documents
source_bidItemDetailuseItemDetailDataFK to workspaces; bid-discovered content
source_file/api/itemsuseLibraryData (source file list)Promoted from metadata
platformContentCard, FilterPanel/api/itemsget_content_itemuseBrowseDataingestion scriptsCHECK constraint
author_nameContentCard/api/itemsuseBrowseDataingest scripts
captured_dateContentCard/api/itemsuseBrowseDataPublication date
file_pathItemDetail/api/items/[id]/filesuseItemDetailDataUploaded file path
thumbnail_urlContentCard/api/review/queueuseBrowseData
FieldUI ComponentsAPI RoutesMCP Tools/ResourcesHooksScriptsNotes
briefContentCard (preview)/api/items, /api/review/queueuseBrowseDatabackfill-layersProgressive depth
detailItemDetailuseItemDetailDatabackfill-layersDetail view only
referenceItemDetailuseItemDetailDatabackfill-layersDetail view only
layerContentCard, FilterPanel/api/items, /api/layers/[id]useBrowseData, useTopicLayerContentbackfill-layersMatches layer_vocabulary.key
FieldUI ComponentsAPI RoutesMCP Tools/ResourcesHooksScriptsNotes
answer_standardQaRow, QaAnswerDisplay, EditorView/api/itemssearch_qa_library, kb://qa/{id}useBrowseData, useQaEditModeimport_bid_library.pyStandard answer text
answer_advancedQaRow, QaAnswerDisplay, EditorView/api/itemssearch_qa_library, kb://qa/{id}useBrowseData, useQaEditModeimport_bid_library.pyAdvanced answer text
FieldUI ComponentsAPI RoutesMCP Tools/ResourcesHooksScriptsNotes
freshnessContentCard, FilterPanel, ReviewCard/api/review/queue, /api/cron/freshness-transitions, /api/freshness/calculateget_content_item, get_freshness_report, get_quality_briefinguseBrowseDataCHECK: fresh/aging/stale/expired
previous_freshness/api/cron/freshness-transitionsget_quality_briefingFor transition tracking
freshness_checked_atGovernanceSection (settings)/api/freshness/calculateLast calculation time
lifecycle_typeItemDetail/api/cron/freshness-transitionsuseItemDetailDataevergreen/date_bound/regulation/bid_discovered
expiry_dateItemDetail/api/cron/freshness-transitionsget_expiring_contentuseItemDetailDataHard expiry date

Publication and Supersession (S199–S202 §5.2 + S200 §5.5)

Section titled “Publication and Supersession (S199–S202 §5.2 + S200 §5.5)”
FieldUI ComponentsAPI RoutesMCP Tools/ResourcesHooksScriptsNotes
publication_statusContentCard, ReviewCard, EditorView, PublicationReviewQueue, PublicationBulkActionBar (S220 §5.3)/api/items POST + PATCH (/api/items/[id]), /api/review/queue (filter), /api/review/publication-bulk-action POST (S220 §5.3 bulk-approve / bulk-return-to-draft; body schema PublicationBulkActionBodySchema)get_content_item, get_quality_briefing, governance.update, supersession formattersuseReviewQueueDataNOT NULL DEFAULT 'published'. CHECK: draft | in_review | published | archived. Canonical lifecycle column (S202 §5.2 Phase 2). State-machine guard via validatePublicationTransition(). Bulk surface (§5.3) iterates per-item PATCH semantics with change_reason='bulk_approve'/'bulk_return_to_draft' audit literals; cap=50 / rate=20 req/min ratified S217 D-3+D-8.
superseded_bySupersedeContentDialog, ContentCard/api/items/[id] PATCH (clears on publish), /api/cron/review-cadencemcp supersession.ts, content tools (when set), governance.tsuseReviewActionsFK content_items.id. Self-referential. Set when an item is superseded by a newer version (S199 21/04/2026 migration 20260421222059).
review_cadence_daysGovernanceSection, ItemDetail (read)/api/cron/review-cadence, /api/governance/reviewgovernance.ts (cadence renewal helpers)Numeric review interval per item (S200 WP5 §5.5 Phase 1; backfilled 27/04/2026 in three cohorts: regulatory 365d, Q&A 180d, methodology/case-study 365d).
next_review_dateItemDetail (badge), GovernanceSection/api/cron/review-cadence, /api/governance/reviewgovernance.ts (cadence renewal helpers)Computed forward-looking review deadline from review_cadence_days. Auto-renewed via lib/governance/cadence-renewal.ts.
FieldUI ComponentsAPI RoutesMCP Tools/ResourcesHooksScriptsNotes
quality_scoreContentCard, QualityScore badge/api/cron/quality-score, /api/review/queueget_quality_briefing, get_quality_actionsuseBrowseData0-100 computed
previous_quality_score/api/cron/quality-scoreget_quality_briefingTrend tracking
quality_score_updated_at/api/cron/quality-score
governance_review_statusContentCard, ReviewCard/api/governance/review, /api/review/queue, /api/cron/freshness-transitionsget_content_item, update_governance_statususeBrowseData, useReviewQueueDataWorkflow state
governance_review_dueItemDetail/api/governance/reviewuseItemDetailDataDeadline
governance_reviewer_idItemDetail/api/governance/reviewuseItemDetailDataAssigned reviewer
verified_atContentCard, ReviewCard/api/review/queue, /api/cron/freshness-transitionsuseBrowseData, useReviewActionsSME verification
verified_byContentCard/api/review/queueuseBrowseDataWho verified
priorityContentCard/api/itemsget_content_itemuseBrowseDatahigh/medium/low
FieldUI ComponentsAPI RoutesMCP Tools/ResourcesHooksScriptsNotes
content_owner_idContentCard, OwnedContentHealth/api/governance/review, /api/cron/freshness-transitions, /api/items POST, /api/upload, /api/ingest/url, /api/items/[id] PATCHassign_content_owner, mcp create_content_itemuseBrowseDataFK to auth.users. Ingest schemas widened S206 (5 entry points) to accept caller-provided owner; lib/auth/owner-default.ts falls back to caller.
ingest_source/api/items POST, /api/upload, /api/ingest/url, /api/intelligence (worker), /api/bids/[id]/outcome/integratemcp create_content_itemscripts/ingest.py, scripts/ingest_markdown.pyPipeline attribution. Drives auto_v1_on_insert trigger’s CASE for change_reason (replaces S207-deleted v1 app writes). 11.6% NULL pre-OPS-41 backfill; now 0% post-S209 (verified 2026-05-06: 617/617 populated).
created_atItemDetail/api/items, /api/review/queueuseItemDetailData
updated_atItemDetail/api/items, /api/review/queue, /api/cron/freshness-transitionsuseItemDetailData
created_by/api/items, /api/upload
updated_by/api/governance/review
FieldUI ComponentsAPI RoutesMCP Tools/ResourcesHooksScriptsNotes
user_tagsContentCard, FilterPanel, RelatedByTags/api/items, /api/review/queueuseBrowseDatacleanup-tagsArray of strings
starredContentCard (star toggle)/api/items (toggle_star RPC)useBrowseDataBoolean flag
notesReviewCard, EditorView, ReviewHistorySection/api/itemsuseReviewActionsFree-form
metadataVarious (indirect via JSONB keys)Many routesuseBrowseDataMany scriptsJSONB; see §35 in schema ref
citation_countContentCard/api/itemsuseBrowseDataTrigger-maintained
FieldUI ComponentsAPI RoutesMCP Tools/ResourcesHooksScriptsNotes
archived_at/api/items/[id]/archive, /api/cron/quality-score (filter)All tools (filter is null)Soft delete
archived_by/api/items/[id]/archive
archive_reason/api/items/[id]/archive
FieldConsumersNotes
content_text_hashlib/dedup/content-dedup.ts onlyDedup detection; no UI or API consumer
parent_idapp/api/upload/route.ts, source-document componentsSelf-referential FK; lightly used
source_bidtypes/content.ts (CONTENT_DETAIL_COLUMNS), app/item/[id]/item-detail-client.tsxOnly 2 non-test consumers

Entity extraction results forming the context graph.

FieldUI ComponentsAPI RoutesMCP Tools/ResourcesHooksScriptsNotes
idEntityBadges/api/entities/[canonical_name]/*get_entity_relationshipsnormalise-entitiesPK
content_item_idRelatedByEntities/api/entities/[canonical_name], /api/certificationsget_entity_relationships, get_certification_statususeBrowseData (entity filter)backfill-entities, propagate-cert-metadataFK to content_items
entity_typeEntityBadges, EntityDetailPanel/api/entities/[canonical_name], /api/certificationsget_entity_relationships, get_certification_status, get_quality_briefingnormalise-entitiesCHECK constraint; 12 types
entity_name/api/entities/[canonical_name]normalise-entitiesOriginal text
canonical_nameEntityBadges, RelatedByEntities/api/entities/[canonical_name], /api/certificationsget_entity_relationships, get_certification_status, get_quality_briefingnormalise-entities, propagate-cert-metadataNormalised form
confidenceEntityBadges/api/entities/[canonical_name]0-1 range
context_snippet/api/entities/[canonical_name]backfill-context-snippetsExcerpt
entity_type_overrideEntityDetailPanel/api/entities/[canonical_name]/type, /api/certificationsget_certification_statusManual type correction
metadata/api/entities/[canonical_name]/metadata, /api/certifications, /api/cron/freshness-transitionsget_certification_status, get_quality_briefing, get_expiring_contentpropagate-cert-metadata, backfill-temporal-bridgeJSONB; certification expiry, version
normalisation_versionnormalise-entitiesBatch tracking
created_atnormalise-entities

All fields have at least one consumer. normalisation_version is script-only.


Generic containers for bids and KB sections.

FieldUI ComponentsAPI RoutesMCP Tools/ResourcesHooksScriptsNotes
idWorkspaceCard, WorkspaceSelector, WorkspaceDetailSheet/api/workspaces, /api/bids/[id]kb://bid/{id}, list_active_bids, show_bid_dashboarduseQuickAssign, useQaProvenanceseed-bid-test-dataPK
nameWorkspaceCard, WorkspaceSelector, WorkspaceDetailSheet/api/workspaces, /api/bids/[id]kb://bid/{id}, list_active_bids, show_bid_dashboard
descriptionWorkspaceDetailSheet/api/workspaceskb://bid/{id}
colorWorkspaceCard/api/workspacesHex colour
iconWorkspaceCard/api/workspacesIcon identifier
typeWorkspacesPage (count by type)/api/workspaceskb://bid/{id}bid or kb_section
status/api/workspaces, /api/bids/[id]/outcomelist_active_bidsWorkflow state
domain_metadataWorkspaceCard (buyer, deadline), BidListCard, BidContextProvider, ActiveBidsSection/api/workspaces, /api/bids/[id], /api/bids/[id]/outcomelist_active_bids, show_bid_dashboardseed-bid-test-dataJSONB; bid details (buyer, deadline, value, etc.)
is_archived/api/workspaces, /api/bidslist_active_bidsSoft delete
created_at/api/workspaces
updated_at/api/workspaces
created_by/api/workspaces
updated_by/api/workspaces
FieldConsumersNotes
updated_byOnly /api/workspaces GET (selected but not displayed)Selected but no known UI consumer

Extracted tender questions within bid workspaces.

FieldUI ComponentsAPI RoutesMCP Tools/ResourcesHooksScriptsNotes
idBidDetailPage, BidQuestionRow/api/bids/[id]/questions, /api/bids/[id]/responses/*get_bid_detail, show_bid_dashboard, get_bid_question, kb://bid/{id}seed-bid-test-dataPK
project_id/api/bids/[id]/questions (filter)get_bid_detail, show_bid_dashboardFK to workspaces; legacy name
question_textBidQuestionRow, BidDetailPage/api/bids/[id]/questionsget_bid_detail, get_bid_question, kb://bid/{id}, search_qa_libraryThe question itself
section_nameBidDetailPage (grouping)/api/bids/[id]/questionsget_bid_detail, kb://bid/{id}Section heading
section_sequence/api/bids/[id]/questions (ordering)get_bid_detailSort order
question_sequence/api/bids/[id]/questions (ordering)get_bid_detailSort order
word_limitBidQuestionRow/api/bids/[id]/questionsget_bid_detailMax word count
evaluation_weightBidQuestionRow/api/bids/[id]/questionsScoring weight
confidence_postureBidQuestionRow, BidDetailPage/api/bids/[id]/questions, /api/bids/[id]/questions/matchget_bid_detail, kb://bid/{id}, show_bid_dashboardMatch confidence
matched_content_idsBidDetailPage/api/bids/[id]/questions, /api/bids/[id]/questions/matchKB item matches
statusBidQuestionRow, BidDetailPage/api/bids/[id]/questionsget_bid_detail, kb://bid/{id}, show_bid_dashboardWorkflow state
has_variants/api/bids/[id]/questionsSub-variants flag
template_requirement_id/api/bids/[id]/templates/*FK to template_requirements
assigned_toBidQuestionRow/api/bids/[id]/questionsUser assigned
created_by/api/bids/[id]/questions
created_at/api/bids/[id]/questions
updated_at/api/bids/[id]/questions

AI-drafted and human-edited responses to bid questions.

FieldUI ComponentsAPI RoutesMCP Tools/ResourcesHooksScriptsNotes
idBidResponseEditor/api/bids/[id]/responses/[rId]get_bid_detailuseDraftStreamseed-bid-test-dataPK
question_id/api/bids/[id]/questions (join)get_bid_detail, show_bid_dashboardFK to bid_questions
response_textBidResponseEditor, BidResponsePreview/api/bids/[id]/responses/[rId], /api/bids/[id]/responses/draft*get_bid_detail, show_bid_dashboarduseDraftStreamStandard response
response_text_advancedBidResponseEditor/api/bids/[id]/responses/[rId]Advanced response
source_content_idsBidResponseEditor (citations)/api/bids/[id]/responses/[rId]KB source items
review_statusBidResponseEditor, BidDetailPage/api/bids/[id]/responses/[rId], /api/bids/[id]/readinessget_bid_detail, show_bid_dashboarddraft/review/approved/rejected
drafted_byPerItemTab (provenance)/api/bids/[id]/responses/draft, /api/provenance/item/[id]useItemProvenancePIPELINE_SYSTEM_USER_ID = AI-drafted; user UUID = human-drafted
last_edited_by/api/bids/[id]/responses/[rId]
approved_by/api/bids/[id]/responses/[rId]
metadata/api/bids/[id]/responses/draft*useDraftStreamDrafting metadata
version/api/bids/[id]/responses/[rId]Response version
overall_scoreQualityScore, BidContextProvider/api/bids/[id]/responses/draft*, /api/bids/[id]/readinessuseDraftStreamQuality check score
created_at/api/bids/[id]/responses/[rId]
updated_at/api/bids/[id]/responses/[rId]

Intelligence pipeline feed sources. Post-S222 W3-A: feed_sources gains direct API consumers for the §2.3.4 native-website-scraping flow.

FieldUI ComponentsAPI RoutesMCP Tools/ResourcesHooksScriptsNotes
idPATCH /api/intelligence/workspaces/:id/sources/:sourceId (route param)PK; accessed via get_due_feed_sources RPC for pipeline polling
namePOST + PATCH /api/intelligence/workspaces/:id/sources (FeedSource schemas)Writable via UI source-add flow
urlPOST + PATCH /api/intelligence/workspaces/:id/sourcesFeed URL. POST for source_type='web' triggers FeedSourceCreateSchema.superRefine async validateWebUrl HEAD pre-flight (S222 W3-A)
source_typePOST /api/intelligence/workspaces/:id/sources ('rss' | 'web' | 'api')Determines downstream pipeline branch + which validator runs at insert time
workspace_idPOST + seed-starter-pack route (server sets from URL param)FK to workspaces
is_activePOST + PATCH /api/intelligence/workspaces/:id/sources (default true)
polling_interval_minutesPOST + PATCH /api/intelligence/workspaces/:id/sources (default 30)Bounds: 5..1440
last_polled_atlib/intelligence/pipeline.tsUpdated by pipeline
last_polled_statuslib/intelligence/pipeline.tsUpdated by pipeline
last_polled_errorlib/intelligence/pipeline.tsUpdated by pipeline
consecutive_failureslib/intelligence/pipeline.tsUpdated by pipeline
article_countlib/intelligence/pipeline.tsIncremented by pipeline
etaglib/intelligence/pipeline.tsRFC 7232 conditional request header. Post-S222 W3-A also written for source_type='web' (web parity with RSS)
last_modifiedlib/intelligence/pipeline.tsRFC 7232 conditional request header. Post-S222 W3-A web parity (see etag row)
created_at
created_byPOST /api/intelligence/workspaces/:id/sources (server-resolved auth user)
updated_at

Key finding: Post-S222 W3-A, feed_sources has direct API consumers for create/update via /api/intelligence/workspaces/:id/sources (POST + PATCH) and the /seed-starter-pack flow. The intelligence pipeline (lib/intelligence/pipeline.ts) remains the polling consumer via the get_due_feed_sources RPC; pipeline writes last_polled_*, etag, last_modified, consecutive_failures, article_count. The split is: routes write user-supplied fields (name/url/source_type/polling_interval_minutes/is_active); pipeline writes runtime telemetry (everything else).

Validation note (S222 W3-A): FeedSourceCreateSchema in lib/validation/schemas.ts:1071-1095 runs an async .superRefine that performs an HTTP HEAD pre-flight on source_type='web' rows via validateWebUrl (lib/intelligence/url-validation.ts). Routes consuming this schema MUST use parseBodyAsync from @/lib/validation/index.ts:55+ not the synchronous parseBody — Zod throws “Encountered Promise during synchronous parse” otherwise. The validation-sweep guard (feedback_validation_sweep_safeparse_ban) covers parseBody adoption; parseBodyAsync is the async-superRefine variant of the same chokepoint.


Intelligence pipeline article records.

FieldUI ComponentsAPI RoutesMCP Tools/ResourcesHooksScriptsNotes
idPK
feed_source_idFK to feed_sources
workspace_idFK to workspaces
external_urlArticle URL
external_idRSS GUID
title
raw_contentFull content
relevance_scoreAI relevance 0-1
relevance_categoryhigh/medium/low/irrelevant
relevance_reasoningAI reasoning
matched_categories
ai_summary
prompt_version_idFK to feed_prompts
passedAbove threshold flag
published_at
content_item_idFK to content_items (when promoted)
ingested_at
created_at
updated_at

Key finding: feed_articles also has no direct UI or API route consumers. All access is through lib/intelligence/pipeline.ts. This is entirely pipeline-internal infrastructure. Articles that pass relevance scoring are promoted to content_items and linked via content_item_id.


AI-generated change reports.

FieldUI ComponentsAPI RoutesMCP Tools/ResourcesHooksScriptsNotes
idChangeReportDetail, ChangeReportList/api/change-reports/[id], /api/change-reports/list, /api/change-reports/latestPK
frequencyChangeReportList/api/change-reports/[id], /api/change-reports/list, /api/change-reports/latestweekly/daily
period_startChangeReportDetail/api/change-reports/[id], /api/change-reports/list, /api/change-reports/latestCoverage window
period_endChangeReportDetail/api/change-reports/[id], /api/change-reports/list, /api/change-reports/latestCoverage window
item_countChangeReportList/api/change-reports/[id], /api/change-reports/list, /api/change-reports/latest
domain_summariesChangeReportDetail/api/change-reports/[id], /api/change-reports/listJSONB
narrative_summaryChangeReportDetail/api/change-reports/[id], /api/change-reports/list, /api/change-reports/latestOverall narrative
generated_atChangeReportList/api/change-reports/[id], /api/change-reports/list, /api/change-reports/latest
generated_byChangeReportList/api/change-reports/[id], /api/change-reports/list, /api/change-reports/latestModel identifier
tokens_usedChangeReportDetail/api/change-reports/[id], /api/change-reports/list, /api/change-reports/latest
item_idsChangeReportDetail/api/change-reports/[id]UUID array
metadataDead field — never selected in any route
created_atChangeReportList/api/change-reports/[id], /api/change-reports/list
created_byDead field — exists in schema, never selected
FieldNotes
metadataNever selected in any API route or consumed by UI
created_byNever selected in any API route or consumed by UI

Domain-based guide definitions.

FieldUI ComponentsAPI RoutesMCP Tools/ResourcesHooksScriptsNotes
id/api/guides, /api/guides/[slug], /api/guides/[slug]/sectionsseed-phew-guidesPK
slug/api/guides (lookup), /api/guides/[slug]seed-phew-guidesURL-friendly identifier
name/api/guides, /api/guides/[slug]seed-phew-guidesDisplay name
description/api/guides, /api/guides/[slug]seed-phew-guides
guide_type/api/guides, /api/guides/[slug]seed-phew-guides
domain_filter/api/guides, /api/guides/[slug]seed-phew-guidesTaxonomy domain link
icon/api/guides, /api/guides/[slug]
color/api/guides, /api/guides/[slug]
display_order/api/guides, /api/guides/[slug]seed-phew-guidesSort order
is_published/api/guides, /api/guides/[slug]seed-phew-guidesVisibility flag
created_by/api/guides, /api/guides/[slug]
created_at/api/guides, /api/guides/[slug]
updated_at/api/guides, /api/guides/[slug]

Key finding: Guides have API route consumers but no direct UI component consumers. They are consumed indirectly via the guide section mapping system (lib/guide-section-mapping.ts) which matches content items to guide sections.


Sections within guides, defining expected content structure.

FieldUI ComponentsAPI RoutesMCP Tools/ResourcesHooksScriptsNotes
id/api/guides/[slug]/sections, /api/guides/[slug]/sections/[sectionId]PK
guide_id/api/guides/[slug]/sectionsFK to guides
section_name/api/guides/[slug]/sectionsseed-phew-guidesSection title
description/api/guides/[slug]/sections
display_order/api/guides/[slug]/sectionsseed-phew-guidesSort order
subtopic_filter/api/guides/[slug]/sectionsUsed in guide-section-mapping.ts
expected_layer/api/guides/[slug]/sectionsUsed in guide-section-mapping.ts
content_type_filter/api/guides/[slug]/sectionsUsed in guide-section-mapping.ts
is_required/api/guides/[slug]/sectionsGap analysis flag
created_at/api/guides/[slug]/sections
updated_at/api/guides/[slug]/sections

Tracks uploaded source documents with version history. Each row is a specific version; the parent_id chain links versions together.

FieldUI ComponentsAPI RoutesMCP Tools/ResourcesHooksScriptsNotes
idSourceDocumentInfo, SourceDocumentHistory/api/source-documents/[id], /api/source-documents/[id]/versions, /api/source-documents/[id]/diff, /api/source-documents/[id]/send-to-review, /api/uploadget_document_versions, get_document_diffuseDiffReviewbackfill-source-documentsPK
filenameSourceDocumentInfo/api/source-documents/[id], /api/source-documents/[id]/diffget_document_versions, get_document_diffbackfill-source-documentsNormalised filename
original_filenameSourceDocumentInfo/api/source-documents/[id]As uploaded
mime_typeSourceDocumentInfo/api/source-documents/[id]e.g. application/pdf
file_sizeSourceDocumentInfo, SourceDocumentHistory/api/source-documents/[id]Bytes
content_hash/api/source-documents/[id], /api/upload (dedup check)backfill-source-documentsMD5 of raw file bytes
versionSourceDocumentInfo, SourceDocumentHistory, DocumentDiffPage/api/source-documents/[id], /api/source-documents/[id]/diffget_document_diffVersion number in chain
parent_id/api/source-documents/[id]/diff, /api/upload (re-upload detection)get_document_diffbackfill-source-documentsFK to previous version (self-referential)
storage_pathSourceDocumentInfo/api/source-documents/[id]Supabase Storage path
statusSourceDocumentInfo, SourceDocumentHistory/api/source-documents/[id], /api/uploadCHECK: uploaded/processing/processed/failed
extracted_text— (no production reader; permanently NULL on the pipeline path since id-392)edit-intent sweep (residual write), l_records.py producer payload (residual select)Documented residual — column retirement deferred to the rebase charter; the document body is composed from content_chunks.content ordered by position (reference_items.body fallback on the URL route) via lib/source-documents/body.ts
extraction_metadata/api/uploadJSONB; page count, table count
workspace_id/api/source-documents/[id], /api/uploadFK to workspaces
pipeline_run_id/api/uploadFK to pipeline_runs
uploaded_bySourceDocumentInfo, SourceDocumentHistory, DocumentDiffPage/api/source-documents/[id] (read), /api/ingest/folder-drop, /api/ingest/url (write)FK to auth.users (id-407). Fill-once writer lib/source-documents/uploader-attribution.ts: UPDATE guarded by uploaded_by IS NULL so the first human attribution is never rewritten. NULL is the meaningful system-minted value — the diff adapter lib/diff/adapters/source-document-revision.ts maps non-null to the resolved display name and NULL to the literal 'System'. Search RPCs project it under the alias sd.uploaded_by AS "created_by" (20260716120000_id145_37_…sql:406,467). Stamped caller-side on the MINT path only (folder-drop inside the writer-fence hold; ingest/url post-reference_ingest RPC, degrading into the route’s warnings[] channel on failure). The Python walk stays NULL by design (uploaded_by is absent from the ON CONFLICT SET list, so a re-walk cannot clobber a human attribution). No backfill of historical rows (DR-093).
created_atSourceDocumentInfo, SourceDocumentHistory, DocumentDiffPage/api/source-documents/[id], /api/source-documents/[id]/diff
archived_at/api/source-documents/[id]Soft delete
archived_by/api/source-documents/[id]

Key consumer paths: SourceDocumentInfo and SourceDocumentHistory components fetch via /api/source-documents/[id] and /api/source-documents/[id]/versions. The diff page (app/documents/[id]/diff/page.tsx) queries directly via Supabase. The upload route (/api/upload) creates and updates rows during file processing. MCP tools get_document_versions and get_document_diff read filename, version, and parent chain. Impact analysis (lib/source-documents/source-document-impact.ts) reads id, filename, and parent_id.


Per-user read tracking. RLS-scoped to user_id = auth.uid().

FieldUI ComponentsAPI RoutesMCP Tools/ResourcesHooksScriptsNotes
id/api/read-marksPK
content_item_idReadToggleButton (via context), ItemActionBar, ReaderView/api/read-marks (GET batch check, POST toggle, DELETE unmark)useItemDetailData, useProgressFK to content_items
read_at/api/read-marksuseProgress (streak calculation)When the item was read
source/api/read-marks (POST)How the read was recorded: manual/review/digest/bulk
user_id/api/read-marks (RLS scoped)RLS filter: auth.uid()

Key consumer paths: The ReadMarksProvider context (contexts/read-marks-context.tsx) is the central consumer, mounted at the app layout level. It fetches counts via /api/read-marks and provides isRead(), toggleRead(), markRead(), markBulkRead(), and checkReadStatus() to all children. Direct UI consumers are ReadToggleButton, ItemActionBar, and ReaderView (via context). useProgress queries read_marks directly for streak calculation (last 30 days). lib/dashboard.ts and lib/reorient.ts read read_marks for activity metrics. Browse page (app/browse/browse-content.tsx) and digest page (app/change-reports/page.tsx) trigger lazy loading of read mark counts.


DB-driven content layer definitions. Consumed via LayerVocabularyProvider context and useLayerVocabulary() hook. Admin-editable in Settings.

FieldUI ComponentsAPI RoutesMCP Tools/ResourcesHooksScriptsNotes
idLayersSection (admin CRUD)/api/layers, /api/layers/[id], /api/layers/reorderPK
keyContentCard, ContentRow, FilterPanel, CoverageLayerFilter, LayerSwitcherNav, LayerSuggestionBanner, UploadTabContent, IngestionSuccessCard, ContentLayerSelector, GuideSection, FileUploadDialog, SectionFormDialog/api/layers, /api/layers/[id]UNIQUE; layer identifier (e.g. sales_brief)
labelContentCard, ContentRow, FilterPanel, CoverageLayerFilter, LayerSwitcherNav, LayerSuggestionBanner, UploadTabContent, IngestionSuccessCard, ContentLayerSelector, GuideSection, FileUploadDialog, SectionFormDialog/api/layers, /api/layers/[id]Display label (e.g. “Sales Brief”)
descriptionContentLayerSelector, LayerSuggestionBanner/api/layers, /api/layers/[id]Layer description
display_orderLayersSection (drag reorder)/api/layers, /api/layers/[id], /api/layers/reorderUI ordering
is_activeLayersSection (toggle)/api/layers (filter), /api/layers/[id]Soft delete
created_at/api/layers
updated_at/api/layers

Key consumer paths: The LayerVocabularyProvider context (contexts/layer-vocabulary-context.tsx) fetches all active layers at app layout level and provides getLayerKeys(), getLayerLabel(), getLayerDescription(), and refresh() to all children. 14 components consume the context via useLayerVocabulary(). The admin LayersSection component manages CRUD via /api/layers/* routes. lib/layer-inference.ts uses layer key constants for programmatic layer assignment. lib/client-config.ts provides fallback layer definitions when the DB fetch fails.


All 3 fields (content_item_id, workspace_id, assigned_at) are consumed by:

  • API: /api/items/[id]/workspaces, /api/items/batch-workspaces, /api/workspaces/[id]/items, /api/bids/[id]
  • Hooks: useBrowseData (workspace filter), useQaProvenance
  • Lib: lib/intelligence/pipeline.ts

Key fields consumed by:

  • Lib: lib/reorient.ts (team changes, recent work), lib/dashboard.ts (activity feed), lib/ai/change-reports.ts (modified count)
  • API: /api/upload (insert version 1), /api/ingest/url (insert version 1)
  • Trigger-populated; immutable records

Consumed by:

  • Lib: lib/dashboard.ts, lib/reorient.ts (bid activity tracking)
  • API: /api/bids/[id]/responses/[rId]/history

Fields consumed only by lib/intelligence/pipeline.ts (loadCompanyContext):

  • name, sectors, services, key_topics, target_customers, value_proposition
  • No UI consumers — pipeline-internal

Consumed by:

  • API: /api/bids/[id]/responses/[rId] (citations)
  • MCP: cite_content tool
  • Trigger updates content_items.citation_count

Consumed by:

  • MCP: get_quality_briefing (quality_flag and coverage_alert types)
  • Lib: lib/dashboard.ts (unread count)
  • No direct UI component consumers in application code (consumed via dashboard data)

Consumed by:

  • API: /api/cron/freshness-transitions (posture lookup)
  • MCP: get_quality_briefing (threshold lookup)
  • Components: GovernanceSection in settings

Application-level role assignments (5 columns: id, user_id, role, display_name, created_at/updated_at). Nearly all consumers follow one pattern: read role for auth gating.

Consumed by:

  • Hook: useUserRole (role via user_id)
  • Lib: lib/auth/client.ts, lib/cron-auth.ts, lib/mcp/auth.ts (role lookup), lib/source-documents/source-document-notifications.ts (user_id where admin)
  • API: /api/admin/users (CRUD), /api/admin/users/invite (pre-create entry), /api/users/display-names (display_name), /api/review/history (display_name), /api/mcp/[transport] (role), /api/dashboard (role)
  • Pages: app/page.tsx, app/review/page.tsx, app/item/new/page.tsx, app/item/new/batch/page.tsx (all read role for gating)
  • Components: ContentOwnerManagement (user_id, role for owner picker)

Standardised tender template requirements with embeddings and domain classification. No UI component consumers.

Consumed by:

  • Lib: lib/templates/template-coverage.ts (semantic matching), lib/content/content-suggestions.ts (gap analysis)
  • API: /api/cron/content-gaps (gap detection)
  • Scripts: catalogue-standard-sq.ts, catalogue-charnwood-itt.ts (import), calibrate-coverage-thresholds.ts

Table.FieldConsumer CountNotes
content_items.content_text_hash1 (lib/dedup/content-dedup.ts)Dedup-only; consider if this should be more widely used
content_items.source_bid2 (types, item detail client)FK to workspaces but barely referenced
content_items.parent_id2 (upload route, source-document components)Self-referential hierarchy unused elsewhere
digests.metadata0Dead field
digests.created_by0Dead field
bid_responses.overall_scoreLow (draft stream, quality check, readiness)Quality check score, sparse UI consumption
feed_sources.* (all fields)1 (pipeline only)Entire table is pipeline-internal
feed_articles.* (all fields)1 (pipeline only)Entire table is pipeline-internal
company_profiles.* (all fields)1 (pipeline only)Entire table is pipeline-internal

Tables with No Direct UI Component Consumers

Section titled “Tables with No Direct UI Component Consumers”
  1. feed_sources — pipeline-internal only
  2. feed_articles — pipeline-internal only
  3. company_profiles — pipeline-internal only
  4. guides — API routes only, consumed indirectly via guide-section-mapping
  5. guide_sections — API routes only, consumed indirectly via guide-section-mapping
  6. governance_config — API/MCP only, GovernanceSection in settings is the sole component
  7. notifications — consumed via dashboard data, no direct component
  8. template_requirements — lib/API/scripts only, no UI component consumers
  1. content_items browse path: useBrowseData hook → CONTENT_LIST_COLUMNS → 33 fields per row
  2. content_items detail path: useItemDetailData + SSR page → CONTENT_DETAIL_COLUMNS → ~45 fields per row
  3. content_items review path: /api/review/queueREVIEW_COLUMNS → 27 fields per row
  4. bid_questions + bid_responses bid path: Always fetched together; ~15 fields each
  • [path] lib/templates/template-coverage.ts no longer exists on main; the symbol moved to lib/domains/procurement/form-templating/template-coverage.ts (doc line 633 cites the old path).
  • [route] Doc line 169 cites /api/bids/[id]/outcome/integrate; the live route is /api/procurement/[id]/outcome/integrate (bid → procurement umbrella rename, S248).
  • [path] The kh_code_sources frontmatter glob lib/dedup/content-dedup.ts matches zero files under the public repo — the lib/dedup/ directory does not exist on main. The doc body still cites lib/dedup/content-dedup.ts as the sole consumer of content_text_hash (lines 197 and 647). Live dedup primitives live elsewhere (lib/q-a-pairs/dedup-merge.ts for curator-approved Q&A pair merges, lib/entities/entity-dedup.ts for entity dedup); neither is a 1:1 replacement for the documented content_text_hash dedup consumer, so the field-consumer row needs a human to confirm where hash-based content dedup now lives (or whether it was retired).