Skip to content

ID-145 PRODUCT — Procurement form-first re-architecture (DR-038)

ID-145 {145.2} PRODUCT — Procurement form-first re-architecture

Section titled “ID-145 {145.2} PRODUCT — Procurement form-first re-architecture”

Type: PRODUCT (behaviour spec). Companion to RESEARCH.md (same directory, read in full) and the forthcoming TECH.md ({145.3}). Defines what the form-first procurement surfaces do, from each consumer’s perspective, as numbered, testable behaviour invariants (BIs) a Checker can verify against acceptance criteria. It does not prescribe migrations, table names, extractor libraries, or route internals — those belong to TECH.md.

Task: ID-145 (procurement form-first re-architecture). Subtask: {145.2}. Authored S463 by a fresh Planner context (distinct from the {145.1} RESEARCH author). UK English throughout (DD/MM/YYYY, “colour”, “organisation”).

Track: /procurement worktree (branch ca-s435-procurement-baseline). Ledger writes on MAIN only.

Grounding rulings (binding, not re-opened here): DR-038 (activity carries its own id; no new *_workspaces), DR-014 (forms enter by manual upload app-side), DR-041 (three-zone IA), DR-025/DR-026 (evidence → promotion gate), DR-034 (content_items-era coverage retired; template-completion + governance retained), DR-036 (record_embeddings is the single embeddings home), DR-056 (S466 — form-first RECONCILES WITH, does not supersede, the id-71 application-umbrella ontology: application_types survives, procurement stays the application umbrella, the umbrella MCP tools are re-keyed not renamed, workspaces is destined for full retirement and not designed as permanent). Owner ruling 2026-07-11: a procurement item IS one form instance, not a workspace container of forms.

Figma: none provided. This is a re-architecture of existing surfaces (three-zone nav chrome already conforms — DR-041); the item page rebuilds on shipped components (ProcurementFormsCard, NextActionCard, ReadinessChecklist, the drafting stack, the upload→analyse→review→fill→complete template workflow). Warm Meridian semantic tokens govern all colour/spacing/typography (design/warm-meridian-implementation-spec.md).


Retire the workspace-umbrella framing from the procurement UX so that a procurement item is one form (a form_type instance carrying its own state, deadline, outcome, questions, coverage and documents), addressable directly, creatable form-first, and able to extract, fill and catalogue a blank form. Re-wire the dashboard, retrieval and MCP surfaces onto the form grain, delete the workspace debris, and keep drafting Claude-side — delivering first-domain production readiness.

The procurement backend is already form-centric (form_templates rows carry workflow_state, outcome + audit triad, deadline, submission_date, issuing_organisation, form_type); the UI is workspace-umbrella + bid-era shaped and broken at runtime. Two structural root causes and their code-intelligence grounding (GitNexus repo:'canonical', verified this session — cite verbatim so TECH and the Executor navigate directly):

  • Born-formless creation. POST /api/procurement (app/api/procurement/route.ts:208-302, gitnexus proc_58 family, cluster [id]) inserts a workspaces row with domain_metadata engagement keys + application_type_id, and never a form (verified: no form_templates insert in the handler; comments read literally “Failed to create bid”). Every new item therefore reads as empty. Staging ground truth (runbook trace, 11/07/2026): 109/498 workspaces are formless; form_responses total = 0.
  • Workspace-derived item facts. deriveProcurementMetadata (lib/domains/procurement/procurement-detail-shape.ts:131-165; gitnexus context: 2 incoming callers — fetchProcurement in procurement-context-provider.tsx and useFormActions in hooks/procurement/use-procurement-actions.ts; calls getPrimaryForm + deriveProcurementStatus) derives the item from the primary child form, so the workspace/form split renders visible — the item page shows “Questions N” and “No forms yet” together (RESEARCH §4b defect 2).
  • Dead retrieval + dead fill writer. question_match_search / question_match_recompute have zero app/lib TypeScript callers (grep verified empty) — the retrieve step of the answering loop is unwired (RESEARCH §5.3). form_template_fields has no app-side INSERT/upsert writer (grep verified empty — only readers and the manual fields/bulk-update editor remain); id-136 deleted the deterministic extractor, so the shipped fill workflow (app/procurement/[id]/templates/page.tsx:TemplateCompletionPage, steps upload→analyse→review→fill→complete; fill/route.ts proc_78, auto-map/route.ts proc_62; Path-C catalogue/from-instance.ts) and the catalogue-form-requirements skill are stranded (RESEARCH §5.5, OQ-5).
  • Dashboard dead links. ActiveProcurementsSection (components/dashboard/active-procurement-section.tsx:51-105, called by DashboardContent in app/page.tsx) reads legacy domain_metadata via lib/dashboard.ts; the “Needs Attention” column links to the retired /bids/<uuid> route family → 404 (RESEARCH §4b defect 1). ContentPerformanceSection (components/dashboard/content-performance-section.tsx:72-288) is wired to the win-rate RPC but starved and bid-labelled.

The fix is re-anchoring + surfacing + three or four net-new builds, not a green-field rebuild. The single highest-leverage fact: forms already carry everything an item page needs; the workspace wrapper adds only the FK.

Ratified decisions (owner, S463 — committed inputs, not re-opened)

Section titled “Ratified decisions (owner, S463 — committed inputs, not re-opened)”

