Skip to content

Completing Forms (Procurement) — User Journeys

⚠️ 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); ID-71 bid→forms rename + outcome-grouped MCP tool surface (form_questions/form_responses tables, find/get/where_are_we_exposed/whats_in_my_queue tools); prior S210 (29 April 2026) for S195-S209 + prod-readiness S10-S13 Pending updates: None

Completing Forms (Procurement) — User Journeys

Section titled “Completing Forms (Procurement) — User Journeys”

Completing Forms provides workflows for responding to UK public-procurement tenders. Editors and admins extract question structures from uploaded PDF/DOCX tenders, draft responses via the contextually-aware 3-pass AI pipeline, curate and review responses inside a dedicated Tiptap session, and export the final document. The Q&A Library (/library) is the parallel surface for maintaining canonical Q&A pairs that feed the form drafting pipeline; the same inline-edit shape (useInlineFieldEdit) is used in both surfaces since the S198 §1.5 ContentEditor upgrade.

Viewers may inspect forms and export, but cannot mutate state.

Entry PointRouteAccessible By
Procurement index/procurementAll roles
Procurement detail/procurement/[id]?tab={overview|questions|documents}&q=&status=&sort=All roles
Procurement session/procurement/[id]/sessionAll roles
Procurement templates/procurement/[id]/templatesAll roles
Q&A Library/libraryAll roles
Item detail/item/[id] (for canonical Q&A pairs)All roles
Reorientation/ (Reorient Me — active-procurement panel)All roles
MCP Appui://form-dashboard/app.html (via show_procurement_dashboard)Authenticated MCP clients

Procurement detail tabs are URL-synced via ?tab=, ?q= (search), ?status= (filter), and ?sort= (P1-5, S184) — deep-links and browser-refresh persist state. Managed by useFormActions.

Actor: Editor or admin Goal: Ingest a tender document and prepare the workspace for answering. Preconditions: Tender file is available in DOCX or PDF format; user has editor or admin role.

  1. Create the form workspace

    • Route: /procurement → “New” button → ProcurementCreationWizard
    • Component: ProcurementCreationWizard (equal-weight 2-card layout — grid-cols-1 sm:grid-cols-2 — Create Blank vs Create & Upload Tender)
    • What the user sees: Title + buyer + deadline metadata entry, with the option to upload the tender immediately.
  2. Upload & extract questions

    • Route: /procurement/[id] (Documents tab)
    • Component: TenderUpload
    • Action: POST /api/procurement/[id]/tender stores the file; an “Extract Questions” CTA triggers POST /api/procurement/[id]/questions/extract (Claude PDF/DOCX extraction via lib/ai/extract-questions.ts).
    • Result: Structured questions appear under the Questions tab with confidence_posture: null (matching not yet run).
  3. Review extracted questions (optional)

    • Component: QuestionReview
    • Action: User confirms / edits / merges extracted questions before they enter the pipeline. Confirms via handleQuestionReviewConfirmed.
  4. Match to KB content

    • Action: User clicks “Match to KB” (admin/editor only) → POST /api/procurement/[id]/questions/match runs search_for_form_response for each generated query and writes confidence_posture + matched_content_ids to each form_questions row.
    • Result: Each question gains a posture (strong_match, partial_match, needs_sme, or no_content), driving the priority-ordered draft queue.
  5. Assess readiness

    • Component: ReadinessChecklist (with ReadinessBadge)
    • What the user sees: A 7-criteria checklist surfacing whether extraction, matching, KB integration review, drafting, citation coverage, review approval, and export prerequisites are met. The useBidReadiness hook polls /api/procurement/[id]/readiness.
  • Viewer: Cannot upload, extract, match, or transition state. Sees the same surface in read-only mode (controls hidden via useUserRole().canEdit).
  • Mobile: Procurement detail page collapses the side panels; the action menu consolidates into a MoreHorizontal dropdown (S153 mobile flake on the “New button visible for editor” assertion is logged in backlog as S153-Mobile-1 — pre-existing, not blocking).
  • Empty extraction (corrupt PDF, encrypted DOCX): the route returns a structured error; the UI surfaces a “no questions found” toast and keeps the form in draft state.
  • All-low-confidence matches: the readiness checklist flags the form as not draft-ready; the user is prompted to add KB content via /item/new before drafting.

