05 — Q&A flow
⚠️ DR-038/DR-025 CONSUMER-SIDE NOTE (S462). Core §2–§7 (two-tier model, sidecar, idempotency, citations, question_matches two-step retrieval) stand. Workspace-keyed CONSUMER language is legacy per DR-038: the §1.1/§7.2
workspaces.scope_tagmatch predicate re-anchors to the form instance; §8.2/§9.1 “per-workspace” tunables become per-form or per-tenant; §9 UC5 review routing is re-expressed as the DR-025/026 knowledge-admission/proposal gate. Procurement rework: ID-145.
05 — Q&A flow
Section titled “05 — Q&A flow”Last verified: 15/05/2026 (S240 Wave 2 split — WP4 architecture-split parallel sub-doc)
Scope: Q&A as a separate domain; q_a_pairs corpus-level pattern; q_a_extractions derived cache; markdown sidecar v1; cocoindex idempotency; citations polymorphic shape; question_matches with question_kind discriminator + separate scoring columns; Q&A write-back (UC6) + promotion (UC5) ratifications; Pattern A/B parser retire; anti-patterns.
Status: [CURRENT-CANONICAL]
Layer: 2 — references 01-vision.md for product framing; anchors schemas in 04-workspace-types.md §5 + §11; downstream consumers (08-new-features.md bid-feedback loop, 09-diagrams.md Q&A round-trip ERD) reference here.
Companion sub-docs: 01-vision.md (ratified S238 pilot), 02-data-flow.md, 03-tech-stack.md, 04-workspace-types.md, 06-mcp-tooling.md (gated on Theme F), 07-collapse-list.md, 08-new-features.md, 09-diagrams.md.
1. Q&A as a separate domain
Section titled “1. Q&A as a separate domain”Q&A is the fifth sub-doc of the WP4 architecture-split family. It owns the Q&A round-trip — from corpus-level q_a_pairs storage through the markdown sidecar ingest pattern, the cocoindex extraction and idempotency constraints, the polymorphic citations shape, the question_matches retrieval substrate, and the UC5/UC6 write-back ratifications. Schema foundations (q_a_pairs table shape, q_a_extractions derived-cache shape) are established here — q_a_pairs corpus-level shape is anchored in 04-workspace-types.md §5 for full detail; q_a_extractions derived-cache shape is owned in §3 of this sub-doc. 02-data-flow.md owns the cocoindex ingest stages; this sub-doc owns the Q&A-domain-specific flow within them.
Q&A is a peer domain to content_items, not a sub-type of it. A q_a_pair is a curated, corpus-level record of a question-and-answer pair: self-contained, versioned, governed, and ready for direct retrieval by Claude via the MCP q_a_search tool. It is NOT a workspace-partitioned artefact — the Q&A corpus is shared across all workspaces; workspace relevance is computed at query time via scope_tag overlap, not via a static FK.
1.1 The corpus-level shape
Section titled “1.1 The corpus-level shape”q_a_pairs does not carry a direct workspace_id FK. Workspace relevance is expressed as:
WHERE q_a_pairs.scope_tag && workspaces.scope_tag AND NOT (q_a_pairs.anti_scope_tag && workspaces.scope_tag)This shape is RATIFIED-S235 per Q-OQR1-06 (docs/plans/phase-0-investigation/10-feedback-investigation-findings/00-synthesis-v2.md §3.6; docs/plans/phase-0-investigation/0.9-decision-graph.md §11.1 ONT.5; WP-ONTO-R1 §4 in docs/plans/phase-0-investigation/phase-b-prerequisite-1-onthology-pipeline.md §4.4). Migration STILL-OPEN per Q-OQR1-16 combined-PR scope (Q-OQR1-16 item 10 per 0.9-decision-graph.md §11.3).
1.2 Empirical ratification anchor
Section titled “1.2 Empirical ratification anchor”The corpus-level cardinality is grounded in production data. 0 of 395 q_a_pair rows in prod are assigned to any workspace via the existing content_item_workspaces junction (verified S234, cited per docs/plans/phase-0-investigation/phase-b-prerequisite-1-onthology-pipeline-feedback-investigation.md §4.4). Q&A pairs in production today function as a shared corpus pool; workspace relevance is the lookup behaviour, not the storage shape. The Wikipedia Principle (“one record, many views” per 01-vision.md §1.2) holds here directly — one Q&A pair about “ISO 27001 certification status” is one record cited by procurement workspaces, sales-proposal workspaces, and competitor-research workspaces alike, not three duplicates.
Full table shape + cardinality is established in 04-workspace-types.md §5. This sub-doc covers the Q&A flow — how pairs are produced, stored, retrieved, and revised.
1.3 Application-type cross-coupling
Section titled “1.3 Application-type cross-coupling”Q&A is a cross-application-type domain. All six baseline application types (procurement, intelligence, sales_proposal, product_guide, competitor_research, training_onboarding per 04-workspace-types.md §3.2) share the same q_a_pairs corpus. A Q&A pair that originated from a procurement bid response (origin_kind='derived_from_bid_response') is equally available to a sales-proposal workspace if its scope_tag overlaps — the Wikipedia Principle (“one record, many views”) applies at the Q&A domain level.
The question_matches table connects application-specific form questions (procurement bid questions, etc.) to corpus-level Q&A pairs. The question_kind discriminator on question_matches (§7) is what keeps the cross-application-type matching clean — a bid-kind match is a procurement-workspace concern; a future rfp-kind match is also a procurement-workspace concern; sales-proposal workspace matching would use a different question_kind value aligned with its form-type vocabulary.
2. q_a_pairs schema sketch
Section titled “2. q_a_pairs schema sketch”This section carries a brief shape recap to frame the flow. The full schema — typed columns, indexes, triggers — lives in 04-workspace-types.md §5 (forward-ref per construction guide §6.2). This sub-doc does NOT duplicate column definitions; it names the shape-critical columns that govern the flow described in §§3-9.
2.1 Shape-critical columns
Section titled “2.1 Shape-critical columns”| Column | Role in Q&A flow |
|---|---|
question_text + alternate_question_phrasings TEXT[] | Retrieval substrate (embedded + FTS); alternate phrasings cover Shape D context variants |
answer_standard TEXT NOT NULL + answer_advanced TEXT NULL | Tiered answer body; mirrors Phew-style audit-6col shape; answer_advanced NULL for non-tiered clients |
scope_tag TEXT[] NOT NULL DEFAULT '{}' | Workspace-relevance filter (corpus-level per Q-OQR1-06) |
anti_scope_tag TEXT[] NOT NULL DEFAULT '{}' | Explicit exclusion from workspaces whose scope_tag matches |
source_workspace_id UUID NULL | Provenance audit only — records originating workspace for origin_kind='derived_from_bid_response' (NOT a workspace-scoping signal) |
origin_kind TEXT NOT NULL | Enum: extracted_from_corpus / curated_explicit / derived_from_bid_response / imported_legacy |
question_embedding vector(1024) | Semantic retrieval (text-embedding-3-large per CLAUDE.md; embedded over question_text + alternate_question_phrasings concatenated) |
publication_status TEXT NOT NULL DEFAULT 'draft' | Lifecycle: draft / in_review / published / archived |
superseded_by UUID NULL | UC8 merge lineage; self-referencing FK to q_a_pairs |
valid_from TIMESTAMPTZ + valid_to TIMESTAMPTZ NULL | Temporal validity (Shape A per S16 §6.1); valid_to NULL = currently valid; version-on-cite at ship time per §6.0.3 in 0.9-edit-flow-investigation.md |
2.2 GIN indexes
Section titled “2.2 GIN indexes”Two GIN indexes are the workspace-relevance substrate:
idx_q_a_pairs_scope_tagonscope_tag— supports&&overlap operator.idx_q_a_pairs_anti_scope_tagonanti_scope_tag— supports exclusion filter.
The prior idx_q_a_pairs_workspace (workspace-partition index) is [RATIFIED-DO-NOT-BUILD] per Q-OQR1-06; see §11. Full index set in 04-workspace-types.md §5.
2.3 Nullable source_workspace_id
Section titled “2.3 Nullable source_workspace_id”source_workspace_id is NULLABLE per Q-OQR1-07 (00-synthesis-v2.md §3.6 + N11 RESOLVED-S234; phase-b-prerequisite-1-onthology-pipeline-feedback-investigation.md §4.4). It records the originating workspace for audit — which procurement workspace generated the bid response that was later promoted to this Q&A pair. It is NOT used in workspace-relevance filtering. Full provenance chain reaches back through source_content_item_id → content_items → source_documents.
The source_workspace_id is populated at UC5 promotion time (§9) when origin_kind='derived_from_bid_response'. For Q&A pairs with origin_kind='extracted_from_corpus' or 'curated_explicit', source_workspace_id is NULL — the pair has no originating workspace, only a corpus-level source_content_item_id (or no content-item source at all for fully manually authored pairs). This null-pattern is the correct expression of corpus-level identity: the pair’s provenance is carried in origin_kind + source_content_item_id; source_workspace_id is a supplementary audit column, not a classification signal.
3. q_a_extractions derived cache
Section titled “3. q_a_extractions derived cache”3.1 The two-tier model
Section titled “3.1 The two-tier model”Q&A production follows a two-tier model per docs/plans/phase-0-investigation/0.9-spike-S16-qa-schema-design.md §6 (“One golden record, many extractions”):
q_a_pairs— the curated golden record. Promoted from an extraction, authored directly, or promoted from a bid response (UC5). This is the corpus.q_a_extractions— the derived cache. LLM or parser output from running an extractor over acontent_itemsrow. An extraction may or may not be promoted to aq_a_pair; once promoted,promoted_to_pair_idcaptures the lineage.
Extractions are invalidated — but NOT auto-deleted — when their source content changes (cocoindex Δ triggers invalidated_at set). New extraction rows are emitted for the new content; the invalidated rows are retained for audit comparison.
3.2 extractor_kind lineage
Section titled “3.2 extractor_kind lineage”The extractor_kind column on q_a_extractions tracks the extractor that produced each row. Shape-critical values for the Q&A flow:
| Value | Description |
|---|---|
prior_bid_response | Extraction from a historical bid response (Phew migration helper — one-shot, NOT a recurring v1 ingestion path per B1 RESOLVED-S234) |
llm_extraction | One-shot migration helper via ExtractByLlm; not recurring v1 path (Q3.5 RESOLVED-MIGRATION-HELPER-ONLY) |
yaml_frontmatter_v1 | Canonical client-authored template shape |
markdown_heading_v1 | H2/H3/H4-with-bold-question extraction (Shape C/D per S16 §4.3-§4.4) |
Full extractor_kind enum per S16 §6.3 (0.9-spike-S16-qa-schema-design.md §6.3). extractor_kind='prior_bid_response' is the UC5-adjacent path — extraction from Phew’s historical bid library that feeds the corpus pre-launch.
3.3 History mirror
Section titled “3.3 History mirror”q_a_pair_history mirrors content_history for Q&A pairs — a trigger-written history row is appended on every q_a_pairs UPDATE (carrying valid_from / valid_to transition). This gives the version-on-cite substrate required by §6.0.3 of 0.9-edit-flow-investigation.md — a shipped bid response cites q_a_pair.id at a specific version snapshot; that snapshot is recoverable via the history table.
The history table and its trigger schema are owned in this sub-doc’s §3.3; the ERD rendering of the trigger relationship is forward-referenced to 09-diagrams.md §2.
3.4 Relationship to the cocoindex flow
Section titled “3.4 Relationship to the cocoindex flow”q_a_extractions is the cocoindex output target for the Q&A extraction stage. The flow:
- Cocoindex source binding observes a markdown sidecar file or corpus content_item change.
- The outer
@coco.fnadapter fires (invalidated by any byte change in the source file). - The inner
extract_q_a(content_text: str, …)function fires (invalidated only whencontent_textchanges — §5 layered fn-shape). ExtractByLlmwith typed Pythonoutput_typeproduces structured Q&A extraction output.- Cocoindex UPSERTs into
q_a_extractionsviapostgres.mount_table_target(managed_by="user"). q_a_extractions.invalidated_atis set by a post-flow trigger when the source changes — keeping the old extraction row for audit comparison while the new extraction row lands.
The full cocoindex flow-stage topology lives in 02-data-flow.md; this sub-doc owns only the Q&A-specific stage detail.
4. Markdown sidecar v1 pattern
Section titled “4. Markdown sidecar v1 pattern”4.1 The sidecar pattern
Section titled “4.1 The sidecar pattern”The markdown sidecar v1 pattern is the bridge between the Q&A corpus and the cocoindex ingest pipeline for source-file-backed Q&A pairs. A q_a_pair whose origin_kind='extracted_from_corpus' or 'curated_explicit' may have a corresponding markdown sidecar file in the source folder — a .md file at source_documents.original_path that carries the canonical GFM content for that Q&A pair.
When the sidecar file changes, cocoindex’s source binding detects the change; the outer @coco.fn fires; the inner extraction function re-runs. The sidecar pattern allows in-platform Q&A editing (via the Tiptap ContentEditor per UC6) to propagate back to the folder via the UC1 Candidate A write-back pattern (per 0.9-edit-flow-investigation.md §6.1).
4.2 Promotion gate
Section titled “4.2 Promotion gate”The v1 sidecar promotion gate is CLOSED-CONDITIONAL per COCO.10 (0.9-decision-graph.md §11.2). The condition is the layered fn-shape requirement (§5.2 below) — the gate is unblocked provided the inner extraction functions consume content_text: str, not FileLike. Per docs/plans/phase-0-investigation/0.9-spike-S9-cocoindex-idempotency.md §7.1, the sidecar pattern can promote to v1 on this basis.
4.3 Finding 02 § 3.3 substrate
Section titled “4.3 Finding 02 § 3.3 substrate”The sidecar pattern’s ratification substrate is Finding 02 § 3.3 (per docs/plans/phase-0-investigation/10-feedback-investigation-findings/00-synthesis-v2.md §3; I3 RESOLVED-PARTIAL-S235 per COCO.9 + COCO.10). The sidecar markdown feeds the Tiptap ContentEditor per the per-MIME viewer composition pattern in 03-tech-stack.md §9.5.
The sidecar markdown is GFM format — the canonical output of Docling (for binary-source Q&A content) or the direct format for manually authored Q&A content. Tiptap renders it in the ContentEditor; the editor uses @tiptap/markdown with editor.getMarkdown() per the CLAUDE.md @tiptap/markdown gotcha (official package, not the community tiptap-markdown package). The editor write-back via UC1 Candidate A writes the updated GFM back to the sidecar file; cocoindex detects the change on the next scan.
4.4 Markdown sidecar on promotion — deferred v1.1
Section titled “4.4 Markdown sidecar on promotion — deferred v1.1”Materialising an approved Q&A pair to a markdown sidecar in the folder at promotion time (UC5 step) is DEFERRED-v1.1 per UC5 4.6.Q7 RESOLVED (0.9-edit-flow-investigation.md §6.5.1). v1 keeps approved Q&A pairs KH-DB-only; sidecar materialisation is evaluated once UC1 + UC4 + UC6 user-direct write-back patterns are mature.
5. Cocoindex idempotency for Q&A flow
Section titled “5. Cocoindex idempotency for Q&A flow”5.1 S9 spike verdict
Section titled “5.1 S9 spike verdict”The S9 spike (docs/plans/phase-0-investigation/0.9-spike-S9-cocoindex-idempotency.md) confirmed that @coco.fn(memo=True) short-circuits cleanly on content equality — with a critical caveat. Verdict: RESOLVED-PARTIAL-S235 per COCO.9 + COCO.10.
5.2 Layered fn-shape requirement
Section titled “5.2 Layered fn-shape requirement”The load-bearing finding is function-shape-dependent. Two-tier structure is required:
| Layer | Function signature | Memo behaviour |
|---|---|---|
| Outer (source-binding) | process_sidecar_file(file: FileLike, …) | Invalidates on any source-file byte change (including metadata-only edits) |
| Inner (expensive LLM ops) | extract_q_a(content_text: str, …) → ExtractedQa | Hits memo on metadata-only edits. Only re-runs when content_text changes. |
Inner-tier functions MUST consume content_text: str, NOT FileLike. If the inner function receives a FileLike, memo is keyed on the whole-file fingerprint — any byte change (including updated_at rewrites) invalidates it, defeating the idempotency guarantee.
This layered shape is the condition on which COCO.10 is CLOSED-CONDITIONAL. The architecture gates the sidecar v1 promotion on it; any violation (passing FileLike to the inner extraction fn) incurs full re-extraction on every metadata-only edit.
5.3 Memo scoping is per-component-path
Section titled “5.3 Memo scoping is per-component-path”A further finding from the S9 spike: memo is scoped per-component-path, NOT per global content-hash. Two mount_each components with byte-identical content_text will both invoke the inner fn independently — memo does NOT collapse them into one execution. KH cannot rely on global content-hash deduplication via @coco.fn memo; cross-record dedup belongs to UC8 substrate (cocoindex entity_resolution + mempalace KG per 0.9-edit-flow-investigation.md §6.8).
5.4 Version-bump cascade
Section titled “5.4 Version-bump cascade”Bumping a @coco.fn(memo=True, version=N) version number cascades upstream through the cocoindex call graph. A version bump on the inner extract_q_a function invalidates process_sidecar_file at the outer tier — forcing full re-extraction across all component paths. This is useful for intentional corpus-wide re-extraction (e.g. when the ExtractByLlm prompt template is updated), but care is needed to avoid incidental re-extraction costs. The implication for Q&A flow: version the inner extraction function deliberately, not as a workaround for prompt adjustments.
5.5 Component-path stability for Q&A sidecars
Section titled “5.5 Component-path stability for Q&A sidecars”If Q&A sidecar file paths change (e.g. a file rename during source-folder reorganisation), the per-component memo cache is lost for that file — triggering re-extraction on the next cocoindex scan. The S9 spike notes this caveat at §5.3: “if file paths change, the per-component cache is lost.” Q&A sidecar files keyed on stable UUIDs (per the source_documents.storage_path stable-identity contract described in 03-tech-stack.md §7.1) inherit path stability; the sidecar .md path convention must preserve the UUID-keyed naming across any folder reorganisation to avoid spurious re-extraction.
6. citations polymorphic shape
Section titled “6. citations polymorphic shape”6.1 N8 RESOLVED-S234
Section titled “6.1 N8 RESOLVED-S234”citations.citing_entity is a polymorphic enum identifying the type of entity that cites a q_a_pair (or other cited content). The closed-list enum is N8 RESOLVED-S234 (00-synthesis-v2.md §5.1; 0.9-decision-graph.md §11.4.1 N8):
citing_entity value | Citing entity type |
|---|---|
bid_response | A procurement bid response |
sales_proposal_response | A sales proposal response |
competitor_research_finding | A finding from a competitor-research workspace |
training_unit | A unit from a training/onboarding workspace |
mcp_search_response | An MCP search result returned to a Claude client |
This supersedes the prior citations.bid_response_id NOT NULL framing (which hard-coded one citing entity type per 0.9-intended-architecture.md pre-S234 framing). The polymorphic shape allows the citations table to serve all current and future application types without schema change — adding audit_response, for instance, adds a new enum value, not a new column.
6.2 The citations table
Section titled “6.2 The citations table”The citations table carries a citing_entity column (the enum above), a citing_entity_id UUID (the FK to the specific citing row — not a single typed FK, but a polymorphic reference indexed per enum value), and a cited_q_a_pair_id UUID referencing q_a_pairs. Full schema detail is deferred to 04-workspace-types.md (forward-ref per construction guide §6.2).
6.3 Citation immutability binding — version-on-cite
Section titled “6.3 Citation immutability binding — version-on-cite”Citations are version-on-cite per §6.0.3 of 0.9-edit-flow-investigation.md (RATIFIED S229). When a bid response ships, it captures the q_a_pair version used at ship time. The q_a_pair can continue to evolve; the shipped citation resolves to the version snapshot via q_a_pair_history (§3.3). This is the load-bearing design that allows Q&A pairs to improve over time without retroactively altering shipped artefacts.
6.4 Citations and the corpus-level shape
Section titled “6.4 Citations and the corpus-level shape”The corpus-level q_a_pairs shape (no workspace_id FK) pairs cleanly with the polymorphic citations shape. A bid response in a procurement workspace cites a corpus-level Q&A pair; the FK on citations.cited_q_a_pair_id reaches directly to the corpus-level q_a_pairs row. Workspace scope is expressed on the citing_entity_id’s row (the bid response belongs to the procurement workspace), not on the cited Q&A pair. This is the cardinality implication documented in phase-b-prerequisite-1-onthology-pipeline-feedback-investigation.md §4.5: “if q_a_pairs are corpus-level (no workspace FK), citations.cited_q_a_pair_id works unchanged — workspace scope is on citing_entity_id’s row.”
7. question_matches with question_kind discriminator
Section titled “7. question_matches with question_kind discriminator”7.1 Generalisation from bid_question_matches
Section titled “7.1 Generalisation from bid_question_matches”The table was named bid_question_matches in the pre-Q-OQR1-02 framing. Per Q-OQR1-02 (00-synthesis-v2.md §3.5; 0.9-decision-graph.md §11.1 ONT.4 + §11.3 row 4), the rename is bid_question_matches → question_matches. The table is aligned with the form_types vocabulary (per Q-OQR1-02 form_type-within-application-type generalisation): the question_kind column discriminates by form_type value rather than hard-coding the procurement case.
The table is never built under the old name — it is created with the new name per the combined-PR direction (RATIFIED-S235, migration STILL-OPEN per Q-OQR1-16 item 10 + 0.9-decision-graph.md §11.3).
7.2 question_kind aligned with form_types vocabulary
Section titled “7.2 question_kind aligned with form_types vocabulary”question_matches.question_kind holds the form-type discriminator. For v1 procurement workspaces, the value is 'bid' / 'rfp' / 'pqq' etc. (the form_types vocabulary per Q-OQR1-02). A question_matches row links:
- A
bid_question(or equivalent form-question row for the relevant form_type) — workspace-scoped via the procurement workspace. - A
q_a_pair— corpus-level.
The scope-tag filter at match time is the workspace-relevance substrate: only q_a_pairs whose scope_tag overlaps the procurement workspace’s scope_tag (and whose anti_scope_tag does not) surface as match candidates.
The two-step retrieval pattern (list/preview → get/verbatim) per S16 §6.1 and the AI-consumer-first principle (01-vision.md §2.1) drives the MCP q_a_search tool design: a fast ranking pass using embedding_score + fulltext_score returns a preview list; the caller then fetches verbatim answer text for the top-N candidates. This is the shape that question_matches serves — it records the ranked match candidates, not the final selected answer.
7.3 Separate embedding_score + fulltext_score columns
Section titled “7.3 Separate embedding_score + fulltext_score columns”Per N9 RESOLVED-S236 (00-synthesis-v2.md §5.1; 0.9-decision-graph.md §11.4.1 N9; WP8 ruling 14/05/2026), question_matches carries separate embedding_score NUMERIC(5,4) and fulltext_score NUMERIC(5,4) columns rather than a single blended match_score. This surfaces per-method signal to the UI and allows downstream tunability without schema change.
Gap flag — per-method scoring tunability deferred to feature spec:
Per-method score weighting (the blend function, the UI treatment of separate vs blended scores) is operational verification deferred to feature spec time per N9 RESOLVED-S236 (“operational verification deferred to feature spec time”). Checked 00-synthesis-v2.md §5.1, 0.9-decision-graph.md §11.4.1, and the readiness audit §7.3 rationale — no ratification on blend-function shape or UI treatment found. Category: tech spec needed. Suggested prerequisite work: open docs/specs/procurement-question-matching/TECH.md at procurement build cycle start; that spec owns the per-method weight configuration, blend function, and UI score presentation. The architecture record carries the schema shape (two separate columns); the feature spec owns the runtime combination policy.
8. Q&A write-back flow + UC6 ratifications
Section titled “8. Q&A write-back flow + UC6 ratifications”8.1 UC6 implementation — RATIFIED S229
Section titled “8.1 UC6 implementation — RATIFIED S229”UC6 (Q&A pair revision) is the write-back flow. It is RATIFIED S229 per 0.9-edit-flow-investigation.md §6.6. Two sub-variants:
User-direct revision — KH-DB-only operation. User opens the Q&A pair in the existing Tiptap + Yjs ContentEditor (03-tech-stack.md §9); edits question / answer text; states intent from the closed per-UC vocabulary (§6.0.1 carry-forward); chooses in-flight citer policy; commits. q_a_pair_history records the version transition via the auto-version trigger (valid_from / valid_to temporal columns).
AI-suggest revision — Candidate B. KH server detects source-doc change via cocoindex source-binding event; invokes the Anthropic API to propose a revision draft (source-doc delta as context); user reviews + edits the draft; commits via the same user-direct flow. Hybrid: Candidate B for proposal generation, Candidate A for human refinement and commit.
8.2 Cross-UC consolidated decisions carry-forward
Section titled “8.2 Cross-UC consolidated decisions carry-forward”The following decisions from 0.9-edit-flow-investigation.md §6.0 apply to UC6 and the wider Q&A flow:
| Decision | Resolution | Source |
|---|---|---|
| Intent-taxonomy policy | Closed per-UC vocabulary + free-text ‘other’ escape valve at commit time | §6.0.1 RATIFIED S229 |
| Citation re-anchor policy | Hybrid — re-anchor if verbatim match, else mark-as-paraphrased with archived-snapshot pointer | §6.0.2 RATIFIED S229 |
| Citation immutability binding | Version-on-cite at ship time; pre-ship Q&A can evolve; shipped bids resolve to version snapshot | §6.0.3 REAFFIRMED S229 |
| Downstream-impact UI | Count + paginated affected-record list; no interactive per-record diff in v1 | §6.0.5 RATIFIED S229 |
| AI-suggest fatigue mitigation | Per-workspace tunable threshold + rejection memory + daily-per-user cap | §6.0.7 RATIFIED S229 |
8.3 UC6 intent vocabulary
Section titled “8.3 UC6 intent vocabulary”Closed list per §6.6.1 of 0.9-edit-flow-investigation.md: typo / factual-update / regulatory-change / source-doc-driven / dedup-merge / promotion-from-bid-variation / clarification / other-with-note. Revising a published Q&A pair moves its publication_status to in_review automatically per §6.6.1 4.3.Q5.
8.4 Multi-Q&A revision atomicity
Section titled “8.4 Multi-Q&A revision atomicity”UC6 supports shared-op-ID multi-Q&A transactional revision per §6.6.1 4.3.Q6 RESOLVED. A regulatory update that touches many Q&A pairs can commit under one op-ID; the per-op rollback affordance (§6.0.6) reverses all touched Q&A pairs to their pre-op state in one operation. This is the pattern that makes taxonomy-driven Q&A review queue (UC9 scope-tag update → UC6 AI-suggest trigger) safe to operate at scale. Admin permission required per §6.0.4 carry-forward.
9. Promotion flow (UC5 — bid response → Q&A pair)
Section titled “9. Promotion flow (UC5 — bid response → Q&A pair)”9.1 Architecture direction — RATIFIED S229
Section titled “9.1 Architecture direction — RATIFIED S229”UC5 (bid response → Q&A promotion) is a KH-DB-only operation per 0.9-edit-flow-investigation.md §6.5 (RATIFIED S229, no Candidate A/B/C applies — UC5 is not a write-back operation). The operation:
- Creates a new
q_a_pairrow indraftstate. - Sets
origin_kind = 'derived_from_bid_response'. - Records lineage:
source_workspace_id(nullable, originating procurement workspace for provenance audit per Q-OQR1-07),promoted_from_bid_response_id(FK to the source bid response), and the bid question from which the response was derived (lineage to the form question that generated the response text). - Inherits the temporal citation binding from UC6:
valid_fromset at promotion time;valid_toNULL (currently valid). - Routes to the
in_reviewstate via the review queue (curator or workspace admin per §6.0.4).
If a close match exists in the corpus (cosine similarity ≥ 0.85 by default, tunable per workspace per 4.6.Q9 RESOLVED), the promotion surface presents options: supersede the existing pair (UC6 revision flow), merge the new content into the existing pair (UC6 revision with merge intent), or proceed regardless (create a new pair and accept curation risk).
This is the architectural direction; the per-question quality-gate checklist, scope-tag picker UI behaviour, and duplicate-detection threshold UX are product-spec scope. See gap flag §9.3 below.
9.2 publish_status lifecycle
Section titled “9.2 publish_status lifecycle”UC5 promotion creates a draft Q&A pair. The default lifecycle is:
draft → in_review (curator review queue) → publishedWorkspace-level policy permits “auto-publish if actor is curator-or-admin” as opt-in per §6.5.1 4.6.Q2 of 0.9-edit-flow-investigation.md. Approval workflow per publish_status lifecycle (the existing publication-lifecycle pattern on content_items mirrored for q_a_pairs).
Source classification carry-through: the promoted Q&A draft inherits a confidence label from the source classification (per 4.6.Q10 RESOLVED in 0.9-edit-flow-investigation.md §6.5.1) — “user-refined”, “captured-from-MCP-generation”, or “manually-authored” — surfaced in the review queue for reviewer context.
AI-suggest promotion candidates are always user-initiated in v1 (per 4.6.Q1 RESOLVED — never auto-commit without user click). AI surfaces candidates per the §6.0.7 fatigue mitigation policy (per-workspace tunable confidence threshold + rejection memory + daily cap).
9.3 Gap flag — promotion UI shape
Section titled “9.3 Gap flag — promotion UI shape”UC5 promotion-UI architecture-level direction is RATIFIED per §6.5; the UI shape for three sub-decisions is not ratified.
The following are architecture-level direction (no gap): KH-DB-only operation; lineage to source bid response + bid question; publish_status lifecycle; AI-suggest always user-initiated (per 4.6.Q1); version-on-cite carry-through from the promoted source (per §6.0.3).
The following UI-shape sub-decisions are product-spec scope:
UC5 promotion-UI shape — product spec needed. Three sub-decisions require a PRODUCT.md: (1) 4.6.Q5 — quality-gate checklist content: the promotion surface displays a soft quality checklist (scope tagged / answer self-contained / question phrasing canonical / lineage present) — the specific checklist items and their display shape are product-spec scope; (2) 4.6.Q6 — scope-tag picker behaviour: at promotion, the user picks scope tags from the UC9 vocabulary; AI-suggest may propose tags; the picker UX, suggestion display, and default scope are product-spec scope; (3) 4.6.Q9 — duplicate-detection threshold UX: the default cosine-similarity threshold is 0.85 (tunable per workspace per §6.5.1 4.6.Q9), and when a close match surfaces, the user picks from “merge / supersede / proceed anyway” — the presentation UX for the close-match decision is product-spec scope. Checked
00-synthesis-v2.md§3 + §5,0.9-decision-graph.md§11.4,0.9-edit-flow-investigation.md§6.5.1, anddocs/specs/core-docs-pathway-assessment/architecture-sub-doc-readiness-audit.md§9 — UI-shape for these three sub-decisions is not ratified. Category: product spec needed. Suggested prerequisite work: opendocs/specs/qa-promotion-ui/PRODUCT.mdahead of the UC5 build cycle; that spec owns the quality-checklist items, scope-tag picker UX, and duplicate-detection threshold presentation. Until that spec lands, the architecture sub-doc writes the architecture-level UC5 direction; the UI shape is flagged as a feature-spec deliverable.
10. Pattern A/B parser retire (B1)
Section titled “10. Pattern A/B parser retire (B1)”10.1 B1 RESOLVED-MIGRATION-HELPER-ONLY
Section titled “10.1 B1 RESOLVED-MIGRATION-HELPER-ONLY”The Pattern A/B Q&A parser (extractQaPairs in lib/bid-library-ingest/extract-qa-pairs.ts) retires post-Phew-migration per Q3.5 RESOLVED-MIGRATION-HELPER-ONLY (00-synthesis-v2.md §2, B1 row; 0.9-decision-graph.md §11.2 COCO.1; docs/plans/phase-0-investigation/phase-b-prerequisite-2-cocoindex-deep-dive.md §1.2). GitNexus zero-caller confirmation establishes that no recurring production path invokes Pattern A/B post-migration.
Pattern A/B was the pre-cocoindex recurring adapter assumption — a bespoke DOCX parser for audit_6col (Pattern A: Q / Standard / Advanced / + 3 meta columns) and draft_5col (Pattern B: Q / Standard / + 3 meta columns) table formats. Cocoindex ExtractByLlm with typed Python output_type subsumes the recurring adapter role; Pattern A/B survives only as a one-shot Phew corpus migration helper.
10.2 Retire scope
Section titled “10.2 Retire scope”The retire scope from 0.9-collapse-candidates.md §12.3 (RATIFIED-S234, retire confirmed by S235 Q3.5 closure):
lib/bid-library-ingest/extract-qa-pairs.ts— the TS-side parser (Pattern A + B).- Downstream callers (GitNexus zero-caller confirmed) — none in recurring production paths.
The retire timing is post-Phew-migration (one-shot migration tooling completes; no further Pattern A/B invocations). This sub-doc owns the Q&A detail; the retire-tier marker is also carried in 07-collapse-list.md §3 for the collapse-list family view. The 02-data-flow.md §9 anti-patterns section mentions Pattern A/B at the data-flow level and cross-references here for the full Q&A-specific retire detail.
10.3 The Phew-migration path
Section titled “10.3 The Phew-migration path”The Phew corpus migration converts the 395 existing content_items rows with content_type='q_a_pair' to the canonical q_a_pairs table shape. At migration time:
- Existing rows receive
origin_kind='imported_legacy'. - The
content_type='q_a_pair'discriminator oncontent_itemsretires post-cutover per0.9-collapse-candidates.md§12.1 ([RATIFIED-RETIRE] per combined-PR scope). extractor_kind='prior_bid_response'inq_a_extractionscarries the extraction lineage for Phew bid-library content where that lineage is recoverable.- One-shot LLM-extraction (
extractor_kind='llm_extraction') is the migration helper for content where the original structured extraction path was Pattern A/B — this is Q3.5 RESOLVED-MIGRATION-HELPER-ONLY scope. After migration, thisextractor_kindvalue is retained in the enum for audit purposes but the recurring pipeline does not invoke it.
11. Anti-patterns + retired patterns
Section titled “11. Anti-patterns + retired patterns”The following framings are explicitly rejected per Phase 0.9 ratifications. 09-diagrams.md ERDs build on these closures. Cross-link: 04-workspace-types.md §11 carries the workspaces-side anti-pattern list (especially q_a_pairs.workspace_id NOT NULL, q_a_pair_workspaces M:N junction, and idx_q_a_pairs_workspace).
| Anti-pattern | Tier | Why rejected | Citation |
|---|---|---|---|
q_a_pairs.workspace_id NOT NULL FK (the 0.9-intended-architecture.md §4.3 framing) | [RATIFIED-RETIRE] | Superseded by corpus-level + scope_tag-driven relevance; 0 of 395 prod rows were workspace-assigned empirically | Q-OQR1-06; 00-synthesis-v2.md §3.6; empirical data per phase-b-prerequisite-1-onthology-pipeline-feedback-investigation.md §4.4 |
q_a_pair_workspaces M:N junction (the prior onto-doc §4.4 framing) | [RATIFIED-DO-NOT-BUILD] | Re-resolved S235 per WP-ONTO-R1 §4 — heavier than scope_tag overlap; doesn’t match empirical state; scope_tag-driven relevance subsumes | Q-OQR1-06; 0.9-decision-graph.md §11.1 ONT.5 |
idx_q_a_pairs_workspace (workspace-partition index) | [RATIFIED-DO-NOT-BUILD] | Workspace relevance is computed at read time via scope_tag GIN index (not workspace partition); building a workspace-partition index would encode the superseded framing in the schema | Q-OQR1-06; 00-synthesis-v2.md §3.6 |
Workspace-private q_a_pairs via private_to_workspace_id column in v1 | [DEFERRED-v1.1] | Single Phew tenant for v1 makes the private/shared distinction non-load-bearing; Q-OQR1-08 ratified the deferral | Q-OQR1-08; 00-synthesis-v2.md §3.14 |
citations.bid_response_id NOT NULL as the sole citer column | [RATIFIED-RETIRE] | Superseded by citing_entity polymorphic enum (N8 RESOLVED-S234); hard-coded bid_response FK cannot serve cross-application-type citation | N8 RESOLVED-S234; 00-synthesis-v2.md §5.1 |
Single blended match_score column on question_matches | [RATIFIED-RENAME] | Separate embedding_score + fulltext_score columns per N9 RESOLVED-S236; blended single score hides per-method tunability signal | N9 RESOLVED-S236; 00-synthesis-v2.md §5.1; WP8 ruling 14/05/2026 |
bid_question_matches table name | [RATIFIED-RENAME] | Renamed to question_matches with question_kind discriminator aligned to form_types vocabulary; table never built under old name | Q-OQR1-02; 00-synthesis-v2.md §3.5; 0.9-decision-graph.md §11.3 row 4 |
| Pattern A/B Q&A parser as recurring infrastructure | [RATIFIED-RETIRE] | Dissolved by ExtractByLlm with typed output_type; Pattern A/B retires as a one-shot Phew-migration helper only | B1 RESOLVED-S234; COCO.1; 00-synthesis-v2.md §2 B1 row |
extractor_kind='prior_bid_response' as a recurring v1 ingestion path | [RATIFIED-DO-NOT-BUILD] | This extractor_kind is a one-shot migration helper (Q3.5 RESOLVED-MIGRATION-HELPER-ONLY); it is NOT a recurring pipeline path | Q3.5 RESOLVED-MIGRATION-HELPER-ONLY; 00-synthesis-v2.md §2 B1 row |
12. Source-doc supersession
Section titled “12. Source-doc supersession”This sub-doc is one of nine that supersede docs/plans/phase-0-investigation/0.9-intended-architecture.md (2015 lines, S229). The source doc’s §4.3 carries the pre-Q-OQR1-06 q_a_pairs.workspace_id NOT NULL framing; its §6.2 frames bid_question_matches as the table name without question_kind generalisation; its §4.6 carries the single-score match_score framing that N9 RESOLVED-S236 supersedes. Full audit trail of the 10 superseded items + ratifying doc per row: docs/specs/core-docs-pathway-assessment/INV-architecture-split-readiness.md §2 (rows 1, 5, 6 directly anchor in this sub-doc’s domain). Canonical-state sources downstream sub-docs cite: 00-synthesis-v2.md §3.6 (q_a_pairs corpus-level) + §5.1 (N8 + N9 WP8 rulings); 0.9-decision-graph.md §11.1 ONT.5 + §11.4.1 N8 + N9; 0.9-edit-flow-investigation.md §6.5 + §6.6 + §6.0.1-§6.0.7.
Source doc preserved as pre-split snapshot during Waves 1-4; archived to .planning/.archive/.specs/ after 09-diagrams.md closes per CLAUDE.md “Historical planning”.
12.1 Heritage docs feeding this sub-doc
Section titled “12.1 Heritage docs feeding this sub-doc”Per construction guide §4.1 — three-tier status taxonomy ([CURRENT-CANONICAL] / [PARTIALLY-SUPERSEDED] / [FULLY-SUPERSEDED]); table shape mirrored from the pilot (01-vision.md §7.1) against this sub-doc’s heritage set per §4.2.
| Doc | Date | Status | Useful for |
|---|---|---|---|
docs/client-documentation/Knowledge Hub — Claude Integration Guide.md | (companion to Platform Overview, 23/04/2026) | [PARTIALLY-SUPERSEDED] — Claude-via-MCP Q&A retrieval flow framing holds (how Q&A consumption works end-to-end from a user’s perspective); MCP-tooling-specific content (tool inventory, mempalace direct/wrapped) now lives in 06-mcp-tooling.md (gated on Theme F). Pre-Q-OQR1-06 workspace-scoped Q&A framing is stale. | Claude-via-MCP Q&A retrieval user-journey framing; end-to-end consumption shape (cite for positioning context only; do NOT cite for table names or workspace-scoping behaviour, which is stale). |
docs/plans/phase-0-investigation/0.9-spike-S16-qa-schema-design.md | 13/05/2026 (S229 DESIGN-RECOMMENDED) | [PARTIALLY-SUPERSEDED] — §6.1 design principles + §6.2 q_a_pairs column list + §6.3 q_a_extractions shape are the two-tier model substrate (DESIGN-READY-FOR-REVIEW at spike time). Post-spike: workspace_id NOT NULL on the §6.2 schema sketch is superseded by Q-OQR1-06 corpus-level + source_workspace_id nullable per Q-OQR1-07; idx_q_a_pairs_workspace in the §6.2 index list is [RATIFIED-DO-NOT-BUILD]. | Two-tier model substrate (§6.1 design principles); q_a_extractions typed-column shape (§6.3); extractor_kind enum values (§6.3 extractor_kind CHECK); origin_kind enum values. Do NOT cite §6.2 workspace_id NOT NULL or idx_q_a_pairs_workspace — these are superseded by Q-OQR1-06. |
docs/plans/phase-0-investigation/0.9-spike-S9-cocoindex-idempotency.md | 13/05/2026 (S235 Wave 1) | [CURRENT-CANONICAL] — the idempotency evidence base holds: H1 (file-keyed memo) REFUTED; H2 (content-keyed memo) CONFIRMED; per-component-path scope confirmed; RESOLVED-PARTIAL-S235 per COCO.9 + COCO.10. No superseded claims. | Layered fn-shape requirement (§7.1); per-component vs global hash scoping (§5.3); version-bump cascade upstream (§5.4); confidence + caveats (§6). |
docs/plans/phase-0-investigation/phase-b-prerequisite-1-onthology-pipeline-feedback-investigation.md | S235 (14/05/2026 — Q-OQR1-01..17 ratifications) | [CURRENT-CANONICAL] — §4 (q_a_pair cardinality reasoning) is the empirical substrate for Q-OQR1-06 + Q-OQR1-07; §4.5 (citations / question_matches implications) correctly identifies the polymorphic citations direction. The N8 resolution (citations polymorphic enum) is per 00-synthesis-v2.md §5.1 (not this doc directly), but this doc provided the substrate reasoning. | Q-OQR1-06 cardinality reasoning (§4); empirical 0/395 production rows (§4.1); scope_tag-driven relevance option C analysis (§4.2-§4.4); citations + question_matches cardinality implications (§4.5); Q-OQR1-07 source_workspace_id rationale (§4.4 + §4.5). |
End of sub-doc. Wave 2 split pair: 02-data-flow.md (parallel sub-doc, dispatched concurrently). Wave 2 tail: 08-new-features.md (anchors the bid-feedback loop 3-UC summary in this sub-doc’s UC5 + UC6 ratifications; 09-diagrams.md ERD §2 renders the Q&A round-trip sequence using the schemas here + in 04-workspace-types.md §5).
Cross-doc consistency note for 09-diagrams.md: the Q&A round-trip sequence diagram MUST reflect the two-sub-variant UC6 flow (user-direct KH-DB-only + AI-suggest Candidate B); the citations polymorphic enum (5 citing_entity values per §6); the separate embedding_score + fulltext_score columns on question_matches (not a blended score); and the corpus-level q_a_pairs shape (no workspace_id FK in the ERD). Any ERD that shows q_a_pairs.workspace_id is rendering the superseded framing per §11 anti-patterns.