Skip to content

Completing Forms (Procurement) — Technical Reference

⚠️ BID-ERA HISTORICAL REFERENCE (bannered S462). Last verified S248 — predates the id-61 renames, id-130 per-form domain model, DR-014 (manual form upload), DR-038 (workspace retirement), and DR-041 (three-zone IA). Valuable for flow/mechanism archaeology only; hook names, table names, workspace-level state framing, and the “100s draft-all timeout” (retired S224 — async 202 queue) are all stale. Current model: ID-145 — specs/id-145-procurement-form-first/RESEARCH.md.

Last verified: Session 248 (20 May 2026) — refreshed for S248 T4 procurement umbrella rename (lib/bid → lib/procurement, components/bid → components/procurement, /api/bids → /api/procurement, BID_STATES → PROCUREMENT_WORKFLOW_STATES, file-level renames); ID-71 bid→forms rename + outcome-grouped MCP tool surface (form_questions/form_responses/form_response_history tables, citations table, get_form_* RPCs, find/get/where_are_we_exposed/whats_in_my_queue tools); prior S224 (05 May 2026) for S224 §5.4.1 batch-draft-all IMPL ship (route now async-queued via processing_queue; UI polling pattern); prior S210 (29 April 2026) for S195-S209 + prod-readiness S10-S13 Pending updates: None (post-S209 spec ratification for §1.7 admin dedup + §1.9 near-duplicate merge dashboard — those impl waves haven’t shipped, so no doc impact)

Completing Forms (Procurement) — Technical Reference

Section titled “Completing Forms (Procurement) — Technical Reference”

Completing Forms governs the creation, analysis, drafting, review, and export of form responses within Canonical. It spans tender file ingestion (PDF/DOCX), structured Q&A extraction, KB-backed matching via embeddings, a three-pass AI drafting pipeline (Sonnet/Opus/Haiku), markdown-native Tiptap editing, and DOCX/XLSX export. Q&A library content (canonical Q&A pairs in content_items with answer_standard / answer_advanced) is the primary KB source feeding form drafts; the /library (Q&A Library) surface and the form session both edit through the same useInlineFieldEdit shape since the S198 §1.5 ContentEditor upgrade.

For current API/component/hook counts, see docs/generated/codebase-stats.md. For current MCP tool listings, see docs/generated/mcp-inventory.md.

29 route files under app/api/procurement/, organised by functional group. All routes require authentication; write operations are gated to admin + editor via getAuthorisedClient(['admin', 'editor']).

