Skip to content

ID-130 {130.1} RESEARCH — Procurement / Form Domain Model + IA

⚠️ PARTIALLY SUPERSEDED (S462, 2026-07-11) — DR-038 + ID-145. The C/Hybrid workspace-umbrella half of this ratified model (PRODUCT B-1, B-3, B-7, B-19, B-22, B-23; TECH AD-2, T-B23 — procurement = a workspaces umbrella holding many forms, procurement_workspaces roll-up) is superseded by DR-038 (S452) + the owner ruling of 2026-07-11: a procurement item IS one form instance. The per-form half (PRODUCT B-2, B-4..B-6, B-8..B-16, B-20, B-21; TECH AD-1, AD-3, AD-4) remains the canonical keeper machinery. Rework owner: ID-145specs/id-145-procurement-form-first/RESEARCH.md. Do not cite the umbrella invariants in new specs.

RESEARCH — Procurement / Form Domain Model + Information Architecture

Section titled “RESEARCH — Procurement / Form Domain Model + Information Architecture”

Status: RATIFIED — 25/06/2026 (S411). Liam ratified D1–D8 via the Lavish ratification gate (.lavish/id-130-ratification.html), accepting all recommended calls. RESEARCH is the load-bearing gate of the ID-130 spec chain ({130.1} RESEARCH ✅ → {130.2} PRODUCT → {130.3} TECH → {130.4} PLAN). The ratified decisions are locked in §6.0; {130.2} PRODUCT is authored against them.

Owning theme: Roadmap theme 2 — Procurement (bid drafting), the platform’s proof-of-value capability.

Authored: S411 (24/06/2026), from a 17-agent grounding workflow (15 source digests + synthesis + adversarial completeness critic) plus live Platform-DB verification (zjqbrdctesqvouboziae). Provenance + per-source digests in the Appendix.

Reading order: §1 (the question) → §2 (verified ground truth) → §4 (the three candidate resolutions + recommendation) → §6 (your decisions). §3/§5 are the evidence base; §7–§9 are scope, timing, and the handoff to PRODUCT.


The ID-61 program renamed “bid” → “procurement” across the codebase. It was mechanically clean, but it exposed a confusion that was always present: the platform cannot distinguish “a procurement” (the umbrella project a client runs) from “a tender/form” (the individual document that has its own questions, deadline, and win/lose outcome). Today, in the live product, a procurement IS a workspace and one workspace = one tender — yet the ratified ontology and every piece of ingestion/AI infrastructure shipped in the last two months model the form as the unit. ID-130 must pick one altitude and make schema + UI + ingestion agree before the first-client re-ingest (ID-45) pours real content into whichever model is live. Pre-launch is the cheapest moment to fix this.


What IS “a procurement”, and where do buyer, deadline, submission_date, outcome, and the 10-state workflow live — on the WORKSPACE (umbrella container) or on the FORM (the individual tender/questionnaire artifact)?

The tension in one sentence: the ratified S391 glossary says one workspace hosts many forms, each with its own buyer/deadline/outcome — but the live schema and UI store ONE buyer/deadline/outcome/ workflow-state per workspace, so today a workspace can only physically represent ONE tender.

This is not a naming question (ID-61 settled naming). It is an altitude question: at which entity do the per-engagement facts and the lifecycle live. Everything else in the Task — UI/IA, the migration, the copy work (ID-61 Unit F), the form-type picker — is downstream of this one decision.


2.1 The ratified ontology (north-star — platform-direction.md, S391 glossary)