Actor: Editor or admin Goal: Draft a well-cited, high-quality answer to a specific tender question. Preconditions: Questions extracted; KB matching complete; user has editor or admin role.

  1. Enter the working session

    • Route: /procurement/[id]/session
    • Page: the session page at app/procurement/[id]/session/page.tsx
    • What the user sees: The Tiptap ResponseEditor, a QuestionNavigator for cross-question navigation, the CitationPanel (default-expanded for admin role or review-or-later form states), and the ContentLibraryDrawer for inserting KB snippets.
  2. Trigger AI drafting (3-pass)

    • Action: User clicks “Draft” → useDraftStream opens an SSE stream against /api/procurement/[id]/responses/draft-stream.
    • System steps:
      • Pass 1: analyseQuestion (Sonnet) — identifies topic, structure, tone.
      • Pass 2: draftResponse (Opus) — drafts with inline citations.
      • Pass 3: checkResponseQuality (Haiku) — deterministic + AI quality gate.
    • Result: Markdown streams into the editor as it’s generated; the StreamingPhaseIndicator shows the current phase.
  3. Edit the response

    • The editor operates in markdown-native mode (S182 P0-BM Phase 2):
      • @tiptap/markdown extension reads/writes markdown via editor.getMarkdown(). Stored in form_responses.response_text.
      • Table extensions (Table, TableRow, TableHeader, TableCell) loaded so GFM tables survive parse/serialise cycles.
      • CharacterCount is a soft indicator only — no hard limit; the word count footer warns when over-limit or under-target.
    • Auto-save: editor content debounced into localStorage via useDraftRecovery (cleared on successful save / accept).
  4. Cite + save

    • Action: User clicks “Save” → PATCH /api/procurement/[id]/responses/[rId] persists response_text. Citations link via cite_content MCP tool or via the Citation Panel UI; citations records the citing_form_response_id ↔ cited_content_item_id link.
    • Result: A new row lands in form_response_history (append-only; change_reason populated). Version increments.
  5. Redraft / regenerate (optional)

    • Action: User clicks “Redraft” (UI label for AI regeneration; internal handler is handleRegenerate).
    • Endpoint: POST /api/procurement/[id]/responses/[rId]/regenerate — wraps runDraftingPipeline with rate-limit (5/min/user) and 120s function timeout. Body validated by ResponseRegenerateBodySchema.

The CitationPanel below the response editor is default-expanded when:

  • The viewer is an admin, or
  • The form is in a review-or-later state (in_review, ready_for_export, submitted, won, lost, withdrawn).

Otherwise default-collapsed. Implemented in app/procurement/[id]/session/page.tsx:264-266 (P1-4, S184). Per-question remount via key prop preserves the initial default on question switch.

Action: “Draft All” → POST /api/procurement/[id]/responses/draft-all runs questions sequentially with a 100s safety timeout. Partial failures surface via warningsEnvelope() and the WarningsBanner UI; users can retry individually.

  • Viewer: Cannot draft, edit, redraft, or save. Read-only view of the session — ResponseEditor mounts in readOnly mode.
  • Cmd+S / Ctrl+S keyboard shortcut triggers handleActionWithRecovery('save') when content is non-empty (page-level handler).
  • Stream interruption (network drop, tab close): useDraftRecovery preserves the last partial draft in localStorage; on remount, the DraftRecoveryDialog prompts to restore.
  • Quality-check fails Pass 3: the response is still saved (the gate records issues in form_responses.metadata), and the response surface flags problem areas for manual review.

Journey 3: Maintaining Q&A Library Content (S198 §1.5)