These are fixed inputs to the Behaviour below. The Checker treats them as given. Verbatim source: the S463 OQ ratification board (id145-oq-ratifications-s463.md).

  • R1 (OQ-2) — Routing. /procurement/[id] re-anchors to the form id (breaking the workspace-id link + the MCP id contract); legacy workspace-id links redirect via a primary-form lookup.
  • R2 (OQ-1) — Grouping. A lightweight, optional, nullable engagement grouping links sibling forms — it is not a container.
  • R3 (OQ-3) — Legacy data. Delete formless/debris workspaces wholesale; no backfill of NULL-form_template_id questions.
  • R4 (OQ-4) — Metadata homes. reference_number + estimated_value become first-class form attributes; the rest of the legacy engagement metadata is dropped unless a live reader exists.
  • R5 (OQ-5, REVISED — CRITICAL) — Autofill is IN v1 scope. The item page can fill a blank form with answers. This requires restoring the fillable-field-structure writer (id-136 deleted it), which un-orphans the fill/auto-map routes and un-strands the catalogue-form-requirements skill. PRODUCT states the behaviour; the extraction mechanism (extend app-side Claude extraction to emit field structure / resurrect the git-recoverable deterministic extractor / adopt commonforms for PDFs / a headless-agent approach per ID-71) is a {145.3} TECH decision.
  • R6 (OQ-6) — State vocabulary. Keep the 10 workflow states verbatim for v1; per-form_type re-vocabulary is a labels-only backlog item.
  • R7 (OQ-7) — Retrieval wiring. An app route recomputes question_matches on question create/update; scope tags derive from form_type + tenant config; an MCP reader consumes question_match_search. (Exact wiring = TECH.)
  • R8 (OQ-8) — MCP rename. Hard rename, no aliases — existing Claude callers break immediately.
  • R9 (OQ-9) — Drafting depth. The drafting-session stack stays a secondary surface, re-pointed to the form id; no new authoring UI until completing-forms exists Claude-side.
  • R10 (OQ-10) — Staging hygiene. Purge the ~507 debris workspaces and seed one form-first exemplar for UAT (impl-time).
  • R11 (OQ-11) — Instance rename. Rename the instance table to reflect that it holds form instances, landed in the TECH migration set (schema-only; app not live).
  • R12 (OQ-12) — Error boundaries. RESOLVED: the two boundaries do not reproduce on main (zombie-dev-server transients). Do not scope an error-boundary fix; the generic boundary stays as the correct fallback. The reproducible rot is the model + copy, fixed by this re-architecture.

Goals. Form-first item model + identity; form-first creation (FormTypePicker + DR-014 upload); a working item page (state/deadline/outcome/questions/coverage/documents + fill); v1 autofill (extract → auto-map → fill → catalogue); dashboard re-wire onto the form grain; retrieval + MCP re-key to the form; legacy debris deletion; UK/Warm-Meridian conformance.