MethodRouteAuthPurposeFile
GET/POST/api/procurementAll authList and create procurement workspacesapp/api/procurement/route.ts
GET/PATCH/DELETE/api/procurement/[id]All authRetrieve, update, or delete a formapp/api/procurement/[id]/route.ts
MethodRouteAuthPurposeFile
GET/POST/api/procurement/[id]/questionsAll authList and create questionsapp/api/procurement/[id]/questions/route.ts
PATCH/DELETE/api/procurement/[id]/questions/[qId]All authUpdate or delete a questionapp/api/procurement/[id]/questions/[qId]/route.ts
POST/api/procurement/[id]/questions/extractAll authExtract Q&A from uploaded tenderapp/api/procurement/[id]/questions/extract/route.ts
POST/api/procurement/[id]/questions/matchAll authMatch questions to KB content (RPC)app/api/procurement/[id]/questions/match/route.ts
MethodRouteAuthPurposeFile
GET/PATCH/api/procurement/[id]/responses/[rId]All authGet or update a single responseapp/api/procurement/[id]/responses/[rId]/route.ts
GET/api/procurement/[id]/responses/[rId]/historyAll authResponse version historyapp/api/procurement/[id]/responses/[rId]/history/route.ts
POST/api/procurement/[id]/responses/[rId]/regenerateEditor+Re-draft via runDraftingPipelineapp/api/procurement/[id]/responses/[rId]/regenerate/route.ts
POST/api/procurement/[id]/responses/[rId]/restoreAll authRestore a previous response versionapp/api/procurement/[id]/responses/[rId]/restore/route.ts
POST/api/procurement/[id]/responses/draftAll authSingle-question 3-pass draftingapp/api/procurement/[id]/responses/draft/route.ts
POST/api/procurement/[id]/responses/draft-allEditor+Batch drafting via processing_queue (S224 §5.4.1 IMPL — 202+{job_id, pipeline_run_id, deduplicated}, async-polled by UI; handler in lib/queue/handlers/procurement-draft-all.ts, job type form_draft_all)app/api/procurement/[id]/responses/draft-all/route.ts
POST/api/procurement/[id]/responses/draft-streamAll authSSE-streamed drafting via ReadableStreamapp/api/procurement/[id]/responses/draft-stream/route.ts
POST/api/procurement/[id]/responses/estimateAll authEstimate drafting effort/costapp/api/procurement/[id]/responses/estimate/route.ts
MethodRouteAuthPurposeFile
POST/api/procurement/[id]/export/docxAll authExport form as DOCXapp/api/procurement/[id]/export/docx/route.ts
POST/api/procurement/[id]/export/xlsxAll authExport form as XLSXapp/api/procurement/[id]/export/xlsx/route.ts
MethodRouteAuthPurposeFile
POST/api/procurement/[id]/tenderAll authUpload tender (PDF/DOCX)app/api/procurement/[id]/tender/route.ts
GET/api/procurement/[id]/tender/downloadAll authDownload stored tender fileapp/api/procurement/[id]/tender/download/route.ts
MethodRouteAuthPurposeFile
GET/POST/api/procurement/[id]/templatesAll authList and create templatesapp/api/procurement/[id]/templates/route.ts
GET/DELETE/api/procurement/[id]/templates/[templateId]All authGet or delete a templateapp/api/procurement/[id]/templates/[templateId]/route.ts
POST/api/procurement/[id]/templates/[templateId]/analyseAll authAnalyse template requirementsapp/api/procurement/[id]/templates/[templateId]/analyse/route.ts
POST/api/procurement/[id]/templates/[templateId]/auto-mapAll authAuto-map fields to contentapp/api/procurement/[id]/templates/[templateId]/auto-map/route.ts
POST/api/procurement/[id]/templates/[templateId]/fillAll authFill template with contentapp/api/procurement/[id]/templates/[templateId]/fill/route.ts
PATCH/api/procurement/[id]/templates/[templateId]/fields/[fieldId]All authUpdate a template fieldapp/api/procurement/[id]/templates/[templateId]/fields/[fieldId]/route.ts
POST/api/procurement/[id]/templates/[templateId]/fields/bulk-updateAll authBulk update template fieldsapp/api/procurement/[id]/templates/[templateId]/fields/bulk-update/route.ts
GET/api/procurement/[id]/templates/[templateId]/completions/[completionId]/downloadAll authDownload completed templateapp/api/procurement/[id]/templates/[templateId]/completions/[completionId]/download/route.ts
MethodRouteAuthPurposeFile
GET/api/procurement/[id]/readinessAll auth7-criteria readiness APIapp/api/procurement/[id]/readiness/route.ts
POST/api/procurement/[id]/outcomeAll authRecord form outcomeapp/api/procurement/[id]/outcome/route.ts
POST/api/procurement/[id]/outcome/integrateAll authIntegrate outcome learningsapp/api/procurement/[id]/outcome/integrate/route.ts

POST /api/procurement/[id]/responses/draft-stream

Section titled “POST /api/procurement/[id]/responses/draft-stream”

Streams incremental response formulation using raw SSE via ReadableStream and draftResponseStreaming from lib/ai/draft.ts. Emits structured events for matching, analysis, drafting, and completion. Consumed in the session UI by useDraftStream.

POST /api/procurement/[id]/responses/[rId]/regenerate

Section titled “POST /api/procurement/[id]/responses/[rId]/regenerate”

Wraps runDraftingPipeline() from lib/ai/draft.ts with a 5-req/min/user rate limit and maxDuration = 120. Validates the body via ResponseRegenerateBodySchema from lib/validation/schemas.ts. Used by the “Redraft” UI affordance — internal naming retained as handleRegenerate.

