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 forthcomingTECH.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 toTECH.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:
/procurementworktree (branchca-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_embeddingsis the single embeddings home), DR-056 (S466 — form-first RECONCILES WITH, does not supersede, the id-71 application-umbrella ontology:application_typessurvives, procurement stays the application umbrella, the umbrella MCP tools are re-keyed not renamed,workspacesis 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, theupload→analyse→review→fill→completetemplate workflow). Warm Meridian semantic tokens govern all colour/spacing/typography (design/warm-meridian-implementation-spec.md).
Summary
Section titled “Summary”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.
Problem
Section titled “Problem”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 aworkspacesrow withdomain_metadataengagement keys +application_type_id, and never a form (verified: noform_templatesinsert 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_responsestotal = 0. - Workspace-derived item facts.
deriveProcurementMetadata(lib/domains/procurement/procurement-detail-shape.ts:131-165; gitnexus context: 2 incoming callers —fetchProcurementinprocurement-context-provider.tsxanduseFormActionsinhooks/procurement/use-procurement-actions.ts; callsgetPrimaryForm+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_recomputehave zero app/lib TypeScript callers (grep verified empty) — the retrieve step of the answering loop is unwired (RESEARCH §5.3).form_template_fieldshas no app-side INSERT/upsert writer (grep verified empty — only readers and the manualfields/bulk-updateeditor remain); id-136 deleted the deterministic extractor, so the shipped fill workflow (app/procurement/[id]/templates/page.tsx:TemplateCompletionPage, stepsupload→analyse→review→fill→complete;fill/route.tsproc_78,auto-map/route.tsproc_62; Path-Ccatalogue/from-instance.ts) and thecatalogue-form-requirementsskill are stranded (RESEARCH §5.5, OQ-5). - Dashboard dead links.
ActiveProcurementsSection(components/dashboard/active-procurement-section.tsx:51-105, called byDashboardContentinapp/page.tsx) reads legacydomain_metadatavialib/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_idquestions. - R4 (OQ-4) — Metadata homes.
reference_number+estimated_valuebecome 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-maproutes and un-strands thecatalogue-form-requirementsskill. PRODUCT states the behaviour; the extraction mechanism (extend app-side Claude extraction to emit field structure / resurrect the git-recoverable deterministic extractor / adoptcommonformsfor 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_typere-vocabulary is a labels-only backlog item. - R7 (OQ-7) — Retrieval wiring. An app route recomputes
question_matcheson question create/update; scope tags derive fromform_type+ tenant config; an MCP reader consumesquestion_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-formsexists 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 / Non-goals
Section titled “Goals / Non-goals”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_typestate 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.
Behaviour
Section titled “Behaviour”A. Form-first item model + identity
Section titled “A. Form-first item model + identity”-
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.statussecond 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 workspacedomain_metadatablob. (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.) -
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.) -
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.)
-
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.)
-
reference_numberandestimated_valueare 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_idssurvives — it has a live reader in the tender-document flow and feeds the item’s documents (BI-16);tender_source,outcome_notes, andnotesare 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_idsresolves to the item’s documents; a dropped key has no renderer and no writer. (R4; OQ-4; ARCH-REVIEW §6 BI-5 reader list.) -
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 form — no surface writes a procurement state to any second location. The ex-workspaces.statussecond 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; noform_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.)
B. Form-first creation
Section titled “B. Form-first creation”-
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 ofPOST /api/procurement(RESEARCH §6 root cause- 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.tsbehaviour.)
- 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
-
Creation offers a FormTypePicker. At creation the user picks (or confirms an inferred, or overrides) the item’s
form_typevia the existing FormTypePicker affordance. “Bid” is not offered as a first-class creation label; the picker presents currentform_typevalues in item/form language. Verifiable: creation surfaces the FormTypePicker; the chosen type is the new item’sform_type. (RESEARCH §2; §4b terminology note.) -
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.)
-
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.)
-
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 ongetAuthorisedClient(['admin', 'editor']); ARCH-REVIEW §6 BI-11.) -
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)”-
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.) -
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.)
-
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.)
-
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.) -
Outcome recording feeds the right metric. Recording an item’s outcome updates win-rate where the
form_typecounts toward win rate, and shortlist pass-rate where theform_typeis a qualification stage — driven by the existingcounts_toward_win_ratedata, 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.) -
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.) -
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)”-
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.)
-
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-mapaction produces a per-field mapping the user can review; it is not a no-op against an empty field set. (RESEARCH §5.5; un-orphansauto-map/route.ts.) -
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-formsexists 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: theupload→analyse→review→fill→completeworkflow 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-orphansfill/route.ts+TemplateCompletionPage.) -
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.)
-
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-requirementsskill, 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.) -
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.) -
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)”-
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.)
-
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.)
-
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.)
F. Dashboard re-wire
Section titled “F. Dashboard re-wire”-
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 workspacedomain_metadata. Verifiable: the active list reflects form facts; a form whose state is terminal is excluded; no item is sourced fromdomain_metadata. (RESEARCH §5.6; replaces thelib/dashboard.tslegacy read.) -
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.) -
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.)
-
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)”-
Question create/update recomputes matches. When a question is created or updated, its
question_matchesare 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.) -
Scope tags derive from
form_type+ tenant config. The scope/anti-scope tags used for matching are derived from the item’sform_typeand tenant configuration — not from a removed workspace identity. Verifiable: an item’s match scope reflects itsform_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.) -
An MCP reader retrieves matches. A Claude-facing MCP reader tool consumes
question_match_searchto return a question’s matches. The tool is form-scoped and returns matches for a given form/question. Verifiable: the MCP reader returns matches viaquestion_match_search; it does not require a workspace id. (R7; MCP re-key per BI-42.)
H. Corpus citation activation
Section titled “H. Corpus citation activation”- The draft writer cites Q&A-corpus sources, and
content_itemvocabulary is fully removed from the procurement/citations slice. (rev. S467) The draft-writing path can citeq_a_pairsources — activating the dormantq_a_paircite path — recording spans and version-on-cite as before. Beyond activating that path, thecontent_itemvocabulary is fully removed from this slice with no lingering references: thecite_contentcontent_item_idargument + its description, theget_content_effectivenessarg mapping, theget_content_win_rate/effectiveness naming,form_questions.matched_record_ids(the content-era array), and the stalecitationstable COMMENT that still advertises the droppedcontent_itemcited-kind are all renamed/removed to theq_a_pairgrain (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-widecontent_itemsoccurrences (~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 namescontent_itemin an arg, description, comment, or column; a draft can carry a citation whose source is aq_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)”-
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/reviewis 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;/reviewremains the distinct content-quality surface. (DR-025/ DR-026; RESEARCH §5.8; ARCH-REVIEW §3 G6, §6 BI-38.) -
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”.)
J. UI-vs-Claude split (R9)
Section titled “J. UI-vs-Claude split (R9)”-
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.)
-
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 oncecompleting-formsexists 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-formsis 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)”- 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_procurementandget_procurement_detailKEEP 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”), thecite_contentcontent_item_idarg (BI-37), andshow_procurement_dashboard’s misleading workspace-takingform_idarg — which are renamed breakingly with no deprecation aliases (R8’s no-alias posture holds for what is renamed).application_typessurvives as the app catalogue;workspacesis destined for full retirement (the intelligence lane is a separate Task) and is not designed as permanent. Verifiable:list_active_procurement/get_procurement_detailretain 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)”-
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_idquestions (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_idquestions; nothing was backfilled. (R3; OQ-3; staging: 109 formless, 119/149 NULL-ftid questions.) -
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.)
-
form_type='bid'defaults are re-classified. The prod forms defaulted toform_type='bid'are re-classified to a realform_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 onform_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)”-
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.) -
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.) -
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.)
Open questions (for {145.3} TECH)
Section titled “Open questions (for {145.3} TECH)”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-questionsto emit structure, resurrect the git-recoverable deterministic id-52 extractors, adopt thecommonformsAPPROACH 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 verifiedcommonformsPRESENT 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 atc84be63b^;langextractis 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+ theform_questionsre-scope + the workspace-removal FKs) before theform_templates → form_instancesrename, so the rename and the api-view regen (DR-030/DR-032) are handled correctly in one migration batch. Confirm theform_template_requirements.requirement_embeddingmigration torecord_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_metadatadrop, 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).
Revision log — S467
Section titled “Revision log — S467”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-3 — DROPPED 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_idsas 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.statushome 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-37 —
content_itemvocabulary 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;/reviewis 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-42 — DR-056 narrowing: umbrella tools
list_active_procurement/get_procurement_detailKEEP names + re-key workspace→form; hard-rename narrowed to genuinely-false vocabulary only;application_typessurvives;workspacesnot 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.