Section titled “2.1 The ratified ontology (north-star — platform-direction.md, S391 glossary)”
  • tenant / client = ONE Supabase DB per client. The DB is the only cross-tenant boundary; there is no tenant_id column.
  • application_type = a use-case CLASS (reference table application_types, 6 keys: procurement, intelligence, sales_proposal, product_guide, competitor_research, training_onboarding). NOT a container; there is no applications table. It is the layer ABOVE workspace.
  • workspace = a container bound N:1 to exactly one application_type. One client has MANY workspaces. workspaceclient/tenant.
  • form = a submission/questionnaire (form_templates / form_questions / form_responses), workspace-scoped. One workspace hosts MANY forms. form_type ∈ {bid, rfp, pqq, itt, tender, questionnaire, …}. “bid” is a form_TYPE, not an application.
  • q_a_pair = corpus-level reusable knowledge unit; NOT workspace-partitioned (one client’s shared corpus; workspace relevance computed at query time via scope_tag overlap).
  • Containment: tenant ⊃ workspaces (1 per application_type) ⊃ forms ⊃ form_responses; q_a_pairs are corpus-level.

2.2 Live schema reality (DB-verified, Platform zjqbrdctesqvouboziae, 24/06/2026)

Section titled “2.2 Live schema reality (DB-verified, Platform zjqbrdctesqvouboziae, 24/06/2026)”
FactVerified value
Live procurement workspaces11 (workspaces JOIN application_types WHERE key=‘procurement’)
Where buyer/deadline/submission/outcome + 10-state workflow liveworkspaces.domain_metadata JSONB, validated by ProcurementMetadataSchema (lib/validation/schemas.ts:1019); workspaces.status column mirrors state via trigger
procurement_workspaces satelliteEXISTS but 0 rows — empty extension-point marker; columns only id, workspace_id, created_at, updated_at (no buyer/deadline/outcome)
workspaces columnsid, name, description, color, created_at, updated_at, domain_metadata, is_archived, status, created_by, updated_by, icon, application_type_idNO type column
form_templates0 rows; columns include first-class form_type, deadline, issuing_organisation, evaluation_methodology — but no outcome / submission_date
form_questions30 rows, keyed to workspace_id; columns end …template_requirement_idNO form_template_id FK
form_types CV8 rows: bid, checklist, itt, pqq, questionnaire, rfp, sales_proposal_template, tender. Procurement-applicable = 7 (all except sales_proposal_template; checklist/questionnaire are multi-application). The straw-man’s framework/dps/gcloud do not exist.
q_a_pairsCorpus-level, no workspace_id FK (relevance via scope_tag overlap)
  • Workspace altitude = live and populated. 11 procurement workspaces; all per-engagement facts in workspaces.domain_metadata JSONB; 30 questions hanging off workspace_id. The buyer-facing UI (creation wizard, detail page, win-rate engine) is built on “1 workspace = 1 tender” and surfaces no form-type concept anywhere.
  • Form altitude = built but empty. form_templates carries the right per-form columns and the AI/ matching stack (ID-57 question_matches) keys on form_questions + form_types — but form_templates has 0 rows. The columns exist; nothing populates them yet.
  • The lopsidedness is the key insight: the form altitude is schema-present, data-empty. ID-45 re-ingest is the event that will populate one altitude or the other. Decide first, or re-ingest cements the workspace-centric model (B) by default.

3. Source-of-truth map (canonical NOW vs stale / superseded)