components/procurement/ (21 files) plus components/qa/ (5 files, shared with the Q&A Library route). For exact counts, see docs/generated/codebase-stats.md.

ComponentFilePurpose
ProcurementContextProvidercomponents/procurement/procurement-context-provider.tsxProcurement state context for child components
ProcurementCreationWizardcomponents/procurement/procurement-creation-wizard.tsxMulti-step form creation (Create Blank vs Create & Upload Tender)
ProcurementExportMenucomponents/procurement/procurement-export-menu.tsxDOCX/XLSX export trigger
ProcurementListCardcomponents/procurement/procurement-list-card.tsxProcurement summary card on /procurement
ProcurementWorkflowBadge / ProcurementWorkflowSteppercomponents/procurement/procurement-workflow-indicator.tsxVisual badge + stepper for form lifecycle state
ProcurementOutcomeDialogcomponents/procurement/procurement-outcome.tsxWin/loss/withdrawn outcome dialog
DraftRecoveryDialogcomponents/procurement/draft-recovery-dialog.tsxPrompt to recover localStorage draft on remount
KBIntegrationReviewcomponents/procurement/kb-integration-review.tsxPre-draft KB-match review (post-S198 §1.6: uses stripMarkdown for previews)
QuestionListcomponents/procurement/question-list.tsxQuestion list view on /procurement/[id]
QuestionNavigatorcomponents/procurement/question-navigator.tsxCross-question navigation in session
QuestionReviewcomponents/procurement/question-review.tsxReview extracted questions before drafting
QuestionRowcomponents/procurement/question-row.tsxIndividual question row
ReadinessChecklistcomponents/procurement/readiness-checklist.tsx7-criteria readiness UI; co-exports ReadinessBadge
ResponseActionscomponents/procurement/response-actions.tsxSave/Approve/Redraft/Regenerate action buttons
ResponseEditorcomponents/procurement/response-editor.tsxTiptap + @tiptap/markdown markdown-native editor (S182 onwards)
ResponseVersionHistorycomponents/procurement/response-version-history.tsxVersion history (post-S198 §1.6: renders via ContentRenderer + htmlToMarkdown)
TemplateCompletionSummarycomponents/procurement/template-completion-summary.tsxSummary of completed templates
TemplateFieldReviewcomponents/procurement/template-field-review.tsxReview individual template fields
TemplateFillProgresscomponents/procurement/template-fill-progress.tsxTemplate completion progress
TenderMetadataPromptcomponents/procurement/tender-metadata-prompt.tsxPrompt for tender metadata entry
TenderUploadcomponents/procurement/tender-upload.tsxTender document upload
WarningsBannercomponents/dashboard/warnings-banner.tsxRenders accumulated batch-operation warnings
ComponentFilePurpose
QAAnswerDisplaycomponents/qa/qa-answer-display.tsxRead + edit panels for answer_standard / answer_advanced (S198 §1.5)
QAPairRenderercomponents/qa/qa-pair-renderer.tsxRenders read-mode Q&A pair via ContentRenderer
QARowcomponents/qa/qa-row.tsxSingle Q&A row in list contexts
QAPreviewListcomponents/qa/qa-preview-list.tsxPreview list during batch import
BatchQAPreviewTablecomponents/qa/batch-qa-preview-table.tsxTabular preview of batch-imported Q&A pairs
HookFilePurpose
useFormActionshooks/procurement/use-procurement-actions.tsForm CRUD + URL-synced tab/filter state (?tab=&q=&status=&sort=)
useBidExporthooks/procurement/use-procurement-export.tsOrchestrates DOCX/XLSX export + downloads
useBidReadinesshooks/procurement/use-procurement-readiness.tsFetches readiness checklist (TanStack Query)
useBidResponseActionshooks/procurement/use-procurement-response-actions.tsResponse-level mutations (save, approve, regenerate)
useBidSessionhooks/procurement/use-procurement-session.tsSession-level state — current question, response, draft buffer
useDraftRecoveryhooks/streaming/use-draft-recovery.tsRecovers localStorage-snapshotted unsaved drafts
useDraftStreamhooks/streaming/use-draft-stream.tsManages a single SSE stream for draft response
useStreamCoordinationhooks/streaming/use-stream-coordination.tsCoordinates concurrent streams (one per question)
useInlineFieldEdithooks/use-inline-field-edit.tsSingle-field-at-a-time edit shape consumed by QAAnswerDisplay (S198)
ModuleFilePurpose
draft.tslib/ai/draft.ts3-pass pipeline (analyseQuestion, draftResponse, draftResponseStreaming, runDraftingPipeline)
match.tslib/ai/match.tsPure helpers — assessConfidence, deduplicateResults, MATCH_THRESHOLDS
extract-questions.tslib/ai/extract-questions.tsPDF/DOCX question extraction via Anthropic SDK
quality-check.tslib/ai/quality-check.tsPass-3 deterministic + AI quality gate
ModuleFilePurpose
procurement-workflow.tslib/procurement/procurement-workflow.ts10-state machine, canTransition, getAvailableTransitions, isTerminal, PROCUREMENT_WORKFLOW_PROGRESSION
procurement-export-docx.tslib/procurement/procurement-export-docx.tsDOCX export via markdownToDocxParagraphs + stripMarkdown (markdown-native since S182)
procurement-export-xlsx.tslib/procurement/procurement-export-xlsx.tsXLSX export with exceljs
procurement-export-data.tslib/procurement/procurement-export-data.tsExport-data assembly
procurement-export-types.tslib/procurement/procurement-export-types.tsShared export types
procurement-helpers.tslib/procurement/procurement-helpers.tsPure helpers (e.g. getDeadlineProximity)
procurement-queries.tslib/procurement/procurement-queries.tsfetchActiveProcurementWithStats (used by dashboard + reorientation paths)
ModuleFilePurpose
extract-answer.tslib/procurement-library-ingest/extract-answer.tsAnswer-cell extraction from Q&A tables
resolve-question.tslib/procurement-library-ingest/resolve-question.tsQuestion identification heuristics
ModuleFilePurpose
template-auto-map.tslib/templates/template-auto-map.tsAuto-map template fields to KB content
template-coverage.tslib/templates/template-coverage.tsCoverage analysis for filled templates
ModuleFileForm-completion relevance
publication-transitions.tslib/governance/publication-transitions.tsKB content rendered into form drafts honours publication_status lifecycle (S205-S208 §5.2 Phases 1+2)
cadence-renewal.tslib/governance/cadence-renewal.tsQ&A items receive a next_review_date via §5.5 backfill (Q&A cohort: 395 @ 180d)
TablePurposeKey ColumnsRLS / Notes
workspacesAnchor for a form (or other type)discriminator is application_types.key ('procurement') via JOIN (the workspaces.type col was dropped post-T2); domain_metadata (deadline, buyer, outcome), is_archivedAll auth: SELECT; Editor+: write
form_questionsExtracted tender questionssection_name, question_sequence, confidence_posture, matched_content_ids (uuid[])All auth
form_responsesDrafted responses (1:1 with question)response_text (markdown), response_text_advanced, review_status, version, drafted_by, metadata (Json), source_content_idsAll auth
form_response_historyAppend-only response snapshotsversion, change_reason, response_textAppend-only
citationsLinks form responses to source items (polymorphic)citing_form_response_id, cited_content_item_id, citation_typeEditor+ to write via cite_content MCP tool
template_requirementsRequired field definitions in templatesrequirement_embedding, requirement_textEditor+
template_fieldsTemplate field statetemplate_id, question_id, mapped_content_idsEditor+
template_completionsFilled-template artefact trackingfields_filled, storage_pathEditor+
content_itemsKB source feeding form drafts (Q&A pairs, policies, case studies, etc.)publication_status (S205-S208), next_review_date, review_cadence_days, answer_standard, answer_advancedRLS via get_user_role()
FunctionPurposeForm-completion relevance
search_for_form_responseHybrid semantic + keyword search tuned for the drafting pipeline (with win-rate boost)Used by /api/procurement/[id]/questions/match
hybrid_searchGeneral-purpose hybrid searchIndirect — KB browse
search_content_chunksSection-level chunk search (S208 widened with filter_overdue_review + filter_review_due_within_days)MCP-callable; form agents can request lifecycle-filtered chunks
get_form_question_stats_batchSingle-RPC stats for all formsPowers /procurement listing + dashboard cards
get_form_question_statsPer-form statsPowers get_procurement_detail MCP tool