Non-goals.

  • Answer drafting UI as the primary authoring surface — drafting is Claude-side (completing-forms, a SIBLING Task). The web drafting stack stays secondary (R9). Do not rebuild “Open Session”/“Questions” as primary authoring UI.
  • The extraction/fill mechanism — TECH decides it (R5). PRODUCT states only the behaviour.
  • Per-form_type state re-vocabulary — backlog (R6).
  • Rebuilding content_items-era coverage — retired (DR-034); only template-completion coverage + governance are retained.
  • Building completing-forms — its own Task; this spec only makes the item model it depends on concrete and flags the dependency.
  • T10 matching internals (scoring/ranking) — the retrieval surface here exposes matches; match scoring is owned elsewhere.
  1. A procurement item IS one form. (rev. S467) Every procurement item is exactly one form instance of a form_type (psq/sq/itt/tender/rfp/questionnaire/checklist/…). Its lifecycle facts split across two distinct, separately-observable axes — both owned by the form, neither read from a workspace: (a) a document-processing status (the upload→analyse→fill pipeline progress of the form artefact) and (b) the procurement workflow state (the 10-state lifecycle machine, BI-6). The remaining facts — deadline, submission_date, outcome (+ recorded-at/by/notes audit triad), issuing_organisation, reference_number, estimated_value — are likewise attributes of that form. The two axes never collapse into one ambiguous “status” (the ex-workspaces.status second home is retired, BI-6). Verifiable: an item page renders processing status and workflow state as two separate signals, and every lifecycle fact is sourced from the form; no fact is read from a workspace domain_metadata blob. (RESEARCH §2; R1/R4; ARCH-REVIEW §5.1 two-axis split, §6 BI-1. The instance-table name is the R11 rename — a TECH migration, not a behaviour contract here.)

  2. The item is addressable by its form id. (rev. S467) /procurement/[id] resolves [id] to the form. The canonical URL of a procurement item is its form’s URL. The form id is stable across the R11 instance-table rename (the primary key is carried over, not re-minted), so item links created during ID-145 development remain valid after the rename lands. Verifiable: a form-id URL minted before the rename still resolves to the same item after it. (R1; OQ-2; ARCH-REVIEW §6 BI-2.)

  3. There is no legacy-workspace redirect; an unknown id is a clean not-found. (rev. S467 — DROP) R3 deletes all procurement workspaces wholesale (BI-43/44; ARCH-REVIEW §5.1 deletion 3), so no workspace-id survives to map and there is nothing to redirect to — and there are no live users or bookmarks (owner latitude, ARCH-REVIEW header). The redirect clause of R1 is therefore overtaken by R3: a visit to an unknown or retired id resolves to the standard not-found surface (404/410), not a primary-form lookup and not a container render. No workspace→form mapping snapshot is taken or retained. Verifiable: an unknown/retired id returns the standard not-found surface; no redirect machinery and no pre-delete mapping table exists in the shipped scope. (R3 supersedes R1’s redirect clause; ARCH-REVIEW §6 BI-3, §7 decision 5.)

  4. No workspace-container surface exists. There is no “forms sub-collection” card, no “N forms in this workspace” roll-up, and no screen on which an item shows a question count and “No forms yet” simultaneously. The workspace/form split is not user-visible anywhere. Verifiable: the RESEARCH §4b defect 2 shape (“Questions 4” + “No forms yet” together) cannot be produced on any item surface. (RESEARCH §4b; DR-038.)

  5. reference_number and estimated_value are first-class form attributes. (rev. S467) They are displayed on the item and editable by an authorised user. Of the other legacy engagement-metadata keys, tender_document_ids survives — it has a live reader in the tender-document flow and feeds the item’s documents (BI-16); tender_source, outcome_notes, and notes are dropped unless a live reader is found, rather than carried as dead data. Verifiable: reference number + estimated value round-trip on the item; tender_document_ids resolves to the item’s documents; a dropped key has no renderer and no writer. (R4; OQ-4; ARCH-REVIEW §6 BI-5 reader list.)

  6. The 10 workflow states are kept verbatim, in exactly one home. (rev. S467) The per-form workflow machine exposes exactly the 10 existing procurement states for v1; state labels are not re-vocabularised per form_type. An item is always in exactly one valid state, and that state has exactly one home on the formno surface writes a procurement state to any second location. The ex-workspaces.status second state home (previously written by the extract/match routes and never reconciled with the form’s state) is retired, so a workflow state stored in two places that silently diverge is impossible. Verifiable: the state set on any item equals the shipped 10-state set; no form_type-specific relabelling appears; no route writes item state to a workspace-side column. (R6; OQ-6; ARCH-REVIEW §2 C2b, §5.1 deletion 5, §6 BI-6.)

  1. Creating an item creates a form; nothing is born formless — and no question exists without a form. (rev. S467) The create action produces a form (with a form_type), not a bare workspace. There is no code path by which a new procurement item exists without a form. Symmetrically, every question belongs to exactly one form — the workspace-keyed, form-nullable question anchoring that produced the §4b “Questions N + No forms yet” defect is retired, so a question orphaned from a form is unrepresentable by construction (not merely repaired after the fact by a resolve/mint trigger stack). *Verifiable: after create, the new item has a form; the born-formless outcome of POST /api/procurement (RESEARCH §6 root cause

    1. no longer occurs; no question can be created or left without a form.* (RESEARCH §5.2; ARCH-REVIEW §2 C2d/C3, §6 BI-7; supersedes the current route.ts behaviour.)
  2. Creation offers a FormTypePicker. At creation the user picks (or confirms an inferred, or overrides) the item’s form_type via the existing FormTypePicker affordance. “Bid” is not offered as a first-class creation label; the picker presents current form_type values in item/form language. Verifiable: creation surfaces the FormTypePicker; the chosen type is the new item’s form_type. (RESEARCH §2; §4b terminology note.)

  3. Manual upload creates the item from a form document (DR-014). A user can create a procurement item by uploading a form document; the uploaded document is that item’s form. The upload reuses the hardened upload path (magic-byte sniffing, 50 MB cap, rate limiting) — “upload a form → it becomes the procurement item” is a first-class entry point, not tender-document ingestion nested under a container. Verifiable: uploading a supported form document yields one item whose form is the uploaded document. (DR-014; RESEARCH §5.1, §4b nuance.)

  4. Upload extracts the form’s questions into the item. On upload the form’s answerable questions are extracted (app-side Claude extraction) and attached to the item, so the item immediately shows its questions. Verifiable: after uploading a question-bearing form, the item’s questions list is populated and each question is attached to that form. (RESEARCH §2 extraction path; see BI-21 for the fillable-structure counterpart.)

  5. Creation is gated to admin/editor. (rev. S467 — reviewed, unchanged) Only admin/editor roles may create a procurement item or upload a form; unauthorised callers receive the standard authorisation-failure routing (authFailureResponse), never a partial create. The existing gates carry over intact — no gate change is in scope. Verifiable: a viewer-role caller cannot create an item or upload a form. (Existing gate on getAuthorisedClient(['admin', 'editor']); ARCH-REVIEW §6 BI-11.)

  6. Creation copy is item/form language. The creation surface uses procurement-item/form wording, not “bid workspace”. The wizard subtitle, filter tabs, and picker options carry no bid-era or workspace-container language. Verifiable: the creation wizard subtitle is not “Set up a new bid workspace…”; no surface labels the item a “bid workspace”. (RESEARCH §3 Unit-F copy; §4b.)

C. The item page (state, deadline, outcome, questions, coverage, documents)

Section titled “C. The item page (state, deadline, outcome, questions, coverage, documents)”
  1. The item page shows lifecycle state. The item renders its current workflow state as a stepper/badge over the 10-state machine, plus deadline, submission_date, issuing_organisation, and outcome (with recorded-at/by and notes when present). State, outcome, and deadline are never colour-only — each carries a text label or icon meaning (WCAG 2.1 AA). Verifiable: the item renders state/deadline/outcome from the form; removing colour still conveys state. (RESEARCH §2; Warm Meridian.)

  2. The item page shows its questions. The item lists the form’s questions with per-question status. Question presence is driven by the form, so an item with questions never also reads as “no form”. Verifiable: an item with N questions shows N questions and never “No forms yet”. (RESEARCH §4b defect 2; the workspace-keyed question read is re-scoped to the form — the ex-bl-435 prerequisite.)

  3. The item page shows coverage/readiness. The item renders template-completion coverage and a readiness view over its questions (the retained DR-034 signal), at the calibrated thresholds. No content_items-era coverage matrix is rebuilt. Verifiable: the item shows a coverage/readiness figure derived from template completion, not from a retired coverage RPC. (DR-034; RESEARCH §2 coverage engine.)

  4. The item page shows documents; an uploaded form carries its own file identity. (rev. S467) The item lists its documents — the uploaded form and any associated tender documents (the surviving tender_document_ids, BI-5) — with the ability for an authorised user to add a document. For an uploaded item, the form is its file (filename, stored artefact, mime): the docless-mint pattern that left 389 forms with no file (ARCH-REVIEW §1.1 C5) must be impossible — a form minted without a file exists only for the manual-create-then-upload path and is honestly labelled as awaiting its document, never presented as a complete uploaded form. Verifiable: an uploaded form appears in the item’s documents with its file identity; a docless item is only the pre-upload manual-create case and is labelled as such; adding a document is admin/editor-gated. (RESEARCH §2 EP8 import mechanics; ARCH-REVIEW §6 BI-16.)

  5. Outcome recording feeds the right metric. Recording an item’s outcome updates win-rate where the form_type counts toward win rate, and shortlist pass-rate where the form_type is a qualification stage — driven by the existing counts_toward_win_rate data, not a hard-coded list. Verifiable: recording a “won”/“lost” outcome on an ITT moves win-rate; recording a PSQ shortlist result moves shortlist pass-rate. (RESEARCH §2 outcome engine.)

  6. State transitions respect the workflow machine. An authorised user advances the item through valid transitions only; an invalid transition is rejected with a surfaced reason. Verifiable: a disallowed state jump is refused, not silently applied. (RESEARCH §2 workflow machine; single-source PROCUREMENT_WORKFLOW_STATES.)

  7. Empty, loading, and error states render inline and gracefully. A new/empty item, a slow load, and a failed fetch each render an inline state (empty card, spinner, soft-error with retry) — not the generic segment error boundary as a primary path. The generic boundary remains only as the last-resort fallback. Verifiable: the four data shapes (healthy, questions-no-form-legacy, formless, zero-response) each render a clean inline state with no error-boundary trip. (R12; OQ-12 runtime trace.)

D. Autofill / form-fill (R5 — CRITICAL, v1 scope)

Section titled “D. Autofill / form-fill (R5 — CRITICAL, v1 scope)”
  1. Uploading a form yields both its questions and its fillable field structure. Beyond the answerable questions (BI-10), an uploaded blank form has its fillable field structure extracted — the per-field slots that a completed form must populate — so downstream auto-map and fill have structure to operate on. The extraction mechanism is a TECH decision (R5); the behaviour is that field structure exists after upload. Verifiable: after uploading a blank form, the item exposes fillable field structure (not only a question list). (R5; RESEARCH §5.5; the writer id-136 deleted is restored.)

  2. Auto-map maps the form’s fields to answers. Once field structure exists, an authorised user can auto-map the form’s fields to candidate answers from the corpus, reviewing and adjusting the mapping. Verifiable: the auto-map action produces a per-field mapping the user can review; it is not a no-op against an empty field set. (RESEARCH §5.5; un-orphans auto-map/route.ts.)

  3. Fill produces a completed form document; partial fill is a first-class success. (rev. S467) From an accepted mapping, the user can fill the form — producing a completed document artefact from the mapped answers, with progress and a completion summary. A fill that leaves gaps is a VALID, successful completion — never a failure: unfilled slots are recorded as gaps (skipped/pending), the completion is marked done-with-gaps rather than failed, and a genuine failure status is reserved for engine/IO errors only. Gaps remain fillable later — a subsequent fill pass re-processes only the outstanding gaps (fill is re-entrant, not one-shot), so an item can be completed incrementally across sessions. Each gap offers the create-content loop: the user (or Claude, once completing-forms exists Claude-side) drafts the missing answer, which can be promoted to the corpus (manually_authored/derived_from_form_response) and then re-matched and re-filled. Verifiable: the upload→analyse→review→fill→complete workflow runs end to end and yields a completion; a completion with unfilled slots is reported as a success-with-gaps, its gaps survive as fillable, and a re-fill pass closes a previously-gapped slot without redoing filled ones. (RESEARCH §5.5; ARCH-REVIEW §3 G7, §4.3 R-D, §6 BI-22 — the enums already model gaps, so this mandates the behaviour, not new vocabulary; un-orphans fill/route.ts + TemplateCompletionPage.)

  4. The fill workflow is anchored to the form id. The fill/auto-map/complete surfaces live on (or reachable from) the form-first item at its form id — not under a workspace-container templates route. Verifiable: the fill workflow is reached from the item’s form id, and its reads/writes key on the form. (R1; RESEARCH §5.5.)

  5. Cataloguing (Path C) is un-stranded. An admin/editor can promote a form’s extracted fields into the reusable requirement catalogue, only via an explicit human-confirmed action (never auto-written by upload). This un-strands the catalogue-form-requirements skill, which was blocked while no field writer existed. Verifiable: catalogue requirement rows appear only after the human-confirmed cataloguing action, and cataloguing is available again for newly uploaded forms. (RESEARCH §2 Path-C, §5.5; id-52 Inv-21 lineage.)

  6. Autofill is AI-invisible infrastructure. Extracted questions, fillable structure, and mapped answers appear to consumers as ordinary structured records — the platform behaves as though the structure was always there, not as a user-facing “AI extraction” feature. Verifiable: the item surfaces questions/fields/mappings as ordinary data, with no “AI extraction” framing. (reference/ai-visibility-policy.md; id-52 Inv-26 lineage.)

  7. Fill, auto-map, and catalogue are admin/editor-gated. Each mutating action requires admin/editor; unauthorised callers receive standard auth-failure routing. Verifiable: a viewer cannot fill, auto-map, or catalogue. (Existing route gates.)

E. Engagement grouping (R2 — optional, not a container)

Section titled “E. Engagement grouping (R2 — optional, not a container)”
  1. Sibling forms may be optionally grouped. A user can link related forms into a lightweight engagement grouping (e.g. a PSQ, its ITT, and the resulting tender for one opportunity). The grouping is nullable and optional — a form need not belong to any group, and belongs to at most one. Verifiable: an ungrouped form is fully functional; a form can be linked to and unlinked from a group. (R2; OQ-1.)

  2. Grouping is a link, never a container. A group does not own its forms, does not scope their data, and is not a page an item lives “inside”. Each grouped form remains an independent item at its own form id with its own state/outcome. Removing a form from a group changes nothing about the form except the link. Verifiable: a grouped form’s URL, state, questions, and outcome are identical whether or not it is grouped. (R2; DR-038 — no workspace-style container.)

  3. Grouping surfaces opportunity lineage. Where forms are grouped, the item shows the lineage (which sibling forms exist and their states) as read-only context, so a user can navigate PSQ → ITT → tender. Open question (→ TECH): whether a shortlist-failure on an early grouped stage rolls up to mark later sibling stages “lost”, and how the win-rate denominator treats grouped vs standalone forms. Verifiable: a grouped item links to its siblings; the roll-up semantics are deferred to TECH. (RESEARCH OQ-1; win-rate denominator question.)

  1. The item list reads from forms. The dashboard’s active-items list is derived from forms (non-terminal workflow_state, deadline, issuing_organisation), not from legacy workspace domain_metadata. Verifiable: the active list reflects form facts; a form whose state is terminal is excluded; no item is sourced from domain_metadata. (RESEARCH §5.6; replaces the lib/dashboard.ts legacy read.)

  2. Every dashboard item link targets the form item route. All item links — including the “Needs Attention” column — point at /procurement/[formId]. There are no /bids/<uuid> links. Verifiable: no dashboard link resolves to the retired /bids/<uuid> family; the RESEARCH §4b defect 1 (324 dead cards, 280 critical) is eliminated. (RESEARCH §4b defect 1.)

  3. Content Performance renders win-rate + shortlist pass-rate. The Content Performance surface is wired to the win-rate and shortlist pass-rate RPCs and renders the shortlist columns (which currently have no renderer). Where data is sparse, it shows an honest empty state rather than a mislabelled zero. Verifiable: Content Performance shows win-rate and shortlist pass-rate; the shortlist columns render. (RESEARCH §5.6.)

  4. Dashboard terminology is item/form language and internally consistent. Every dashboard surface uses “procurement”/“form” language; a single screen never mixes “Active Bids” (heading) with “Active procurements” (aria-label/tile). Verifiable: no dashboard surface reads “Active Bids”; heading, aria-label, and tile agree. (RESEARCH §4b terminology split; Unit-F copy.)

G. Retrieval (question_matches) wiring (R7)

Section titled “G. Retrieval (question_matches) wiring (R7)”
  1. Question create/update recomputes matches. When a question is created or updated, its question_matches are recomputed by an app route, so the retrieve step of the answering loop has fresh matches. Verifiable: creating/updating a question yields recomputed matches for it; the two-RPC contract is no longer caller-less (RESEARCH §5.3). (R7; OQ-7.)

  2. Scope tags derive from form_type + tenant config. The scope/anti-scope tags used for matching are derived from the item’s form_type and tenant configuration — not from a removed workspace identity. Verifiable: an item’s match scope reflects its form_type + tenant config, with no workspace_id dependency. (R7; OQ-7. Carry-forward to TECH: the concrete scope-tag source/config shape — mempalace surfaced no prior “scope tags” ref this session; TECH confirms.)

  3. An MCP reader retrieves matches. A Claude-facing MCP reader tool consumes question_match_search to return a question’s matches. The tool is form-scoped and returns matches for a given form/question. Verifiable: the MCP reader returns matches via question_match_search; it does not require a workspace id. (R7; MCP re-key per BI-42.)

  1. The draft writer cites Q&A-corpus sources, and content_item vocabulary is fully removed from the procurement/citations slice. (rev. S467) The draft-writing path can cite q_a_pair sources — activating the dormant q_a_pair cite path — recording spans and version-on-cite as before. Beyond activating that path, the content_item vocabulary is fully removed from this slice with no lingering references: the cite_content content_item_id argument + its description, the get_content_effectiveness arg mapping, the get_content_win_rate/effectiveness naming, form_questions.matched_record_ids (the content-era array), and the stale citations table COMMENT that still advertises the dropped content_item cited-kind are all renamed/removed to the q_a_pair grain (breaking arg renames are permitted per R8’s no-aliases posture). The underlying table + cited-kind were already dropped (M6, verified live: no .from('content_items') query survives); this is the contract/vocabulary sweep that finishes it, not a data migration. Scope boundary: BI-37 owns only the procurement + citations slice; the remaining repo-wide content_items occurrences (~495 across ~126 non-test files, dominated by certifications/dashboard/guides/cron surfaces outside ID-145) are routed to the Curator as a tracked platform-wide vocabulary-retirement item, not done here. Verifiable: no procurement or citations surface names content_item in an arg, description, comment, or column; a draft can carry a citation whose source is a q_a_pair; the out-of-slice occurrences are logged for the Curator, not silently pulled into scope. (RESEARCH §5.4; ARCH-REVIEW §2 C10, §5.1, §6 BI-37; id-58 citations verified on prod.)

I. Promotion-gate UI (DR-025/026, right-sized)

Section titled “I. Promotion-gate UI (DR-025/026, right-sized)”
  1. A thin promotion-gate UI exists in Governance, composed from existing backend. (rev. S467) The corpus-flywheel promotion gate has a deliberate, right-sized UI (Governance zone per DR-041): an authorised user reviews promotion candidates and accepts/edits/rejects them, and resolves the dedup-proposal queue. It is thin — a review/disposition queue, not a full editor — and it composes surfaces that already exist: the finished q_a_extractions_promotion_candidates() RPC ({138.17}; 3-branch — new candidates ∪ mid-promotion self-heal ∪ text-drift re-propose), the existing promote route (app/api/q-a-pairs/promote/route.ts), and the existing dedup-proposals queue. No new promotion backend is built for this gate. The content-quality lane at /review is a SEPARATE workflow and is unchanged — it is not the promotion gate and is not folded in. Verifiable: promotion candidates and dedup proposals are reviewable and dispositionable in a Governance-zone surface driven by the existing RPC + promote route; no new promotion backend endpoint is introduced; /review remains the distinct content-quality surface. (DR-025/ DR-026; RESEARCH §5.8; ARCH-REVIEW §3 G6, §6 BI-38.)

  2. Promotion writes are human-gated at launch. Records join the promoted corpus only via a human-confirmed promotion; there is no auto-apply at launch (auto-apply is earned per progressive trust). Verifiable: no record is promoted without a human confirmation. (DR-026; platform-direction “observe and intervene”.)

  1. The web UI owns govern/curate/visualise + form entry + review/approve + fill — including part-drafted forms and manual answering. (rev. S467) The web surface provides: the form-first item page, DR-014 upload/form entry, coverage/outcome visualisation, the review/approve (promotion) gate, and the fill surface. It does not provide primary answer-authoring, but it must handle two states honestly: (i) part-drafted forms — an item with a mix of drafted, approved, matched, and empty per-question states renders that mix truthfully (no all-or-nothing framing), so a partially-progressed form is a first-class view; and (ii) questions the corpus cannot answer — a question with zero match candidates surfaces a manual-answer affordance so a user can answer it directly, with the option to promote that answer into the corpus (closing the loop with BI-22’s gap flow and BI-24 cataloguing). Verifiable: the web UI exposes these surfaces and no primary drafting/authoring experience; an item with mixed per-question states renders each state honestly; a zero-candidate question offers manual answer → optional corpus promotion. (RESEARCH §6; R9; ARCH-REVIEW §3 G10, §6 BI-40.)

  2. The drafting-session stack stays secondary and form-anchored. (rev. S467 — reviewed, unchanged) The existing drafting session (SSE draft-stream, response editor, question navigator, citation panel) remains available as a secondary surface, re-pointed to the form id — a trivial re-point, since the stack already keys on the route [id] alone (ARCH-REVIEW §6 BI-41). It is not rebuilt or expanded into a primary authoring UI, and is thinned toward review/approve once completing-forms exists Claude-side. Verifiable: the drafting stack loads at the form id and is presented as secondary, not as the item’s primary action. (R9; OQ-9. Retrieval + drafting are Claude-side; completing-forms is a SIBLING Task and is the named dependency — it depends on this spec’s form-first id contract.)

K. MCP surface (R8 narrowed by DR-056 — re-key + selective rename)

Section titled “K. MCP surface (R8 narrowed by DR-056 — re-key + selective rename)”
  1. MCP procurement tools are re-keyed to the form; only genuinely-false vocabulary is renamed. (rev. S467 — DR-056) Per DR-056 (form-first reconciles with, does not supersede, the id-71 application-umbrella ontology), the umbrella tools list_active_procurement and get_procurement_detail KEEP their names (id-71 OQ-4 ratified them as the procurement-application enumerate/inspect surface) and have their id argument re-keyed workspace→form, so reads are form-scoped, not workspace-keyed. The R8/OQ-8 “hard rename” is narrowed to genuinely-false vocabulary only — the bid-era app bundle copy (“Bid Dashboard”/“Active bids”), the cite_content content_item_id arg (BI-37), and show_procurement_dashboard’s misleading workspace-taking form_id arg — which are renamed breakingly with no deprecation aliases (R8’s no-alias posture holds for what is renamed). application_types survives as the app catalogue; workspaces is destined for full retirement (the intelligence lane is a separate Task) and is not designed as permanent. Verifiable: list_active_procurement/get_procurement_detail retain their names and return form-scoped data; the false-vocabulary names/args are gone with no alias; the id-71 and ID-145 registers agree (DR-056 records the cross-reference). (DR-056; R8 narrowed; OQ-8; RESEARCH §3, §5.7; ARCH-REVIEW §2 C12, §6 BI-42.)

L. Legacy data policy + migration outcome (R3, R10, R11)

Section titled “L. Legacy data policy + migration outcome (R3, R10, R11)”
  1. Formless/debris workspaces are deleted wholesale; there is no NULL-FK backfill. The migration deletes formless and debris workspaces rather than minting forms for them, and does not backfill the NULL-form_template_id questions (they are purged with their debris). No “unassigned” sentinel is created. Verifiable: post-migration there are no formless procurement items and no orphaned NULL-form_template_id questions; nothing was backfilled. (R3; OQ-3; staging: 109 formless, 119/149 NULL-ftid questions.)

  2. Staging debris is purged and one exemplar is seeded. The ~507 debris workspaces are purged and exactly one form-first exemplar item is seeded for UAT/demo (impl-time), so the surfaces can be verified against a clean, representative item. Verifiable: post-purge the procurement list is free of duplicate-name E2E debris and contains a usable form-first exemplar. (R10; OQ-10. Carry-forward to TECH/impl: owner-provided real forms may seed the exemplar — mempalace surfaced none this session; confirm at impl time.)

  3. form_type='bid' defaults are re-classified. The prod forms defaulted to form_type='bid' are re-classified to a real form_type (or handled per an explicit policy), so no live item carries the bid-era default type as its meaning. Verifiable: no live item silently relies on form_type='bid' as its classification. (RESEARCH OQ-3; §4b “Bid” picker option.)

M. Cross-cutting invariants (must not regress)

Section titled “M. Cross-cutting invariants (must not regress)”
  1. UK English + Warm Meridian throughout. All new/changed copy is UK English (DD/MM/YYYY, “colour”, “organisation”); all colour/spacing/typography uses Warm Meridian semantic tokens (never raw Tailwind colours); no meaning is conveyed by colour alone (WCAG 2.1 AA). Verifiable: changed surfaces use semantic tokens and carry non-colour signalling. (Project-wide; design/warm-meridian-implementation-spec.md.)

  2. Role coupling holds. Read surfaces (item view, dashboard) are visible per existing role rules; all mutations (create, upload, transition, outcome, fill, auto-map, catalogue, promotion) are admin/editor-gated via authFailureResponse. Verifiable: a viewer sees read surfaces but cannot mutate; an unauthenticated caller is routed to login/auth failure. (Existing auth pattern.)

  3. Three-zone IA and shell are unchanged. New item/creation/fill routes slot into the existing DR-041 three-zone nav (Applications | Knowledge | Governance); the shell chrome, Knowledge surfaces, and command palette are not restructured by this work. Verifiable: navigation membership/labels move in lockstep across the three nav surfaces if touched, and the shell is otherwise unchanged. (DR-041.)

Collected TECH-facing questions; the R1–R12 ratifications above are not re-opened.

  • OQ-5 extraction mechanism (from R5). Which mechanism produces the fillable field structure — extend the app-side Claude extract-questions to emit structure, resurrect the git-recoverable deterministic id-52 extractors, adopt the commonforms APPROACH for PDFs, or a headless-agent approach per id-71 — is a {145.3} TECH decision. The measured substrate already exists and is not to be re-derived: FORM-EXTRACTION-SPIKE.md + ARCH-REVIEW.md §4 carry the empirical import-and-call results — §4.1 verified commonforms PRESENT and functional (198 fields on the owner’s real 57-page Standard SQ in 36 s CPU) but code-licence-blocked, so the review’s default (§4.3 R-B) is an in-repo FFDetr-weights wrapper; the deterministic id-52 readers are verified cleanly git-recoverable at c84be63b^; langextract is deferred. ARCH-REVIEW §4.3 R-A..R-E is TECH’s default position. Any external library TECH ultimately cites still needs a pre-ratification empirical import-and-call check per the shared empirical-verification discipline before it is cited. Reconcile with id-52’s intended blank-form extraction behaviour (id-52 PRODUCT Inv-6..17).
  • OQ-11 schema audit (from R11). Audit the full form-related schema (instance table + fields + requirements + form_types + the form_questions re-scope + the workspace-removal FKs) before the form_templates → form_instances rename, so the rename and the api-view regen (DR-030/DR-032) are handled correctly in one migration batch. Confirm the form_template_requirements.requirement_embedding migration to record_embeddings (DR-036) is carried or already landed.
  • OQ-1 roll-up semantics (from BI-29). Shortlist-failure ⇒ lost roll-up across grouped sibling forms, and the win-rate denominator treatment of grouped vs standalone forms.
  • OQ-7 scope-tag source (from BI-35). The concrete form_type + tenant-config shape that yields scope/anti-scope tags, and where recompute is triggered (route vs pipeline step).
  • Route re-anchor (from BI-2). The breaking route + MCP id-contract change (workspace→form id), sequenced with the R11 rename so dev-minted form-id links survive it (BI-2). The legacy redirect is dropped (BI-3 rev. S467 — R3 deletes all procurement workspaces, so nothing is left to redirect to), so no primary-form-lookup and no pre-delete mapping snapshot is in scope.
  • Migration sequencing. The staging-first migration batch (bl-435 question re-scope + UNIQUE re-scope, workspace retirement, domain_metadata drop, route/MCP re-anchor, copy sweep, debris purge) sequenced before the ID-45 client re-ingest cements the model — and the {128.10} golden snapshot must not freeze legacy seed shapes (RESEARCH §8 sequencing).

Fresh-Planner revision pass ({145.2} REVISION, not rewrite). Folded ARCH-REVIEW.md §5/§6, the S466 handoff inputs (HANDOFF-S466-INPUTS.md), and DR-056 into the 14 owner-flagged amended BIs + 5 annotations. The 34 accepted BIs are untouched except where DR-056 narrows the MCP rename (BI-42). Numbering is stable; no BI cut. One line per BI touched — the input that forced it:

  • BI-1 — added the two-axis lifecycle split (document-processing status vs procurement workflow state, both form-owned); disambiguates which axis carries state. (ARCH-REVIEW §6 BI-1 / §5.1.)
  • BI-2 — added form-id stability across the R11 instance-table rename (PK carried over, links survive). (ARCH-REVIEW §6 BI-2.)
  • BI-3DROPPED the legacy-workspace redirect entirely; unknown id → clean not-found; R3’s wholesale delete overtakes R1’s redirect clause; no mapping snapshot. (Binding revision input; ARCH-REVIEW §6 BI-3 / §7 decision 5.)
  • BI-5 — named tender_document_ids as the one surviving legacy key (live reader → documents, BI-16); the other three die unless a reader exists. (ARCH-REVIEW §6 BI-5.)
  • BI-6 — added single-state-home consolidation (no route writes a second procurement-state column; the ex-workspaces.status home is retired). (ARCH-REVIEW §2 C2b / §6 BI-6.)
  • BI-7 — added the inverse anchor (no question exists without a form; the §4b defect is unrepresentable by construction, not repair-after-the-fact). (ARCH-REVIEW §2 C3 / §6 BI-7.)
  • BI-11 — reviewed, unchanged: existing admin/editor gates carry over, no gate change in scope. (ARCH-REVIEW §6 BI-11.)
  • BI-16 — added uploaded-form file identity; docless-mint impossible for uploaded items; minted-without-file only for manual-create-then-upload, honestly labelled. (ARCH-REVIEW §1.1 C5 / §6 BI-16.)
  • BI-22 — partial fill is a first-class SUCCESS (gaps valid, never failure); gaps remain fillable across re-entrant passes; each gap offers the create-content loop. (Annotation; ARCH-REVIEW §3 G7 / §4.3 R-D.)
  • BI-37content_item vocabulary FULLY removed from the procurement/citations slice (args, descriptions, comments, matched_record_ids, table COMMENT), not merely adding the q_a_pair path; repo-wide remainder (~495 occurrences / ~126 files) routed to the Curator. (Annotation; ARCH-REVIEW §2 C10 / §6 BI-37.)
  • BI-38 — composes the existing q_a_extractions_promotion_candidates() RPC + promote route + dedup queue; no new backend; /review is a separate content-quality lane, unchanged. (Annotation; ARCH-REVIEW §3 G6 / §6 BI-38.)
  • BI-40 — web UI must handle part-drafted forms (honest mixed per-question states) and manual answering of zero-candidate questions (→ optional corpus promotion). (Annotation; ARCH-REVIEW §3 G10 / §6 BI-40.)
  • BI-41 — reviewed, unchanged: drafting stack re-points to the form id trivially (keys on route [id] only). (ARCH-REVIEW §6 BI-41.)
  • BI-42DR-056 narrowing: umbrella tools list_active_procurement / get_procurement_detail KEEP names + re-key workspace→form; hard-rename narrowed to genuinely-false vocabulary only; application_types survives; workspaces not permanent. (Binding revision input DR-056; ARCH-REVIEW §2 C12 / §6 BI-42.)
  • Header + Open Questions (scaffolding, not BIs) — added DR-056 to the grounding rulings; OQ-5 re-pointed to FORM-EXTRACTION-SPIKE.md + ARCH-REVIEW §4 as the measured substrate (empirical checks already run, not to be re-derived); the BI-2/3 route OQ updated to drop the redirect.

Interactions recorded, not silently resolved:

  • R1 (ratified: redirect legacy links) vs R3 (ratified: delete ALL procurement workspaces). R3 empties the redirect target, so BI-3 encodes the drop and notes R1’s redirect clause as overtaken. This is directed by the S466 handoff input + ARCH-REVIEW §6/§7, so it is encoded, not left open — flagged here for transparency because it touches a ratified R-decision.
  • BI-19 (accepted, not in the amend set) enumerates “legacy” data shapes (questions-no-form-legacy, formless) that become unrepresentable after BI-7’s construction guarantee + R3’s wholesale delete. BI-19 is left intact as defensive last-resort rendering (no binding input touches it; per the brief the accepted BIs stay untouched absent a DR-056 narrowing) — flagged for the Checker as a soft consistency note, not changed.

No blocking unreconciled conflicts.


End of PRODUCT — ID-145.2. Output: specs/id-145-procurement-form-first/PRODUCT.md. Not committed here (the Orchestrator commits after the Checker gate). Subtask status unchanged (the Checker sets it). TECH ({145.3}) reads this in full and maps one Proposed change per numbered BI.