Section titled “3. Source-of-truth map (canonical NOW vs stale / superseded)”
TopicCanonical NOWStale / superseded
Ontology glossaryplatform-direction.md (S391) — §2.1 aboveAny “workspace = client/tenant” framing (task-120 pre-S391 layers, self-retracted)
What “a procurement” IS in live codeA workspaces row with application_types.key='procurement', addressed at /api/procurement/[id]The straw-man’s implied dedicated procurements/satellite entity — no procurements table exists
Where buyer/deadline/outcome + workflow live TODAYworkspaces.domain_metadata JSONB + workspaces.statusStraw-man + intended-arch claim a procurement_workspaces SATELLITE holds them — it is empty
Where buyer/deadline/form_type SHOULD live (per shipped ingestion)form_templates columns (form_type, deadline, issuing_organisation, evaluation_methodology)— (live ingestion reality; contradicts the straw-man)
form_typeform_types CV (8 rows, §2.2); bid['procurement']; bid is a form_TYPE”procurement-is-a-form-type” inverted framing = ratified-REJECTED (task-71 {71.12}); straw-man’s 7-key {bid,rfp,pqq,itt,framework,dps,gcloud}
q_a_pairsCorpus-level, no workspace_id FK”q_a_pairs.workspace_id NOT NULL” / “cross-workspace = tenancy wall” (RATIFIED-RETIRE)
MCP/tool vocabularyUmbrella = procurement (list_active_procurement); item = form_* (get_form_question)Per-item bid_* naming; residual get_bid_summary flagged “verify”
Workflow state-machine HOME (intent)Unresolved — substrate (reserved-seats) says state machines live in lib/<key>/ code; ID-61 KEEP-list says VALID_BID_STATUSES belongs to the form_type lifecycleStraw-man + intended-arch put a 10-state machine ON the workspace satellite
bid_questions/bid_responses table namesRenamed form_questions/form_responses (ID-64, ~2026-06-09; zero bid% tables in prod)S403 recon “tables retain bid names” — superseded
Deployment / tool ceilingIONOS/Coolify; per-tool structured justification (ID-71 WS-11)“Cloud Run sidecar”; “~30–40 tool ceiling” (intended-arch 01-vision)

4. The core domain question — FORM vs WORKSPACE

Section titled “4. The core domain question — FORM vs WORKSPACE”

The glossary says one workspace hosts many forms, each with its own buyer/deadline/outcome. The live schema + UI store one of each per workspace. So a workspace can physically represent only one tender today. Three resolutions, each grounded in the verified evidence.

Move workflow + deadline + outcome + buyer to the form; the workspace becomes a pure umbrella.

  • Backed by: every shipped ingestion task — ID-52 (form_templates already carries form_type/deadline/issuing_organisation/evaluation_methodology), ID-80 (the pipeline writes those onto the form), ID-57 (matching keys on form_questions + form_types), ID-71 (item layer = form vocabulary). The reserved-seats 1:1 satellite cardinality argues for it (one satellite row can’t hold N tenders).
  • Cost / lift (largest, buyer-facing): (i) a real per-form engagement entity that owns outcome/submission_date/workflow (today form_templates has neither column); (ii) re-key form_questions from workspace_id to a form id (it has no form_template_id FK today); (iii) lift the ProcurementMetadataSchema JSONB blob + workspaces.status onto the form entity; (iv) the UI must expose form-type / multi-form (today it shows none).
  • Verdict: most glossary- and re-ingest-consistent; the AI/matching stack already lives at this altitude, so it is the lower-friction landing for everything except the buyer-facing UI.

Keep buyer/deadline/outcome/workflow on the workspace; accept 1 engagement = 1 workspace.

  • Backed by: the live code reality + the ID-61 rename, which baked “1 workspace = 1 tender” into the wizard, detail page, and win-rate engine.
  • Cost / lift: cheapest — matches what is shipped; ID-61 Unit F copy ships as-is.
  • Risk: directly contradicts the glossary; strands the per-form form_templates columns the pipeline already writes (duplication/fork); breaks the moment a client runs a PQQ then an ITT in one engagement. This is the option ID-45 would harden by default if no decision is made.

Workspace = umbrella that may hold many forms; per-tender fields live on the form; the workspace keeps only an aggregate/roll-up (e.g. nearest deadline, overall status, win-rate denominator).

  • Backed by: the substrate placing state machines in lib/<key>/ code (not workspace columns) + form_templates already owning the per-form fields; reconciles the ID-61 internal split (state machine documented as the form_type’s, driving fields on the workspace) by moving the fields to the form and keeping a thin workspace roll-up.
  • Cost / lift: most design work — must define the form/engagement entity, the roll-up semantics, AND the multi-form UI/IA.
  • Risk: most faithful long-term and re-ingest-safe; risk of over-building if v1 clients genuinely run one form per engagement.