Section titled “Journey 3: Maintaining Q&A Library Content (S198 §1.5)”

Actor: Editor or admin Goal: Update a canonical Q&A pair so future form drafts pull a corrected or improved answer. Preconditions: Q&A pair exists in content_items (content_type = 'question_answer').

The Q&A Library is the upstream surface that feeds form drafting via search_for_form_response. Q&A pair quality is the single most important input to form response quality.

  1. Open the Q&A pair

    • Route: /library (filter, search, list) → click row → /item/[id], or /item/[id] directly.
    • Component: QAPairRenderer (read mode) → QAAnswerDisplay panels.
  2. Edit Standard answer

    • Action: Click “Edit” on the Standard panel.
    • Component: QAAnswerDisplay mounts a single dynamically-imported Tiptap ContentEditor (components/item-detail/content-editor.tsx), pre-populated from item.answer_standard ?? ''. Identified by data-testid="qa-answer-panel-standard".
    • Constraints (S198 §1.5):
      • Single-field-at-a-time: clicking Edit on the Standard panel mounts ONE editor; Save/Cancel unmounts it. Editing the Advanced panel must follow.
      • Save-safety guard: ≥20% shrink relative to the per-field baseline blocks the save (SAVE_SAFETY_BLOCK_MESSAGE toast). Composes inside the active editor’s handleSave.
      • Per-field regen-embedding checkbox: ticking it threads regenerate_embedding: true into the PATCH body. Resets on startEdit / cancelEdit / successful save so the flag never leaks to a sibling field’s save (S198 verifier H1 fix).
  3. Save

    • Endpoint: PATCH /api/items/[id] with field-level payload (answer_standard or answer_advanced).
    • Side effect: rebuilds content_items.content from Q: {question}\n\n{answer_standard}\n\n{answer_advanced}. The if (question) parts.push(...) rebuild (S198 Wave 1 §4.1 H2 fix) correctly omits the Q: prefix when the question is empty.
    • When regenerate_embedding=true: re-embeds the rebuilt content via text-embedding-3-large and refreshes downstream chunks.
  4. Edit Advanced answer

    • Same pattern via data-testid="qa-answer-panel-advanced". Independent baseline + independent regen-embedding flag.
  • Viewer: Read mode only via QAPairRenderer → ContentRenderer. No edit affordances render.
  • Empty question: PATCH omits the Q: prefix, leaving the rebuilt content as {answer_standard}\n\n{answer_advanced}.
  • Save-safety block (≥20% shrink): editor stays mounted; user adjusts content; embedding flag retained across the failed attempt.
  • Race condition (admin browses while another edit is mid-save): optimistic-concurrency does NOT guard answer_* fields directly — the rebuilt content field is the storage contract; concurrent edits to the same field will overwrite without explicit collision detection.

Journey 4: Reviewing & Approving Responses

Section titled “Journey 4: Reviewing & Approving Responses”

Actor: Reviewer (admin) or editor (self-review) Goal: Move drafted responses through review to ready-for-export. Preconditions: All form questions have a form_responses row.

  1. Transition to in_review

    • Action: User clicks “Send to Review” on /procurement/[id] (Overview tab). Triggers handleStatusTransition('in_review') in useFormActions, which calls PATCH /api/procurement/[id] with { status: 'in_review' }.
    • State machine: drafting → in_review (or back from ready_for_export).
  2. Review each response

    • Route: /procurement/[id]/session (citation panel auto-expands for review-or-later states).
    • Reviewer reads each response, verifies citations (useCitationOrphans batch-checks for missing source IDs), and either approves (review_status='approved' on form_responses) or sends back for revision.
  3. Mark ready for export

    • Action: All responses approved + readiness checklist green → user transitions to ready_for_export.
    • State machine: in_review → ready_for_export.
  • Reviewer requests edits: back-transition in_review → drafting preserved per state machine.
  • Orphan citations: useCitationOrphans flags form_response_history.citations.source_id values that no longer resolve to a content_items row (e.g. deleted KB content). Reviewer must re-cite or remove.

