Procurement Workspaces — PRODUCT
⚠️ PARTIALLY SUPERSEDED (S462, 2026-07-11) — DR-038 + ID-145. Section A (the workspace umbrella, the
procurement_workspacessatellite B-3/B-4, and the workspace-level 10-state workflow B-5..B-7) is superseded — id-130 moved per-stage state ontoform_templatesand DR-038 retires workspace scoping. Durable invariants: B-2 (form_types), B-9..B-12 (retrieval + citation), B-13..B-16 (EP8 import mechanics), B-17/B-18 (promotion policy) — re-anchor them to the form instance. Rework owner: ID-145 —specs/id-145-procurement-form-first/RESEARCH.md.
Procurement Workspaces — PRODUCT
Section titled “Procurement Workspaces — PRODUCT”Status:
PARTIALLY-SUPERSEDED (S462)— NEW-S242. Phase 2 application-feature spec for the procurement application_type (first domain application, Phew pilot). Substrate fromdocs/plans/phase-0-investigation/architecture/04-workspace-types.md§4.2 (OQ-Q38-E 6-column ratification) + §7 (procurement umbrella rename) +docs/specs/reserved-workspace-seats/PRODUCT.mdline 93 (referenced as the procurement seat-shape spec home). Absorbsdocs/specs/ep8-qa-docx-import-ui-spec.mdv5 (RATIFIED-S242) — EP8’s Q&A .docx import UI scope folds into §B.4 here.
How to use this doc
Section titled “How to use this doc”This file holds user-perspective invariants for the procurement application — what an admin / bid author / curator can do inside a procurement workspace, what the platform guarantees, and how procurement composes with the cross-application Q&A and review surfaces. Companion TECH.md carries implementation references, migration sequencing, file:line landing points, and validation steps.
Invariants are numbered globally (B-1, B-2, …) so TECH.md references by ID. Each carries [RATIFIED-S2XX] with source citation, [DEFERRED-v1.1] / [DEFERRED-v2] for explicit deferrals, [LOCKSTEP-WITH-MIGRATION] for items tied to a combined-PR migration scope per PLAN.md (T2 combined-PR by default; sister migrations T5 / T7 / T11 where explicitly cited), or [ABSORBED-S242 EP8 v5 §X] for behaviours folded in from EP8.
Source-of-truth pointers
Section titled “Source-of-truth pointers”docs/plans/phase-0-investigation/architecture/04-workspace-types.md§3.2 (Q-OQR1-03 application_types vocabulary —procurementrow) + §4.2 (OQ-Q38-E 6-column scope) + §7 (umbrella rename + cascaded effects) + §8 (Q-OQR1-16 combined-PR walkthrough).docs/plans/phase-0-investigation/architecture/05-qa-flow.md§7 (question_matcheswithquestion_kinddiscriminator) + §8 (UC6 Q&A revision write-back) + §9 (UC5 bid response → Q&A promotion).docs/plans/phase-0-investigation/architecture/08-new-features.md§4 (change_reportsrename) + §5 (UC9 scope_tag governance) + §7 (bid feedback 3-UC composite).docs/plans/phase-0-investigation/architecture/02-data-flow.md§3 (cocoindex 6-stage flow) + §8 (edit_intentre-classification trigger policy).docs/specs/reserved-workspace-seats/PRODUCT.md(substrate spec — procurement seat is out of scope for RWS; this spec FILLS IN the per-app columns).docs/specs/rls-pattern/PRODUCT.mdP-1..P-5 (per-tenant RLS pattern; procurement satellites inherit auto-RLS + grants helper).docs/specs/ep8-qa-docx-import-ui-spec.mdv5 (RATIFIED-S242) — ABSORBED-S242 into §B.4 below; the standalone EP8 spec is superseded by this spec at S242 close (supersession-and-archive action is a Wave 3 deliverable per S242 prompt).docs/specs/id-31-canonical-pipeline-implementation-plan/PLAN.md§4.4 T4 (procurement umbrella rename — code-side execution + 6-column population) + §5 NEW spec row “procurement-workspaces” (T4.7 spec destination).docs/reference/product-roadmap.json§1.11 (EP8 roadmap item) + §1.4 (real-data eval baseline forward-ref).
Audience
Section titled “Audience”Bid authors, curators, and admins running the procurement application (Phew pilot — UK SMB bid management); engineers implementing T4 procurement rename + 6-column population; reviewers verifying procurement workflow + Q&A composer surface compliance; per-application-type build cycles using procurement as the worked example.
Summary
Section titled “Summary”A procurement workspace is the per-engagement work surface for the procurement application_type. Each row in workspaces with application_type='procurement' has exactly one row in the procurement_workspaces satellite carrying buyer, deadline, submission date, and outcome columns; the workspace runs a 10-state workflow from draft through to a terminal outcome (won / lost / withdrawn). Inside a procurement workspace, the user composes responses to bid questions by drawing from the cross-application Q&A corpus (corpus-level q_a_pairs per 05-qa-flow.md §1) via the question_matches retrieval substrate, imports new Q&A pairs from supplier .docx files (absorbed-EP8 UI), and feeds shipped bid responses back into the corpus via the UC5 promotion path. The procurement application IS the first application of the bid feedback 3-UC flywheel (UC5 promote + UC6 revise + UC8 dedup per 08-new-features.md §7) and the load-bearing v1 user-data-quality loop.
Behavior
Section titled “Behavior”Section A — Workspace lifecycle + workflow
Section titled “Section A — Workspace lifecycle + workflow”B-1 — application_type='procurement' is the umbrella vocabulary
Section titled “B-1 — application_type='procurement' is the umbrella vocabulary”Procurement is one of the six core-provenance baseline application_types rows seeded at v1 migration time per 04-workspace-types.md §3.2. Creating a workspace via the create-dialog or the /api/workspaces POST endpoint with application_type_id resolving to the procurement row binds the workspace to the procurement application (typed columns: buyer, deadline, submission_date, outcome, outcome_recorded_at, outcome_recorded_by in the satellite — see B-3).
[RATIFIED-S235] via Q-OQR1-02 (04-workspace-types.md §7.1 + 00-synthesis-v2.md §3.5; 0.9-decision-graph.md §11.1 ONT.4) — “bid” is renamed to “procurement” pre-launch; “bid” survives only as a form_type value (B-2).
[LOCKSTEP-WITH-MIGRATION] with T2 combined PR (04-workspace-types.md §8 item 4) — the rename ships as DB + code in one combined migration; the procurement-feature 6-column population (B-3) lands in the same PR per PLAN.md §4.4 T4.7.
Cross-refs: [Q-OQR1-02], [arch 04-workspace-types §7], [PLAN.md §4.4 T4].
B-2 — form_types is the closed-list discriminator within procurement
Section titled “B-2 — form_types is the closed-list discriminator within procurement”Within a procurement workspace, each piece of work (a tender response, a pre-qualification questionnaire, a dynamic-purchasing-system application) carries a form_type value from the v1 closed list (canonical keys per docs/ontology/26-form-type.md §7.3): bid / rfp / pqq / itt / framework / dps / gcloud (user-facing label “G-Cloud”). The workspace tracks form-type-specific work via the same state machine (B-5); a single procurement workspace MAY run multiple form types over its lifetime. Implementation hook: T-4 enforces this closed list code-side.
[RATIFIED-S235] via Q-OQR1-02 (04-workspace-types.md §7.3) — the seven form_types are the v1 closed list. [DEFERRED-v1.1] per Q-OQR1-14: data-driven form_type behaviour (extraction policy / match policy / export policy as DB-configured per-form_type rows) is v2 scope. v1 is code-driven — per-form_type behaviour lives in lib/procurement/ modules, not in DB rows.
Cross-refs: [Q-OQR1-02], [Q-OQR1-14], [arch 04-workspace-types §7.3].
B-3 — Procurement satellite carries 6 typed columns
Section titled “B-3 — Procurement satellite carries 6 typed columns”The procurement_workspaces satellite table (renamed from bid_workspaces per 04-workspace-types.md §7.2) carries exactly these per-app columns at v1 ship time, beyond the seat-baseline id PK + workspace_id FK + created_at / updated_at timestamps:
| Column | Type | Nullable | Meaning |
|---|---|---|---|
buyer | TEXT | NOT NULL | The procuring authority / organisation name (e.g. “Camden Council”, “NHS Wales”) |
deadline | TIMESTAMPTZ | NULL | The submission deadline (UK timezone-aware). NULL pre-deadline-discovery |
submission_date | TIMESTAMPTZ | NULL | When the bid was actually submitted. NULL pre-submission. Set on submitted state transition |
outcome | TEXT | NULL | won / lost / withdrawn. NULL pre-terminal-state. Constrained via CHECK |
outcome_recorded_at | TIMESTAMPTZ | NULL | Audit — when the outcome row was first recorded |
outcome_recorded_by | UUID | NULL | Audit — FK to auth.users(id) for the user who recorded the outcome |
[RATIFIED-S240] via OQ-Q38-E (04-workspace-types.md §4.2 — the 6-column scope is ratified; column-level types here are illustrative; authoritative SQL is the T2 combined-PR migration body per PLAN.md §4.4 T4.7).
These 6 columns promote from the former workspaces.domain_metadata JSONB blob (types/bid.ts:26-40 BidMetadata interface). The Shape B typed-columns-over-JSONB platform standard per 04-workspace-types.md §3.9 (RATIFIED-S235) is the governing rule. Reference-number, estimated-value, tender-source, tender-document-IDs, outcome-notes, and free-text notes from the legacy BidMetadata shape are NOT promoted at v1; they EITHER stay in a residual JSONB column for migration-tail compatibility OR drop entirely (T2 migration body authoritative; see TECH.md T-3).
[LOCKSTEP-WITH-MIGRATION] with T2 combined PR (PLAN.md §4.4 T4.7) — the columns ship in the same migration as the table rename.
Cross-refs: [OQ-Q38-E], [arch 04-workspace-types §4.2], [Shape-B-typed-columns].
B-4 — Satellite cardinality is 1:1 with workspaces
Section titled “B-4 — Satellite cardinality is 1:1 with workspaces”A workspace bound to application_type='procurement' has exactly one row in procurement_workspaces, enforced via UNIQUE(workspace_id) on the satellite FK per the per-application-type satellite pattern (04-workspace-types.md §4.1; docs/specs/reserved-workspace-seats/PRODUCT.md S-3 cardinality rule). Deleting a workspace cascades to delete the satellite row; the reverse cascade does NOT apply (deleting a satellite row leaves the workspace intact, which the UI prevents at the surface level). Implementation hook: T-3 step 1 enumerates the UNIQUE(workspace_id) constraint as part of the satellite shape.
[RATIFIED-S235] per Q-OQR1-113-A satellite-per-application-type-vocabulary closure + [RATIFIED-S240] from docs/specs/reserved-workspace-seats/PRODUCT.md S-3 (precedent pattern; this spec applies the same shape to the procurement seat).
Cross-refs: [Q-OQR1-113-A], [RWS S-3], [arch 04-workspace-types §4.1].
B-5 — PROCUREMENT_WORKFLOW_STATES is the closed 10-state workflow
Section titled “B-5 — PROCUREMENT_WORKFLOW_STATES is the closed 10-state workflow”A procurement workspace progresses through one of these 10 workflow states (renamed from the legacy BID_STATES per Q-OQR1-02; state set unchanged):
draft → questions_extracted → matching → drafting → in_review → ready_for_export → submitted → { won | lost | withdrawn }won, lost, and withdrawn are terminal states. The state values are preserved verbatim from the legacy BID_STATES const (types/bid.ts:3-14); only the const name (BID_STATES → PROCUREMENT_WORKFLOW_STATES), the TS type name (BidState → ProcurementWorkflowState), and the module path (lib/bid/bid-state-machine.ts → lib/procurement/procurement-workflow.ts) change.
[RATIFIED-S235] via Q-OQR1-02 + Q-OQR1-05 (04-workspace-types.md §7.2 cascaded-renames table — “state set unchanged (10 values verbatim)”). [LOCKSTEP-WITH-MIGRATION] — the const rename ships in the same PR as the satellite rename (T2 combined PR; T4 code sweep).
Allowed transitions match the legacy state machine (lib/bid/bid-state-machine.ts:48-59 VALID_TRANSITIONS table) verbatim — see TECH.md T-2 for the move + rename file:line landings.
Cross-refs: [Q-OQR1-02], [Q-OQR1-05], [arch 04-workspace-types §7.2].
B-6 — Submitting a bid records submission_date automatically
Section titled “B-6 — Submitting a bid records submission_date automatically”When a workspace transitions to submitted state, the platform sets procurement_workspaces.submission_date to the transition timestamp (server clock, UTC). The user does not type this field; it is a side-effect of the state transition.
[RATIFIED-S242] — derived from the legacy BidMetadata.submission_date field semantics (pre-Q-OQR1-02 framing — types/bid.ts:36) carried forward verbatim into the typed-column promotion. Implementation: state-machine commitTransition() writes both workspaces.status and procurement_workspaces.submission_date in the same transaction.
B-7 — Recording a terminal outcome captures audit columns
Section titled “B-7 — Recording a terminal outcome captures audit columns”When a workspace transitions to one of the three terminal states (won / lost / withdrawn), the platform sets procurement_workspaces.outcome to that state value, outcome_recorded_at to the transition timestamp, and outcome_recorded_by to the acting user’s auth.uid(). All three are written in the same transaction as the workspaces.status write.
The outcome value is constrained at the satellite layer: a CHECK constraint or enum restricts outcome to (won, lost, withdrawn). If a workspace is in a non-terminal state, outcome is NULL; the CHECK permits NULL. Implementation hook: T-5 (state-transition write) records the three audit columns atomically with the workspaces.status write on terminal-state transitions.
[RATIFIED-S242] — derived from BidMetadata.outcome + outcome_recorded_at + outcome_recorded_by audit fields (types/bid.ts:35-39) lifted into the satellite per OQ-Q38-E. The outcome enum value MUST match the terminal PROCUREMENT_WORKFLOW_STATES member, enforced at write time via the state-machine commitTransition() helper.
Cross-refs: [OQ-Q38-E], [B-5].
Section B — Bid composer surface (cross-application Q&A retrieval)
Section titled “Section B — Bid composer surface (cross-application Q&A retrieval)”B-8 — Bid composer renders inside a procurement workspace
Section titled “B-8 — Bid composer renders inside a procurement workspace”The bid composer is the surface where the user composes responses to extracted bid questions for a given procurement workspace. It renders inside the workspace’s question-review page (current pre-rename location: components/bid/question-review.tsx and siblings; post-rename: components/procurement/question-review.tsx per T4 subtask 5). The composer DOES NOT exist as a standalone route — it is a per-workspace surface coupled to the procurement workflow state.
[RATIFIED-S242] — derived from current components/bid/ surface inventory (bid-context-provider.tsx, bid-creation-wizard.tsx, question-list.tsx, question-navigator.tsx, question-review.tsx, question-row.tsx, response-editor.tsx) and the procurement workflow state-machine binding (B-5 in-review state).
B-9 — Composer consumes question_matches against bid questions
Section titled “B-9 — Composer consumes question_matches against bid questions”For each bid_question in the workspace, the composer renders ranked match candidates retrieved from the corpus-level q_a_pairs corpus via the question_matches table. Matches are scoped by the procurement workspace’s scope_tag (overlap with the candidate Q&A pair’s scope_tag; anti-scope exclusion enforced) per the corpus-level cardinality model (05-qa-flow.md §1.1).
The user sees, per question:
- The bid question text + the form question lineage (form_type, section name, evaluation weight).
- A ranked list of candidate Q&A pairs from the corpus with separate
embedding_scoreandfulltext_scorecolumns (NOT a blended score per05-qa-flow.md§7.3 N9 RESOLVED-S236). - A “preview” affordance per candidate (loads
answer_standardtext inline, two-step list/preview → get/verbatim pattern per S16 §6.1). - An “accept” affordance per candidate (selects the Q&A pair as the response source; commits the citation per B-12).
- A confidence-posture indicator (
strong_match/partial_match/needs_sme/no_contentper the legacyConfidencePosturetaxonomy attypes/bid.ts:77-81— preserved verbatim through the rename).
[RATIFIED-S236] per N9 RESOLVED (05-qa-flow.md §7.3) — separate score columns; [DEFERRED-procurement-question-matching-tech-spec] for the per-method weight blend function and UI score-presentation policy (per PLAN.md §5 CONDITIONAL spec row docs/specs/procurement-question-matching/TECH.md).
Cross-refs: [N9 RESOLVED-S236], [arch 05-qa-flow §7], [arch 05-qa-flow §7.3].
B-10 — Composer uses two-step retrieval pattern
Section titled “B-10 — Composer uses two-step retrieval pattern”Match candidates surface in the composer via the two-step list/preview → get/verbatim retrieval pattern (per 05-qa-flow.md §7.2). Step 1 (list) returns ranked preview metadata (Q&A pair ID, question_text, scope_tag set, both score columns) — no verbatim answer body. Step 2 (get) returns the verbatim answer_standard (+ answer_advanced if the workspace’s tier policy enables it) for the user-selected candidate IDs. The composer issues step 2 only when the user clicks “preview” or “accept” on a specific candidate.
[RATIFIED-S235] per S16 §6.1 + AI-consumer-first principle (01-vision.md §2.1). This is the same retrieval pattern the MCP q_a_search tool surfaces (05-qa-flow.md §7.2); the bid composer is a UI-side consumer of that pattern.
Cross-refs: [S16 §6.1], [arch 05-qa-flow §7.2].
B-11 — Question status mirrors ConfidencePosture + author posture
Section titled “B-11 — Question status mirrors ConfidencePosture + author posture”Per question in the composer, the platform tracks a QuestionStatus value (not_started / ai_drafted / in_progress / needs_review / complete per types/bid.ts:82-87). The status reflects author posture toward the question’s response, distinct from the AI-driven ConfidencePosture value (B-9) which reflects retrieval quality. Implementation hook: T-4 + T-6 carry both enum sets through the rename sweep verbatim.
[RATIFIED-S242] — preserved verbatim from the legacy bid_questions.status column semantics. The legacy ResponseReviewStatus (types/bid.ts:88-94) for response-version review state is also preserved verbatim. No new statuses; no removed statuses.
B-12 — Citing a Q&A pair captures version-on-cite
Section titled “B-12 — Citing a Q&A pair captures version-on-cite”When the user accepts a Q&A pair as the source for a bid question response and the response moves toward shipment (state ready_for_export per B-5), the platform records a citation per 05-qa-flow.md §6 (citations polymorphic enum N8 RESOLVED-S234). The citation row carries:
citing_entity = 'bid_response'(one of the 5 polymorphic enum values per05-qa-flow.md§6.1).citing_entity_id= thebid_response.id(or the renamed equivalent post-T4).cited_q_a_pair_id= the corpus-level Q&A pair ID.- A version snapshot of the Q&A pair at the time of cite (resolved via
q_a_pair_historyper05-qa-flow.md§3.3 + §6.3).
The Q&A pair can continue to evolve via UC6 (B-15) post-citation; the shipped bid response resolves citations against the version snapshot per the version-on-cite ratification (05-qa-flow.md §6.3 RATIFIED-S229).
[RATIFIED-S229] per 0.9-edit-flow-investigation.md §6.0.3. [LOCKSTEP-WITH-MIGRATION] for the citations table polymorphic enum + version-on-cite trigger (T11 — citations enum + version-on-cite, per PLAN.md §4.11).
Cross-refs: [N8 RESOLVED-S234], [arch 05-qa-flow §6], [PLAN.md §4.11 T11].
Section C — Q&A .docx import (ABSORBED-S242 EP8 v5)
Section titled “Section C — Q&A .docx import (ABSORBED-S242 EP8 v5)”The four sub-sections C.1 – C.4 absorb the entirety of docs/specs/ep8-qa-docx-import-ui-spec.md v5 RATIFIED-S242 scope. The EP8 spec is superseded by this section at S242 close; archival to .planning/.archive/.specs/ is a Wave 3 deliverable per the S242 prompt.
B-13 — Admin can upload Q&A .docx files via the procurement settings surface
Section titled “B-13 — Admin can upload Q&A .docx files via the procurement settings surface”[ABSORBED-S242 EP8 v5 §4 + §5.4] — An admin user opens /settings?section=bid-library-import (legacy URL preserved during T4 transition; post-rename the section key MAY move to procurement-library-import per PLAN.md §4.4 T4 — TECH.md T-7 owns the migration). They see a drop-zone accepting up to 10 .docx files at 10 MB each. The UI surface lives at components/settings/bid-library-import-section.tsx (current name; post-T4 rename per PLAN.md §4.4) lazy-loaded via the SettingsSidebar admin-only filter.
Auth: admin role only — non-admins do NOT see the section entry per the existing settings-page pattern.
[ABSORBED-S242 EP8 v5 §5.4]. Cross-refs: [EP8 §4.1], [EP8 §5.4].
B-14 — Upload follows a 4-stage flow: presign → upload → analyse → import
Section titled “B-14 — Upload follows a 4-stage flow: presign → upload → analyse → import”[ABSORBED-S242 EP8 v5 §4.1-§4.4 + §5.1-§5.3]
The Q&A .docx ingest flow is:
- Presign — Client POSTs to
/api/ingest/qa-docx/presignwith{ files: [{ filename, size, mime }] }; receives a presigned Supabase Storage URL per file (5-minute expiry). - Upload — Client uploads the file bytes directly to Supabase Storage via the presigned URL. Vercel’s 4.5 MB body limit is bypassed by routing file bytes around the Next.js route.
- Analyse — Client POSTs to
/api/ingest/qa-docx/analysewith{ storage_keys: string[] }. The route reads files from Storage and returns per-file analysis: tracked-changes detection, pairs detected, draft-vs-final filename heuristic, cross-file title collisions, existing-in-DB matches. - Import — Client renders the analysis table; user picks options (auto-supersede, require-clean, force, skip-dedup, batch-tag); client POSTs to
/api/ingest/qa-docx/import; route creates apipeline_runsrow and kicks off the import asynchronously; client pollsGET /api/pipeline-runs/:iduntil terminal state.
This 4-stage flow is shipped verbatim from EP8 v5; the dependency direction reversed at EP8 v4→v5 (the Q&A ContentEditor + 5-path POST alignment shipped S198, removing all upstream sequencing per EP8 v5 §1.5).
[ABSORBED-S242 EP8 v5 §4 + §5]. Cross-refs: [EP8 §4], [EP8 §5].
B-15 — Track Changes, draft/final, and dedup surface as explicit pre-flight choices
Section titled “B-15 — Track Changes, draft/final, and dedup surface as explicit pre-flight choices”[ABSORBED-S242 EP8 v5 §1 + §4.2]
Per file in the upload batch, the analyse step surfaces four kinds of signal that drive the import-options form (never silent):
- Tracked Changes detection — Reads OOXML body XML for
<w:ins>/<w:del>tags. Reportshas_tracked_changes: boolean+tracked_changes_stats: { insertions, deletions }. User chooses: warn-only (default) or block (require_clean: true). - Duplicate detection at upload — Content-hash + title-normalised duplicate check against existing
content_itemsrows (or post-T7, againstq_a_pairsperPLAN.md§4.7). Reports count + sample matches. User chooses: soft-block (default) or skip-dedup (admin-only force override). - Draft-vs-final detection — Filename heuristic: incoming “final” + existing “DRAFT” → supersession candidate. Reports the supersession hint per file. User chooses: auto-supersede or manual review.
- Per-file result summary — Post-import, the summary card reports files processed, total pairs extracted, new pairs stored, duplicates flagged, pairs superseded, skipped, errored (link to the
pipeline_runsdashboard).
The four signals MIRROR the legacy scripts/import_bid_library.py CLI behaviour (the canonical reference). Parity is verified via the integration test at __tests__/integration/bid-library-ingest/parity.integration.test.ts per EP8 §9.3.
[ABSORBED-S242 EP8 v5 §1 + §4.2 + §9.3]. Cross-refs: [EP8 §1], [EP8 §4.2], [EP8 §9.3].
B-16 — Imported Q&A pairs land in the corpus, NOT the procurement workspace
Section titled “B-16 — Imported Q&A pairs land in the corpus, NOT the procurement workspace”[ABSORBED-S242 EP8 v5 §7.1] and aligned with the corpus-level cardinality model.
A Q&A .docx import creates content_items rows with content_type='q_a_pair' and ingest_source='qa_import' (CHECK enum value, established at EP8 ship time per supabase/migrations/20260428174512_add_ingest_source_to_content_items.sql). The rows enter the SHARED Q&A corpus — they are NOT scoped to the procurement workspace that initiated the upload. Workspace relevance is computed at retrieval time via scope_tag overlap (B-9) per the corpus-level cardinality ratification (04-workspace-types.md §5 + 05-qa-flow.md §1.1).
Post-T7 data migration (per PLAN.md §4.7), content_type='q_a_pair' is retired and new ingest rows land directly in q_a_pairs with origin_kind='imported_legacy' (T7) or a new origin_kind value (e.g. imported_docx_v1 — TECH.md T-9 owns the enum extension). The procurement spec does NOT freeze the post-T7 enum value; that’s a T7-owned decision.
[ABSORBED-S242 EP8 v5 §7.1] (the content_items payload shape with ingest_source='qa_import' + publication_status='in_review' + content_owner_id resolved via resolveContentOwnerId()) — preserved verbatim subject to T7 migration timing.
[LOCKSTEP-WITH-MIGRATION] for the post-T7 path (the import route’s INSERT target switches from content_items to q_a_pairs at T7-cutover; T7 owns the cutover sequencing).
Cross-refs: [EP8 §7.1], [arch 04-workspace-types §5], [arch 05-qa-flow §1.1], [PLAN.md §4.7 T7].
Section D — Bid response → Q&A promotion (UC5)
Section titled “Section D — Bid response → Q&A promotion (UC5)”B-17 — Shipping a bid response promotes it to the Q&A corpus
Section titled “B-17 — Shipping a bid response promotes it to the Q&A corpus”When a bid response in a procurement workspace transitions to ready_for_export or submitted state (per B-5), the user MAY (not MUST) promote the response to the cross-application Q&A corpus via the UC5 promotion flow. The promotion is KH-DB-only per 05-qa-flow.md §9.1 RATIFIED-S229 — no markdown sidecar materialisation in v1 (DEFERRED-v1.1 per §9.1).
The promotion:
- Creates a new
q_a_pairrow indraftpublication_status. - Sets
origin_kind='derived_from_bid_response'. - Records lineage:
source_workspace_id= the originating procurement workspace UUID (NULLABLE per Q-OQR1-07 — populated here for provenance audit only, NOT a scoping signal);promoted_from_bid_response_id= the bid response FK; thebid_question.idfrom which the response was derived. - Inherits
valid_fromset at promotion time;valid_toNULL (currently valid). - Routes to
in_reviewvia the cross-application review queue (curator or workspace admin per0.9-edit-flow-investigation.md§6.0.4).
If a close match exists in the corpus (cosine similarity ≥ 0.85 by default, tunable per workspace per 05-qa-flow.md §9.1 4.6.Q9 RESOLVED), the promotion surface presents options: supersede the existing pair (route to UC6), merge into the existing pair (UC6 with merge intent), or proceed regardless.
[RATIFIED-S229] per 05-qa-flow.md §9.1.
Cross-refs: [arch 05-qa-flow §9.1], [UC5 RATIFIED-S229].
B-18 — Lost-bid responses default to skip on promotion
Section titled “B-18 — Lost-bid responses default to skip on promotion”[RATIFIED-S234] per N10 RESOLVED (08-new-features.md §7.2). A bid response from a workspace in lost terminal state defaults to skip on the promotion surface — the response is NOT auto-promoted to a Q&A pair. The user can explicitly override per-response. Won-bid responses default to promote (UC5 happy path); withdrawn follows the same default-skip posture as lost.
The default-skip for lost bids prevents corpus pollution with answers the buyer rejected.
Cross-refs: [N10 RESOLVED-S234], [arch 08-new-features §7.2].
B-19 — Promotion UI shape is deferred to a separate PRODUCT spec
Section titled “B-19 — Promotion UI shape is deferred to a separate PRODUCT spec”The promotion UI shape (quality-gate checklist content, scope-tag picker behaviour, duplicate-detection threshold UX) is NOT specified inline in this spec. Per the 05-qa-flow.md §9.3 gap flag, those three sub-decisions land in docs/specs/qa-promotion-ui/PRODUCT.md (CONDITIONAL spec per PLAN.md §5 — materialises at UC5 build kickoff).
[DEFERRED-qa-promotion-ui-spec] — architecture-level direction is RATIFIED (this spec carries B-17 + B-18); the UI-shape sub-decisions are owned by docs/specs/qa-promotion-ui/PRODUCT.md.
Cross-refs: [arch 05-qa-flow §9.3], [PLAN.md §5 qa-promotion-ui].
Section E — Bid feedback 3-UC composite
Section titled “Section E — Bid feedback 3-UC composite”B-20 — Procurement is the v1 surface for the bid feedback 3-UC flywheel
Section titled “B-20 — Procurement is the v1 surface for the bid feedback 3-UC flywheel”The bid feedback loop (08-new-features.md §7) is the v1 user-data-quality flywheel composing three already-ratified UCs:
| UC | Action | This spec section | Architecture anchor |
|---|---|---|---|
| UC5 | Bid response → Q&A pair promotion | B-17 + B-18 | 05-qa-flow.md §9 RATIFIED-S229 |
| UC6 | Q&A pair revision (user-direct + AI-suggest) | B-21 (forward-ref) | 05-qa-flow.md §8 RATIFIED-S229 |
| UC8 | Cross-workspace dedup + merge | B-22 (forward-ref) | 08-new-features.md §3 + 05-qa-flow.md §11 RATIFIED-S229 |
The procurement application IS the v1 surface where this composition first lands — bids ship, responses become Q&A pairs, the Q&A corpus matures, future bids retrieve improved answers, and dedup keeps the corpus clean cross-workspace.
[RATIFIED-S229] for each constituent UC per 0.9-edit-flow-investigation.md §6.5 + §6.6 + §6.8; composition documented at 08-new-features.md §7.
Cross-refs: [arch 08-new-features §7], [UC5/UC6/UC8 RATIFIED-S229].
B-21 — UC6 (Q&A revision) is consumed by procurement curators, not authored by procurement
Section titled “B-21 — UC6 (Q&A revision) is consumed by procurement curators, not authored by procurement”UC6 (05-qa-flow.md §8) is the cross-application Q&A pair revision surface. Procurement curators / admins MAY revise Q&A pairs surfaced in their procurement workspaces (especially pairs with source_workspace_id = their procurement workspace per B-17). But UC6 is NOT a procurement-only surface — it’s owned by the Q&A domain (05-qa-flow.md) and consumed by every application_type that retrieves from q_a_pairs.
This spec forward-refs UC6 detail to 05-qa-flow.md §8 (the two sub-variants: user-direct revision KH-DB-only + AI-suggest revision Candidate B) and does NOT duplicate. The procurement application carries the user-flow surface for accessing Q&A pairs (B-8 composer + the review queue), but the revision flow itself is Q&A-domain.
[RATIFIED-S229] per 05-qa-flow.md §8.
Cross-refs: [arch 05-qa-flow §8], [UC6 RATIFIED-S229].
B-22 — UC8 (dedup + merge) is consumed by procurement, owned by Q&A + dedup
Section titled “B-22 — UC8 (dedup + merge) is consumed by procurement, owned by Q&A + dedup”UC8 (08-new-features.md §3) is the cross-workspace dedup + merge surface. Procurement workspaces are first-class consumers (the procurement Q&A corpus benefits directly from cross-workspace dedup once intelligence / sales_proposal workspaces emit Q&A pairs of their own). UC8 detection runs via cocoindex entity_resolution selective adoption per 08-new-features.md §3.3; user-driven merge / supersede / proceed-anyway resolution per the cross-UC downstream-impact UI pattern.
This spec forward-refs UC8 detail to 08-new-features.md §3 and does NOT duplicate.
[RATIFIED-S229] per 08-new-features.md §3.
Cross-refs: [arch 08-new-features §3], [UC8 RATIFIED-S229].
Section F — Edit-intent + freshness coupling
Section titled “Section F — Edit-intent + freshness coupling”B-23 — Procurement composer edits respect the edit_intent re-classification policy
Section titled “B-23 — Procurement composer edits respect the edit_intent re-classification policy”When a user edits text in the bid composer (a draft response in drafting state, an answer the composer previously suggested) and commits the edit, the platform captures an edit_intent value per the closed per-UC vocabulary from 02-data-flow.md §8.1 (cosmetic / data / structural). The intent governs whether the edit triggers cocoindex re-classification of the associated source content (02-data-flow.md §8.2 mapping).
For the bid composer specifically:
- Typo fixes / formatting tweaks →
cosmetic→ skips cocoindex re-run. - Factual updates to the response body →
data→ triggers cocoindex re-run on the source (if the edit propagates back to a sidecar — see B-24). - Section reorder / heading restructure →
structural→ triggers cocoindex re-run.
The composer edit surface is the entry point; 02-data-flow.md §8 owns the policy. This spec forward-refs 02-data-flow.md §8 and does NOT duplicate.
[RATIFIED-S234] via ONT.14 (0.9-decision-graph.md §11.1) for the edit_intent CV; [RATIFIED-DIRECTIONAL] per 02-data-flow.md §8.2 for the intent-to-action mapping.
Cross-refs: [arch 02-data-flow §8], [ONT.14 RATIFIED-S234].
B-24 — Draft bid responses are KH-DB-only; shipped responses MAY cite Q&A pairs
Section titled “B-24 — Draft bid responses are KH-DB-only; shipped responses MAY cite Q&A pairs”A draft bid response (workspace state drafting or in_review) lives in bid_responses (or the renamed equivalent post-T4) as KH-DB-only data. There is no markdown sidecar for bid responses in v1. Shipped responses (workspace state ready_for_export or beyond) MAY cite Q&A pairs (B-12) and MAY be promoted to Q&A pairs (B-17), at which point the citation + the Q&A pair carry the version-on-cite immutability binding.
[RATIFIED-S229] per 0.9-edit-flow-investigation.md §6.0.3 (version-on-cite); [DEFERRED-v1.1] per 05-qa-flow.md §4.4 for markdown-sidecar materialisation of approved Q&A pairs.
Cross-refs: [arch 05-qa-flow §4.4], [arch 05-qa-flow §6.3].
Section G — Cross-application integrations
Section titled “Section G — Cross-application integrations”B-25 — Change reports surface freshness signals for procurement workspaces
Section titled “B-25 — Change reports surface freshness signals for procurement workspaces”Procurement workspaces consume the cross-application change_reports surface (renamed from digests per 08-new-features.md §4) for freshness signals on Q&A pairs they cite. When a Q&A pair the workspace previously cited evolves (UC6 revision triggers q_a_pair_history per 05-qa-flow.md §3.3), the change-report surface highlights it for the affected workspace per the version-on-cite binding (B-12).
[LOCKSTEP-WITH-MIGRATION] with T5 (digests → change_reports rename per PLAN.md §4.5).
This spec forward-refs the change-report surface to 08-new-features.md §4 and does NOT duplicate.
Cross-refs: [arch 08-new-features §4], [PLAN.md §4.5 T5].
B-26 — Scope-tag governance (UC9) operates cross-workspace, including procurement
Section titled “B-26 — Scope-tag governance (UC9) operates cross-workspace, including procurement”UC9 (scope_tag governance — rename / split / merge across the corpus, 08-new-features.md §5) operates cross-workspace. A scope_tag rename initiated by an admin propagates to every q_a_pair carrying the renamed tag, and the per-workspace match results (B-9) reflect the change on the next retrieval. The procurement workspace is a passive consumer — it does NOT initiate UC9 operations from its own surface; UC9 lives in the admin / governance surface per 08-new-features.md §5.
[RATIFIED-S229] per 08-new-features.md §5.
This spec forward-refs to 08-new-features.md §5 and does NOT duplicate.
Cross-refs: [arch 08-new-features §5].
B-27 — Procurement MCP tools are renamed [LOCKSTEP-WITH-MIGRATION]
Section titled “B-27 — Procurement MCP tools are renamed [LOCKSTEP-WITH-MIGRATION]”The MCP tools serving procurement workspaces (list_active_bids, get_bid_detail, get_bid_question) rename in lockstep with the T2 + T4 procurement umbrella rename, per 06-mcp-tooling.md §6.3:
list_active_bids→list_active_procurementget_bid_detail→get_procurement_detailget_bid_question→get_procurement_question
cite_content and get_content_effectiveness are application-agnostic; no rename. Tool renaming is [LOCKSTEP-WITH-MIGRATION] per 06-mcp-tooling.md §6.3 — do NOT rename tools before the combined PR lands.
[RATIFIED-S235] per Q-OQR1-02 cascaded renames; [LOCKSTEP-WITH-MIGRATION] per 06-mcp-tooling.md §6.3.
Cross-refs: [arch 06-mcp-tooling §6.3], [Q-OQR1-02].
Section H — Real-data eval baseline
Section titled “Section H — Real-data eval baseline”B-28 — Procurement is the v1 application for real-data eval baseline
Section titled “B-28 — Procurement is the v1 application for real-data eval baseline”Per the product roadmap §1.4 (forward-ref to docs/reference/product-roadmap.json), the procurement application carries the v1 real-data eval baseline — the Phew bid library (~395 existing q_a_pair rows post-T7 migration, ~10 historical bid responses, ~5 procurement workspaces) is the ground-truth fixture set for evaluating retrieval quality (question_matches precision/recall on bid_question ↔ q_a_pair pairs).
[RATIFIED-roadmap-§1.4] (forward-ref). This spec forward-refs the eval baseline to docs/reference/product-roadmap.json §1.4 and does NOT duplicate the eval methodology.
Cross-refs: [product-roadmap §1.4].
Out of scope (v1)
Section titled “Out of scope (v1)”- Per-app columns beyond the OQ-Q38-E 6 —
reference_number,estimated_value,tender_source,tender_document_ids,outcome_notes, free-textnotesfrom the legacyBidMetadatashape (types/bid.ts:26-40) are NOT promoted to typed columns at v1. They EITHER stay in a residual JSONB column onprocurement_workspacesfor migration-tail compatibility OR drop entirely (T2 migration body authoritative; see TECH.md T-3). - Data-driven
form_typebehaviour — DEFERRED-v2 per Q-OQR1-14 (B-2). v1 form_type behaviour is code-driven inlib/procurement/. - Workspace-private Q&A pairs — DEFERRED-v1.1 per Q-OQR1-08 (
04-workspace-types.md§2.4). v1 Q&A corpus is shared cross-workspace; scope_tag overlap is the workspace-relevance substrate. - Markdown sidecar materialisation for shipped Q&A pairs — DEFERRED-v1.1 per
05-qa-flow.md§4.4 UC5 4.6.Q7 RESOLVED. v1 keeps approved Q&A pairs KH-DB-only. - Promotion UI shape (quality checklist / scope-tag picker UX / duplicate-detection threshold UX) — DEFERRED to
docs/specs/qa-promotion-ui/PRODUCT.mdper B-19. - Per-method scoring tunability (embedding vs fulltext weight blend, UI score presentation) — DEFERRED to
docs/specs/procurement-question-matching/TECH.mdper B-9. - MCP
q_a_searchtool wiring — owned by T10 retrieval substrate + T6 RPC implementation perPLAN.md§4.6 + §4.10; this spec is the consumer (B-9 + B-10), not the producer. - State machine evolution —
PROCUREMENT_WORKFLOW_STATES(B-5) is preserved verbatim fromBID_STATES; adding/removing states is a separate ratification cycle. - Multi-tenant procurement — v1 is single-tenant (Phew pilot per
01-vision.md); per-tenant RLS via the substrate workspaces table is inherited (B-29 below).
v1.1 candidates (DEFERRED — not blocking v1)
Section titled “v1.1 candidates (DEFERRED — not blocking v1)”- Markdown sidecar materialisation for approved Q&A pairs (per
05-qa-flow.md§4.4). - Data-driven
form_typebehaviour (per Q-OQR1-14 deferred v2; v1.1 candidate if SMB onboarding flow surfaces need). - Workspace-private Q&A pairs (per Q-OQR1-08 deferred v1.1).
- Admin UI for client-managed
application_typesextension (per Q-OQR1-13; covers adding new form_types beyond the v1 closed list). - Per-procurement-workspace residual JSONB compatibility column retire — once T2 migration tail is fully drained, drop the residual JSONB column (see TECH.md T-3).
- Per-method scoring blend UI (per B-9 deferred to
procurement-question-matching/TECH.md).
Section I — RLS + security
Section titled “Section I — RLS + security”B-29 — Procurement satellite RLS inherits from workspaces parent
Section titled “B-29 — Procurement satellite RLS inherits from workspaces parent”procurement_workspaces carries 4 RLS policies (select, insert, update, delete) that delegate to the parent workspaces row via the workspace_id FK + an EXISTS subquery — the same pattern as the 5 reserved seats per docs/specs/reserved-workspace-seats/PRODUCT.md S-5. No separate tenant_id column on the satellite; tenant scope flows from workspaces.id via the FK.
[RATIFIED-S240] per the RWS S-5 pattern (this spec applies the same pattern to procurement); [RLS-PATTERN P-1] + [RLS-PATTERN P-2] for the auto-RLS event trigger + per-role grants helper.
This spec forward-refs RLS pattern detail to docs/specs/rls-pattern/PRODUCT.md P-1..P-5 and does NOT duplicate.
Cross-refs: [RWS S-5], [rls-pattern PRODUCT P-1..P-5].
End of PRODUCT spec. Implementation references in ./TECH.md.