4.5 Recommendation (for ratification, not a pre-commit)

Section titled “4.5 Recommendation (for ratification, not a pre-commit)”

The evidence leans A/C (form-ward). The entire AI/ingestion half already votes form-centric, ID-45 re-ingest writes the form altitude, and the form altitude is schema-present, data-empty — so moving now is cheap on the data side (almost nothing to migrate: form_templates = 0 rows) and expensive only on the buyer-facing UI side. The single decisive sub-question is §6 D2 (does a v1 engagement ever hold more than one form?):

  • If yes / plausiblyCandidate C (umbrella + per-form fields + workspace roll-up). Most defensible; avoids a second migration when multi-form demand arrives.
  • If firmly no for v1 → a thin Candidate C (≈ A with the workspace as a 1:1 convenience umbrella) still beats B, because it puts the lifecycle where the AI stack and re-ingest already are, and keeps B’s “1 = 1” as a UI simplification rather than a schema constraint.

Against B: it is cheapest this week but forks live pipeline columns and converts a presentation simplification into a schema law that ID-45 then cements — the most expensive thing to undo post-launch.

PRODUCT ({130.2}) will carry the ratified choice as a numbered invariant; RESEARCH’s job is to surface the choice and its evidence, which it now has.


5. Drifts & contradictions found (9 — corrected against live DB)

Section titled “5. Drifts & contradictions found (9 — corrected against live DB)”
  1. Task-61 identity (RESOLVED, not a remap). Ledger Task 61’s title is “Pre-launch operational pre-decisions + observability (T13)”. The bid→procurement rename is a sub-scope of it (subtasks {61.3}, {61.7/9/10/11/12} = Units A–F). No ID-90/102 remap occurred — a title/scope collision under one umbrella number. The rename is owned by 61 but is not its title.
  2. Straw-man “RATIFIED” ≠ approved. specs/procurement-workspaces/{PRODUCT,TECH}.md (in the private docs-site) is marked RATIFIED-S242, but that was ratification against May-2026 investigation docs, predating both the S391 glossary and the conflation discovery; the S242 verifier never interrogated form-vs-workspace. Treat as straw-man, not authority.
  3. ID-61’s own internal split. The rename put buyer/deadline/submission/outcome on the WORKSPACE but KEPT VALID_BID_STATUSES/TERMINAL_BID_STATUSES with the rationale “bid is a genuine form_type with its own lifecycle” — i.e. the state machine is documented as a form_type property while its driving fields sit on the workspace. (Note: the 10 states are also written twiceProcurementMetadataSchema inline at schemas.ts:1022-1033 AND the standalone VALID_BID_STATUSES const at schemas.ts:2007-2018 — a refactor hazard PRODUCT/TECH should de-dup.)
  4. bid arg-alias, not a schema axis (CORRECTED). Earlier framing claimed a workspaces.type='bid' column creating a “parallel encoding of bid”. Live DB has NO workspaces.type column. The only live type==='bid' is an MCP arg-compatibility shim (lib/mcp/tools/workspaces.ts:78, args.type==='bid' ? 'procurement'). Retire the alias, but it is not a schema-level dual-axis problem.
  5. deadline duplicated across altitudes. workspaces.domain_metadata->>'deadline' AND form_templates.deadline both exist; which is canonical is unresolved (ingestion writes one, UI reads the other). The altitude decision (§4) resolves this.
  6. form_questions keyed to workspace_id, not a form id. “Forms as distinct artifacts within a workspace” are not partitioned at the question/response layer — contradicts “one workspace hosts many forms” at the data level. (Candidate A/C must re-key.)
  7. Intended-arch internal inconsistency. 04-workspace-types.md §7.1 lists 7 procurement form_types (no tender); §7.3 says “8 including tender”. The closed list was never authoritatively pinned — superseded by the live form_types table (§2.2).
  8. Mis-attributed eval claim. “Phew bid library = eval ground-truth” is NOT in Task-104 (eval infra; “baseline” there means regression snapshots, not domain data) — it originates in the straw-man B-28. Do not let it leak into ID-130 sourcing as a Task-104 fact.
  9. State-machine source-of-truth drift. application_types.state_machine_config JSONB exists but the live 10-state workflow is hardcoded in ProcurementMetadataSchema, not driven from that column. Whether the workflow becomes config-driven, and at which altitude, is open.