search_for_form_response defaults include_superseded = false (S186 WP-B.3, migration 20260421223339_add_include_superseded_to_search_rpcs.sql) — the form draft pipeline therefore never matches against superseded KB content unless the caller opts in.

Procurement-specific tools in lib/mcp/tools/procurement.ts. KB search is served by the outcome-grouped find tool in lib/mcp/tools/search.ts (ID-71 folded the former search_knowledge_base / search_qa_library / search_content_chunks / find_similar_items tools into one find with type / scope / granularity / similar_to params). Current tool counts in docs/generated/mcp-inventory.md.

ToolRead-onlyPurpose
list_active_procurementYesPipeline view of active procurement workspaces
get_procurement_detailYesFull form record + question stats
get_form_questionYesSingle question + matched content + response
cite_contentNoEditor+; links a content item to a form response (writes citationsciting_form_response_id / cited_content_item_id)
get_content_effectivenessYesSurfaces win-rate / citation-count for a content item
show_procurement_dashboardYesApp-trigger tool — renders the Procurement Dashboard MCP App (ui://form-dashboard/app.html)
findYesOutcome-grouped KB search; the Q&A branch (type) and section-level branch (granularity: 'chunk', S208 review-cadence filters) feed form drafting; similar_to branch for semantic discovery
search_for_form_responseYesRPC name backing /api/procurement/[id]/questions/match (not directly exposed as a tool)
PromptArgsPurpose
form_briefingform_nameReorient on a single form
draft_responsequestion_textDraft a response to a tender question via KB
form_pipeline_reviewstale_threshold_daysPipeline-wide blockers + prioritised next actions
URIPurpose
kb://forms/{id}Single form (procurement) workspace as JSON
kb://qa/{id}Q&A pair as JSON
ui://form-dashboard/app.htmlProcurement Dashboard MCP App (interactive UI)
SettingLocationDefaultPurpose
ANTHROPIC_API_KEY.env.localAll AI calls (analyse, draft, quality-check)
OPENAI_API_KEY.env.localEmbeddings (text-embedding-3-large)
MATCH_THRESHOLDS.stronglib/ai/match.ts0.7Threshold for strong_match posture (≥2 matches above)
MATCH_THRESHOLDS.partiallib/ai/match.ts0.5Threshold for partial_match posture (≥1 match above)
MATCH_THRESHOLDS.minimallib/ai/match.ts0.3Below this = no_content
maxDuration (regenerate)route file120Vercel function timeout for response regeneration
draft-all queue runtime caplib/queue/handlers/procurement-draft-all.ts (handler) + app/api/cron/process-queue/route.ts:4260s per cron tick (Vercel Pro)Per-tick budget for the form_draft_all handler in the queue worker. Multi-tick fan-out via processing_queue.status='pending' requeue. Prior 100s safety timeout retired in S224 §5.4.1 refactor — route is now async-queued (HTTP 202) so a single request never blocks for the loop.
Rate limit (regenerate)app/api/procurement/[id]/responses/[rId]/regenerate/route.ts5/min/userPrevents redraft floods

Form-completion routes use the project-wide safe-query patterns to prevent silent failures:

  • sb() (fail-fast) — throws on any Supabase error, used in critical paths like draft saving, export generation, and fetchActiveProcurementWithStats.
  • tryQuery() (Result-returning) — { data, error } for paths where partial success is acceptable.
  • warningsEnvelope() — wraps composite responses (e.g. batch drafting) so the client receives both results and non-fatal warnings.
  • WarningsBanner (components/dashboard/warnings-banner.tsx) — renders accumulated warnings from batch operations.
  • authFailureResponse(auth) — routes the discriminated-union failure reason to the correct HTTP status (401 / 403 / 500).

Enforced by ESLint rules local/no-unchecked-supabase-error and local/no-silent-promise-catch. Spec: docs/specs/silent-failure-prevention-spec.md.

Structured logging (production-readiness §9.16): all form-completion routes emit correlation-id-tagged logs; failures surface via Sentry with release tagging (per kh-prod-readiness S10-S13).

Form-completion + Q&A test files (counts move with code; see docs/generated/codebase-stats.md):

Test FileCovers
__tests__/api/procurement-drafting.test.tsrunDraftingPipeline integration + drafting pipeline flow
__tests__/api/procurement-questions-pipeline.test.tsQuestion extraction pipeline
__tests__/api/procurement-questions-create.test.tsPOST /questions
__tests__/api/procurement-export.test.tsDOCX/XLSX export route handlers
__tests__/api/procurement-outcome.test.tsOutcome recording
__tests__/api/procurement-readiness.test.tsReadiness checklist API
__tests__/api/procurement-responses-crud.test.tsResponse CRUD operations
__tests__/api/procurement-responses.test.tsResponse handlers (draft/draft-all/regenerate/restore/history/save)
__tests__/api/procurement-workflow-integration.test.tsState-machine + DB integration
__tests__/api/procurement.test.tsWorkspace CRUD
__tests__/api/items/qa-content-rebuild.test.tsPATCH preserves Q: {question}\n\n prefix (S198 §4.1 H2 fix)
__tests__/api/items/qa-create-alignment.test.tsQ&A create populates answer_standard correctly
__tests__/app/procurement-boundaries.test.tsxProcurement page error/loading boundaries
__tests__/app/procurement/[id]/page.test.tsxProcurement detail page
__tests__/app/procurement/[id]/page-mobile.test.tsxMobile procurement detail UI
__tests__/components/procurement-creation-wizard.test.tsx2-card wizard layout
__tests__/components/procurement-export-menu.test.tsxExport menu
__tests__/components/procurement-outcome.test.tsxOutcome dialog
__tests__/components/procurement-workflow-indicator.test.tsxWorkflow badge + stepper
__tests__/components/procurement/draft-recovery-dialog.test.tsxRecovery dialog
__tests__/components/procurement/readiness-checklist.test.tsxReadiness UI
__tests__/app/browse/browse-content-from-procurement.test.tsxBrowse-to-procurement pipeline
__tests__/components/qa-answer-display.test.tsxS198 §1.5 Q&A ContentEditor (single-field edit, regen-embed checkbox)
__tests__/components/qa-pair-renderer.test.tsxQ&A read mode
__tests__/components/qa-row.test.tsxQ&A row in list
__tests__/components/qa-preview-list.test.tsxBatch preview list
__tests__/eval/procurement-drafting-eval.test.tsForm drafting eval wrapper (synthetic gold-standard fixture, baseline deferred — no real form data)
__tests__/hooks/procurement/use-procurement-actions.test.tsURL-synced tab state
__tests__/hooks/use-procurement-readiness.test.tsReadiness hook
__tests__/integration/qa-editor-chunk-parity-and-regen.integration.test.tsS198 chunk + embedding regen parity
__tests__/integration/qa-editor-create-post-populates-answer-standard.integration.test.tsInitial Q&A creation alignment
__tests__/integration/qa-editor-patch-content-shape-reconciliation.integration.test.tsPATCH content rebuild (Q: prefix)
__tests__/integration/queue/procurement-draft-all.integration.test.tsQueued batch draft-all handler
__tests__/lib/procurement-drafting.test.tsPure drafting logic
__tests__/lib/procurement/procurement-export-docx.test.tsmarkdownToDocxParagraphs (incl. tables, lists, headings)
__tests__/lib/procurement/procurement-export-xlsx.test.tsexceljs integration
__tests__/lib/procurement-library-ingest/extract-answer.test.tsAnswer-cell extraction
__tests__/lib/procurement-library-ingest/resolve-question.test.tsQuestion identification
__tests__/lib/procurement-matching.test.tsassessConfidence, deduplicateResults
__tests__/lib/procurement/procurement-workflow.test.tsAll transitions + back-transitions + terminal states
__tests__/lib/queue/handlers/procurement-draft-all.test.tsform_draft_all handler unit coverage
__tests__/lib/qa-detection.test.tsQ&A detection heuristics
__tests__/mcp/procurement-pipeline-review-prompt.test.tsform_pipeline_review MCP prompt
__tests__/mcp/formatters-bids.test.tsForm-formatter Markdown output
__tests__/validation/procurement-schemas.test.tsZod schema validation
__tests__/validation/no-bid-regression-guard.test.tsRegression guard against reintroducing dropped bid identifiers

E2E coverage at e2e/tests/bid-pipeline.spec.ts (10 deterministic test-drift fixes shipped S153) plus 3 new S198 §1.5 scenarios for the Q&A ContentEditor.

  • No isolated draft scope per user — all authenticated users see all drafts. There is no per-user “my drafts” filter.
  • No real-time collaborationTiptap Collaboration is not wired in. Edits serialise via standard REST PATCH saves, increasing conflict probability under concurrent editing.
  • localStorage-bound recoveryuseDraftRecovery snapshots into localStorage, so unsaved drafts cannot be retrieved from a different browser. Spec deliberate: cross-device drafts would require a dedicated draft-persistence table.
  • DOCX extraction relies on a TS+Python ecosystem — a form-library ingest path and Python-side cell converters fill gaps (mammoth.convertToMarkdown() drops tables; the Python cell converter loses links + lists + nested tables relative to the TS extractor).
  • Form drafting eval baseline deferred — the eval runner ships and the fixture is 24 synthetic items, but the regression baseline is blocked on real form data per SoTP §AI Evaluation.
  • search_for_form_response excludes superseded by default — calls that want superseded matches must pass include_superseded=true. Form drafting routes do not opt in (this is the intended behaviour).
  • §1.7 admin dedup review + §1.9 near-duplicate merge dashboard ratified but not implemented — specs landed S209 WP2; implementation is on the pre-launch backlog. Form drafts will continue to surface duplicate Q&A matches until the dedup UI ships.
DecisionRationaleAlternative Considered
3-pass pipeline (Sonnet → Opus → Haiku)Citations + structured outputs are incompatible in a single Anthropic call; cheap final QA via HaikuSingle-call output with post-hoc citation extraction
Markdown-native editing (S182, P0-BM Phase 2)Eliminates HTML/MD round-trip data loss; consistent with KB-wide markdown adoption (S166)HTML round-trip via dompurify + tiptap-html
Single-field-at-a-time Q&A edit (S198 §1.5)Avoids stale-baseline shrinkage when editing both Standard + Advanced together; matches useInlineFieldEdit shapeDual-editor mount with shared baseline
Per-field regen-embedding checkbox (S198 §1.5)Editing a single field shouldn’t force a re-embed of the joined content_items.content; user opt-inAlways regenerate on save (cost) / never regenerate (drift)
search_for_form_response with win-rate boostEmpirical signal: matches cited in winning tenders should rank higherPlain hybrid_search
LocalStorage draft recoveryBrowser-only, zero round-trip cost; covers the “tab crash” failure mode without a server tableDedicated draft-persistence table — defer until real-time editing arrives
KB content honours publication_status lifecycle (S205-S208)Drafts must not pull from 'draft' or 'archived' KB items; the publication-status enum handles thisPer-content-item visibility flags
Form review_status on form_responses is a free-text stringApplication-level distinction from KB-wide governance_review_status; the two are deliberately separateSingle shared review enum
Q&A library lives in content_items (not a separate table)“One record, many views” — Q&A pairs share the canonical content schema with all other KB itemsDedicated qa_pairs table