Actor: Editor or admin Goal: Construct the final, deliverable document for the buyer. Preconditions: Form in ready_for_export; readiness checklist green.

  1. Trigger export

    • Component: ProcurementExportMenu
    • Actions:
      • “Export to DOCX” → POST /api/procurement/[id]/export/docxmarkdownToDocxParagraphs preserves headings, lists, bold/italic, links, and GFM tables; plain-text fields use stripMarkdown.
      • “Export to XLSX” → POST /api/procurement/[id]/export/xlsxexceljs output with plain-text equivalents.
  2. Template-based export (optional)

    • Route: /procurement/[id]/templates → select template → “Fill” → POST /api/procurement/[id]/templates/[templateId]/fill.
    • Side effect: template_completions row records fields_filled + storage_path.
    • Download: GET /api/procurement/[id]/templates/[templateId]/completions/[completionId]/download.
  3. Submit & record outcome

    • State transition: ready_for_export → submitted.
    • After tender close: ProcurementOutcomeDialog records won / lost / withdrawn via POST /api/procurement/[id]/outcome. Optionally POST /api/procurement/[id]/outcome/integrate integrates outcome learnings into the KB.
  • DOCX rendering edge cases (nested tables, deep list nesting): lib/procurement/procurement-export-docx.ts covers via markdownToDocxParagraphs; tests in __tests__/lib/procurement/procurement-export-docx.test.ts.
  • Template Python write-back path: template completion currently uses the Python python-docx write-back (out-of-band relative to pure Vercel deployments).
ActionViewerEditorAdmin
View forms + responsesYesYesYes
Export form (DOCX/XLSX)YesYesYes
Create / update / delete formNoEditor only for create/update; Admin for deleteYes
Upload tender + extract questionsNoYesYes
KB matchingNoYesYes
Draft / redraft / batch-draftNoYesYes
Save / cite / approve responsesNoYesYes
Edit Q&A Library contentNoYesYes
Edit regenerate_embeddingNoYesYes
Transition form stateNoYes (most transitions); Admin only for deleteYes
Record form outcomeNoYesYes
Cite content via MCPNoYesYes
Citation panel default-expandedPer statePer stateAlways (admin)
  • No real-time collaborationTiptap Collaboration is not wired in. Concurrent editors race via standard REST PATCH saves.
  • localStorage-bound recoveryuseDraftRecovery cannot recover a draft from a different browser or device.
  • No isolated draft scope per user — all editors see all drafts; there is no “my drafts” filter.
  • Q&A field edit is field-by-field, not multi-field — by design (S198 §1.5). Users cannot mount Standard + Advanced editors simultaneously; they must save one before editing the other.
  • Mobile editor flake on the “New button visible for editor” assertion (bid-pipeline.spec.ts:467) — pre-existing, logged as S153-Mobile-1 in product backlog.
  • §1.7 admin dedup review + §1.9 near-duplicate merge dashboard ratified S209 WP2 but not yet implemented — duplicate Q&A pairs may surface as form matches until the dedup UI ships.
  • Technical reference: docs/product-functionality/bid-management/technical.md
  • Workflows: docs/product-functionality/bid-management/workflows.md
  • State of the product (Bid Workflow): docs/reference/state-of-the-product.md §5
  • Schema reference: docs/reference/SCHEMA-QUICK-REFERENCE.md
  • Q&A ContentEditor spec (S198 §1.5): docs/specs/qa-contenteditor-upgrade-spec.md
  • Publication-lifecycle spec (S205-S208 §5.2): docs/specs/publication-lifecycle-state-machine-spec.md
  • §1.7 admin dedup review spec: docs/specs/§1.7-admin-dedup-review-spec.md
  • §1.9 near-duplicate merge dashboard spec: docs/specs/§1.9-near-dup-merge-dashboard-spec.md