6. Decisions for Liam (ratification register)

Section titled “6. Decisions for Liam (ratification register)”

6.0 RATIFIED — 25/06/2026 (S411, Lavish gate)

Section titled “6.0 RATIFIED — 25/06/2026 (S411, Lavish gate)”

Liam accepted all recommended calls. These are now binding inputs to {130.2} PRODUCT.

#Ratified decision
D1C — Hybrid. Workspace = umbrella holding many forms; per-stage buyer/deadline/outcome/workflow live on the form; workspace keeps a roll-up (overall outcome, nearest deadline, win-rate denominator).
D2Many forms per engagement (PSQ → ITT → tender under one procurement) — research-confirmed; this is the cardinality PRODUCT must model.
D3HOLD ID-61 Unit F — fold its UI copy into the ratified IA, do not ship as a standalone sweep.
D4Align the form_type list to Procurement Act 2023 — PRODUCT reviews pqq → SQ/PSQ terminology and confirms itt/tender/bid/rfp; the live set is the baseline.
D5deadline on the form (per-stage); workspace shows nearest as a roll-up. Kills the current dual-altitude duplication.
D6Defer the empty procurement_workspaces satellite’s fate to TECH ({130.3}) — activate-as-roll-up vs drop is an implementation detail.
D7Surface form_type in the UI via infer + confirmable picker — classify from the uploaded document, user confirms/overrides.
D8Fold the 5 ID-61 PJ flags into the IA — singular nav noun “Procurement”; keep industry craft terms (“bid-writing”) where natural; PRODUCT resolves the rest. No mechanical re-litigation.

Derived modelling note (from D1+D2 + research): because each stage is independently scored and the SQ/PSQ carries a pass/shortlist outcome while the ITT carries the win/lose, outcome is effectively multi-valued — per-form (per-stage) outcome + a workspace-level overall outcome. PRODUCT must model both and define how the workspace roll-up derives the overall result.


The original options below are retained as the rationale behind §6.0. The headline call (D1) drove everything; the rest followed from it.

  • D1 — The altitude: A / B / C (§4). Recommendation: C, or a thin-C ≈ A; against B. Everything downstream depends on this.
  • D2 — Does a v1 engagement ever hold more than one form? (PQQ → ITT → tender in one engagement?) If yes/plausibly → the form altitude is needed now. If firmly no → a 1:1 umbrella simplification is defensible. This is the decisive sub-question for D1.
  • D3 — Hold or ship ID-61 Unit F (UI copy)? Unit F hard-codes “procurement workspace = one tender”; shipping before D1 creates rework. Recommendation: HOLD pending this RESEARCH.
  • D4 — Canonical form_type closed list. Ratify the live form_types (7 procurement-applicable: bid, checklist, itt, pqq, questionnaire, rfp, tender) — and confirm the straw-man’s framework/dps/gcloud are dropped, not re-added.
  • D5 — Canonical deadline altitude (form vs workspace, or a roll-up) — kills drift #5.
  • D6 — Fate of the empty procurement_workspaces satellite — activate as the form/engagement extension point, repurpose, or drop.
  • D7 — Surface form_type in the UI at all in v1? If so, where — a picker at form-creation, or inferred from the uploaded document’s classification (FormMetadata.form_type)? Today it is invisible.
  • D8 — The 5 ID-61 Unit-F product-judgment flags (fold into the IA decision rather than re-litigate mechanically):
    • PJ-1 nav noun singular vs plural (Bids/Back to BidsProcurement) — the first IA hook for the form-type question.
    • PJ-2 “Manage bid submissions and tender responses” rephrase (page.tsx:155).
    • PJ-3 “bid-writing” craft term — keep or rephrase (proposal-/tender-writing)?
    • PJ-4 “Past bid responses” card (persona cluster copy).
    • PJ-5 mcp-apps-local Bid* type names (BidSummary/BidDashboardData/ExpandedBidState/ BidBriefing/bidId).

  • ID-61 Unit F (the {61.12} UI-copy sweep — 62 copy-sites / 13 lockstep pairs / DOM-ids / e2e / bare-words, mapped in specs/id-61-bid-rename/UNIT-F-SCOPE-MAP.md) — held and folded into the ratified IA, not executed as a mechanical rename.
  • Deferred ID-61 wire-field cleanupactive_bids/bid_summary wire fields (the 4th wire contract Unit D didn’t cover); bid_outcome alias in get_content_win_rate; stale /api/bids JSDoc; the --bid-* design tokens (bl-349) and persisted-literal classes (bl-370) per the ID-61 KEEP/defer register.
  • NOT in scope: the q_a_pair corpus model (corpus-level, settled); the dedup flywheel (ID-120 done); the cocoindex pipeline standup (ID-127); the question_matches scoring internals (ID-57).

8. Timing & sequencing (the ID-45 constraint)

Section titled “8. Timing & sequencing (the ID-45 constraint)”

ID-45 (first-client full-corpus re-ingest, in_progress; {45.3} TECH / {45.4} PLAN unwritten + a GO gate) writes content into whichever altitude is live. The pipeline already writes the form altitude (form_templates columns), so:

  • If ID-130 lands on A/C, re-ingest naturally populates the right altitude (and the data migration is near-trivial today: form_templates = 0 rows, procurement_workspaces = 0 rows).
  • If ID-130 lands on B, the pipeline’s per-form columns become dead/forked data and re-ingest cements the workspace-centric model.

Sequencing rule: the ID-130 altitude decision (D1) and the schema migration that follows it should land before ID-45 GO. ID-130 RESEARCH→PRODUCT→TECH can proceed in parallel with the ID-127 pipeline standup (disjoint lanes); they converge at ID-45.


A fresh Planner authors PRODUCT.md against this ratified RESEARCH. PRODUCT must:

  1. Carry the ratified D1 altitude as numbered, testable invariants — define the entity that owns buyer/deadline/submission/outcome/workflow, and the workspace↔form cardinality.
  2. Specify the UI/IA for form-type surfacing (D7) and multi-form navigation (if D2 = many), folding in the 5 PJ flags (D8) as copy/IA decisions.
  3. Specify the form_type closed list (D4) and the canonical deadline home (D5).
  4. State the migration shape at product altitude (what moves from workspaces.domain_metadata to the chosen home; the fate of the empty satellite, D6) — TECH ({130.3}) owns the SQL.
  5. Hold the guardrails: keep q_a_pairs corpus-level; keep “bid = form_type, not application”; no tenant_id; the AI/matching stack already keys on form_questions + form_types, so the form altitude is the lower-friction landing for the rest of the AI stack.

Authored from the S411 grounding workflow (wf_97aef791-2e1): 15 parallel source digesters → synthesis → adversarial completeness critic (verdict usable-with-gaps; all flagged overclaims corrected against the live DB in §2.2/§5). Sources digested: ledger Tasks 120, 80, 52, 56, 57, 61, 71, 104; the straw-man procurement-workspaces spec (PRODUCT+TECH); reserved-workspace-seats; intended-architecture (01-vision, 04-workspace-types, 05-qa-flow, 08-new-features); the ID-61 rename map (STRATEGY, DECISION-REGISTER, UNIT-F-SCOPE-MAP); live DB schema; live procurement UI/code. Per-source digests and the full grounding brief are retained in the S411 session scratchpad.

End of RESEARCH — awaiting Liam ratification (D1–D8, §6).