{"openapi":"3.1.0","info":{"title":"CivilQuants API","description":"QS-grade parametric estimating engine. REST + MCP surface.","version":"0.0.1"},"paths":{"/api/assemblies":{"get":{"tags":["assemblies"],"summary":"List Assemblies","operationId":"list_assemblies_api_assemblies_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/_AssembliesListResponse"}}}}}}},"/api/assemblies/{slug}":{"get":{"tags":["assemblies"],"summary":"Get Assembly Detail","operationId":"get_assembly_detail_api_assemblies__slug__get","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/_AssemblyDetailResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/assemblies/{slug}/compute":{"post":{"tags":["assemblies"],"summary":"Compute Assembly","operationId":"compute_assembly_api_assemblies__slug__compute_post","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/civilquants__api__routes__assemblies___ComputeRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/_ComputeResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/assemblies/{slug}/dxf":{"post":{"tags":["assemblies"],"summary":"Download Assembly Dxf","description":"Stream the full CAD DXF for an assembly's current parameter set.\n\nMirrors the entitlement gate on POST /compute: anonymous → 402,\nuser without active subscription → 402. The DXF carries the same\n9-cell title block + warranty disclaimer the paid Excel/PDF\nartefacts do (CLAUDE.md pin 10 §15 invariant).","operationId":"download_assembly_dxf_api_assemblies__slug__dxf_post","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/civilquants__api__routes__assemblies___ComputeRequest"}}}},"responses":{"200":{"description":"Streamed DXF file, content-type application/dxf.","content":{"application/dxf":{}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/assemblies/{slug}/excel":{"post":{"tags":["assemblies"],"summary":"Download Assembly Excel","description":"Stream the customer-facing 2-tab .xlsx for an assembly's current\nparameter set.\n\nTier-aware entitlement per `entitlement_for_assembly`: free-tier\nslugs (six per launch spec § 1.1) grant for any caller — the\nworkbook just renders with the free-tier watermark + sheet\nprotection so the marketing-attribution moat (pin 13) stays\nintact. Paid-tier slugs require an active Subscription / 7-day\npass / dev-unlock; otherwise 402 with the canonical hint envelope.\n\nThe render-tier (controls watermark + protection) is derived from\nthe caller's auth context independently of the slug tier:\nauthenticated callers on free slugs get unlocked workbooks; the\nfree-tier watermark + lock only fires when the caller is anonymous\n(or unauthenticated in dev without dev-unlock).","operationId":"download_assembly_excel_api_assemblies__slug__excel_post","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"include_workings","in":"query","required":false,"schema":{"type":"boolean","default":true,"title":"Include Workings"}},{"name":"include_procurement_summary","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Include Procurement Summary"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/civilquants__api__routes__assemblies___ComputeRequest"}}}},"responses":{"200":{"description":"Streamed .xlsx workbook — Bill of Quantities + Quantities by assembly per CLAUDE.md pin 2. Free-tier callers receive a watermarked + sheet-protected workbook; entitled callers (active subscription / 7-day pass / dev-unlock) receive an unlocked workbook with no watermark.","content":{"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":{}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/assemblies/{slug}/pdf":{"post":{"tags":["assemblies"],"summary":"Download Assembly Pdf","description":"Stream a branded single-assembly PDF drawing sheet.\n\nPaid-only — mirrors the DXF gate (anonymous / unsubscribed → 402)\nvia the flat ``entitlement_for``. This applies to ALL slugs,\nincluding the six free-tier assemblies: the free drawing surface is\nthe inline SVG preview, while the downloadable PDF/DXF drawing-sheet\ndeliverables are a paid feature (see entitlement.py — \"free callers\nsee the inline SVG preview but pay for the CAD file\"). The sheet\ncarries the CQ monogram, the 9-cell title block, the dimensioned\ndrawing, and the measured quantities, with the warranty disclaimer\non cover + footer (pin 10 §15 invariant).","operationId":"download_assembly_pdf_api_assemblies__slug__pdf_post","parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","title":"Slug"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/civilquants__api__routes__assemblies___ComputeRequest"}}}},"responses":{"200":{"description":"Streamed PDF drawing sheet — CQ-branded cover + title block + dimensioned drawing + measured quantities + warranty disclaimer.","content":{"application/pdf":{}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/artefacts/{token}":{"get":{"tags":["artefacts"],"summary":"Fetch Artefact","operationId":"fetch_artefact_api_artefacts__token__get","parameters":[{"name":"token","in":"path","required":true,"schema":{"type":"string","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/mcp/health":{"get":{"tags":["mcp"],"summary":"Mcp Health","description":"Liveness probe wrapped in the canonical MCP envelope (schema § 0.1).\n\nTier is `paid` because health-check is infrastructure-tier surface (not\na free-tier customer-facing tool) — the schema's free-tier static fields\n(`tos_url`, `warranty_disclaimer`, `tier_upgrade`) don't apply.","operationId":"mcp_health_api_v1_mcp_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MCPResponse_HealthPayload_"}}}}}}},"/api/v1/mcp/catalogue":{"get":{"tags":["mcp"],"summary":"Mcp Catalogue","description":"Picker-tree catalogue per launch spec § 5.2 step 8.\n\nReads ``get_category_tree(include_empty=False)`` so the response only\nsurfaces L1/L2 leaves with at least one registered assembly — the\nlive picker contract clients integrate against. Every entry carries\n``tier`` so free callers can self-filter to the six free assemblies\n(attenuation_tank, manhole, gully, headwall, end_area_earthworks,\ncantilever_wall) without making a TIER_INSUFFICIENT-bounced compute\ncall to find out.\n\nPublic route — no auth header required. Tier reported on the response\nenvelope as ``free`` so the schema § 0.7 upgrade hooks surface on\nanonymous calls; authenticated callers receive the same picker tree\n(the catalogue is identical across tiers; only call rights differ).\n\nFree callers must have a recent click-wrap acceptance recorded; the\ngate returns ACCEPTANCE_REQUIRED with the landing-page URL otherwise\n(Phase 3.4.5 commit 3 / 19th-fire G).","operationId":"mcp_catalogue_api_v1_mcp_catalogue_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/MCPResponse_CataloguePayload_"},{"$ref":"#/components/schemas/MCPResponse_Any_"}],"title":"Response Mcp Catalogue Api V1 Mcp Catalogue Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/mcp/compute":{"post":{"tags":["mcp"],"summary":"Mcp Compute","description":"Universal compute dispatcher per canonical schema § 1.\n\nLooks up the requested assembly, applies tier-routing\n(TIER_INSUFFICIENT envelope when a free caller targets a paid assembly),\nruns the engine + the chosen standard's renderer, and wraps the result\nin `MCPResponse[ComputeResult]`. A free caller requesting any standard\nother than MMHW is HARD-REJECTED with INVALID_INPUT (56th-fire T1).\n\nFree-tier callers consume daily quota per launch spec § 3.6 (10 calls/day\nper IP, resets at UTC midnight). Paid callers bypass the limiter entirely.\n\nPer-tool `@mcp.tool()` decorators with the canonical \"CALL THIS WHENEVER\"\ndescriptions from spec § 6.5.5 land in Track B-Infra; this generic route\nis the FastAPI sidecar that the fastmcp wrapper will dispatch into.","operationId":"mcp_compute_api_v1_mcp_compute_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/civilquants__api__routes__mcp___ComputeRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/MCPResponse_ComputeResult_"},{"$ref":"#/components/schemas/MCPResponse_Any_"}],"title":"Response Mcp Compute Api V1 Mcp Compute Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/mcp/compute-multi-section":{"post":{"tags":["mcp"],"summary":"Mcp Compute Multi Section","description":"Multi-section compute dispatcher (60th-fire item K).\n\nRuns N sections of a single linear/sectioned assembly and aggregates the\nbills into a single ``MultiSectionResult`` envelope. Section-specific\ngeometry keys (e.g. ``stem_height``) are passed as Pydantic extras on each\n``_SectionInput`` and reach the engine unmodified via ``section.params``.\n\nPer-section rate-slot consumption: a free caller burns one slot per\nsection. Paid callers bypass the limiter entirely, matching `mcp_compute`.","operationId":"mcp_compute_multi_section_api_v1_mcp_compute_multi_section_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/_MultiSectionRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/MCPResponse_MultiSectionResult_"},{"$ref":"#/components/schemas/MCPResponse_Any_"}],"title":"Response Mcp Compute Multi Section Api V1 Mcp Compute Multi Section Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/mcp/resolve-description":{"post":{"tags":["mcp"],"summary":"Mcp Resolve Description","description":"Deterministic catalogue router (item L). Free / anonymous-allowed.","operationId":"mcp_resolve_description_api_v1_mcp_resolve_description_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/_ResolveRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/MCPResponse_ResolveResult_"},{"$ref":"#/components/schemas/MCPResponse_Any_"}],"title":"Response Mcp Resolve Description Api V1 Mcp Resolve Description Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/mcp/project/save":{"post":{"tags":["mcp","project"],"summary":"Mcp Project Save","description":"Persist a project + its assembly list under the current actor.\n\nAuthorisation: paid-tier user / api_token only. Pass holders blocked\n(see ``_gate_paid_user``). Owner is stamped from ``auth.user_id``;\nfree-form ``name`` / ``client`` etc. are written verbatim subject to\nPydantic length caps. Unknown ``slug`` values inside ``assemblies``\nbounce as a single ``INVALID_INPUT`` envelope listing the offending\nslug — the route does NOT partially-commit a project with bad rows.","operationId":"mcp_project_save_api_v1_mcp_project_save_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/_ProjectSaveRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/MCPResponse__ProjectSaveResponse_"},{"$ref":"#/components/schemas/MCPResponse_Any_"}],"title":"Response Mcp Project Save Api V1 Mcp Project Save Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/mcp/project/{project_id}":{"get":{"tags":["mcp","project"],"summary":"Mcp Project Fetch","description":"Return the saved project + its assemblies for LLM-side inspection.","operationId":"mcp_project_fetch_api_v1_mcp_project__project_id__get","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/MCPResponse__ProjectFetchResponse_"},{"$ref":"#/components/schemas/MCPResponse_Any_"}],"title":"Response Mcp Project Fetch Api V1 Mcp Project  Project Id  Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"tags":["mcp","project"],"summary":"Mcp Project Update","description":"Update one or more metadata fields on an existing project.\n\nReturns the full updated project shape — same payload as\n``GET /api/v1/mcp/project/{id}`` — so the UI can replace its local copy in\none round trip. Paid-tier only. Same RESOURCE_NOT_FOUND collapse\npattern as the GET endpoint (cross-user + missing IDs are\nindistinguishable from the client's perspective).","operationId":"mcp_project_update_api_v1_mcp_project__project_id__patch","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/_ProjectPatchRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/MCPResponse__ProjectFetchResponse_"},{"$ref":"#/components/schemas/MCPResponse_Any_"}],"title":"Response Mcp Project Update Api V1 Mcp Project  Project Id  Patch"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["mcp","project"],"summary":"Mcp Project Delete","description":"Permanently delete a project. Cascade removes assemblies,\nrevisions, rates, and outputs per the ORM ``cascade='all,\ndelete-orphan'`` rules in ``persistence/projects/models.py``.\n\nPaid-tier only. Returns RESOURCE_NOT_FOUND for unknown IDs and for\nprojects owned by other users (timing-attack protection).","operationId":"mcp_project_delete_api_v1_mcp_project__project_id__delete","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/MCPResponse__ProjectDeleteResponse_"},{"$ref":"#/components/schemas/MCPResponse_Any_"}],"title":"Response Mcp Project Delete Api V1 Mcp Project  Project Id  Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/mcp/project/{project_id}/assembly":{"post":{"tags":["mcp","project"],"summary":"Mcp Project Add Assembly","description":"Add a single assembly to an existing project (G.4.5).\n\nMirrors the multi-assembly path in ``mcp_project_save`` for a single\nrow — same slug validation, same owner check via ``_load_owned_project``,\nsame `ProjectService.add_assembly` call. Position is auto-assigned at\nthe end of the existing assembly list.\n\nUsed by the \"Add to project\" CTA on `/calculators/[slug]` when the\nuser picks an existing project from the modal.","operationId":"mcp_project_add_assembly_api_v1_mcp_project__project_id__assembly_post","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/_AddAssemblyRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/MCPResponse__AddAssemblyResponse_"},{"$ref":"#/components/schemas/MCPResponse_Any_"}],"title":"Response Mcp Project Add Assembly Api V1 Mcp Project  Project Id  Assembly Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/mcp/project/{project_id}/recompute":{"post":{"tags":["mcp","project"],"summary":"Mcp Project Recompute","description":"Re-run every enabled assembly + render aggregated BoQs.\n\nDefault behaviour (omit ``standard_codes``) renders all four standards\nso the cross-standard quantity-invariance moat (H3.5 canary) is\nvisible at the project level. Caller can narrow to a single standard\nfor token-cheap polling.\n\nHydration uses ``ComputeService.hydrate_assembly`` which (post Step\n1.6) calls ``coerce_parameters`` to rebuild nested-dataclass list\nfields safely — the underlying save→reload→recompute path is what\nspec § 3.4.2 promises (\"save your tender, come back tomorrow, click\nrecompute\").","operationId":"mcp_project_recompute_api_v1_mcp_project__project_id__recompute_post","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/_ProjectRecomputeRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/MCPResponse__ProjectRecomputeResponse_"},{"$ref":"#/components/schemas/MCPResponse_Any_"}],"title":"Response Mcp Project Recompute Api V1 Mcp Project  Project Id  Recompute Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/mcp/project/{project_id}/excel":{"post":{"tags":["mcp","project"],"summary":"Mcp Project Excel","description":"Render the project's XLSX bundle and persist to the artefact store.\n\nSheet order (per save-file recommendation):\n  * Project_Total  — aggregated BoQ in project's default_standard\n  * A0N_<slug>     — one tab per enabled assembly (position-ordered)\n  * Procurement    — standard-agnostic material rollup (appendix)\n\nPaid-tier only (same gate as the other ``/api/v1/mcp/project/*`` routes).\nThe 24h presigned URL per spec § 6.5 lives in the ArtefactRef.","operationId":"mcp_project_excel_api_v1_mcp_project__project_id__excel_post","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/_ProjectExcelRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/MCPResponse__ProjectExcelResponse_"},{"$ref":"#/components/schemas/MCPResponse_Any_"}],"title":"Response Mcp Project Excel Api V1 Mcp Project  Project Id  Excel Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/mcp/project/{project_id}/dxf-bundle":{"post":{"tags":["mcp","project"],"summary":"Mcp Project Dxf Bundle","description":"Render the project's DXF bundle and persist to the artefact store.\n\nBundle shape (per save-file Q1 — \"separate index document\" pattern):\n  * ``00_Index.dxf``                — project metadata + filename listing\n  * ``A0N_<slug>_<hash>.dxf``       — one per enabled assembly\n\nPaid-tier only (same gate as the other ``/api/v1/mcp/project/*`` routes).\n24h presigned URL per spec § 6.5.","operationId":"mcp_project_dxf_bundle_api_v1_mcp_project__project_id__dxf_bundle_post","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/_ProjectDxfBundleRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/MCPResponse__ProjectDxfBundleResponse_"},{"$ref":"#/components/schemas/MCPResponse_Any_"}],"title":"Response Mcp Project Dxf Bundle Api V1 Mcp Project  Project Id  Dxf Bundle Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/mcp/project/{project_id}/pdf-bundle":{"post":{"tags":["mcp","project"],"summary":"Mcp Project Pdf Bundle","description":"Render the project's PDF bundle and persist to the artefact store.\n\nPage structure (per save-file Q2 — single multi-page PDF):\n  * Cover             — project metadata + hash + warranty\n  * Drawing pages     — one per enabled assembly (position-ordered)\n  * Project BoQ       — aggregated, in the project's default standard\n  * Procurement       — standard-agnostic material rollup appendix\n\nPaid-tier only (same gate as the other ``/api/v1/mcp/project/*`` routes).\n24h presigned URL per spec § 6.5.","operationId":"mcp_project_pdf_bundle_api_v1_mcp_project__project_id__pdf_bundle_post","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/_ProjectPdfBundleRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/MCPResponse__ProjectPdfBundleResponse_"},{"$ref":"#/components/schemas/MCPResponse_Any_"}],"title":"Response Mcp Project Pdf Bundle Api V1 Mcp Project  Project Id  Pdf Bundle Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/mcp/project/{project_id}/assembly/{assembly_id}":{"delete":{"tags":["mcp","project"],"summary":"Mcp Project Remove Assembly","description":"Remove a single assembly from a project.\n\nOwner check is on the project (``_load_owned_project``); we then\nverify the ``assembly_id`` belongs to that project (an attacker who\nknows an assembly_id from a different project can't use a project\nthey own to delete it). Cross-project + unknown assembly + cross-\nuser project all collapse to the same RESOURCE_NOT_FOUND envelope.","operationId":"mcp_project_remove_assembly_api_v1_mcp_project__project_id__assembly__assembly_id__delete","parameters":[{"name":"project_id","in":"path","required":true,"schema":{"type":"string","title":"Project Id"}},{"name":"assembly_id","in":"path","required":true,"schema":{"type":"string","title":"Assembly Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/MCPResponse__AssemblyDeleteResponse_"},{"$ref":"#/components/schemas/MCPResponse_Any_"}],"title":"Response Mcp Project Remove Assembly Api V1 Mcp Project  Project Id  Assembly  Assembly Id  Delete"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/mcp/projects":{"get":{"tags":["mcp","project"],"summary":"Mcp Projects List","description":"Return all projects owned by the authenticated actor (G.4.7 add).\n\nPaid-tier only — same gate as the singular ``/api/v1/mcp/project/...`` routes.\nFree / pass / unauthenticated callers get ``TIER_INSUFFICIENT``.\n\nSorted by ``updated_at DESC`` (newest first) per ProjectService's\n``list_projects`` ordering. The summary shape is intentionally narrow\n— no parameters, no assembly details — so listing 25 projects (the\nv1 per-user cap per 29th-fire Q5) stays cheap to serialise.","operationId":"mcp_projects_list_api_v1_mcp_projects_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/MCPResponse__ProjectListResponse_"},{"$ref":"#/components/schemas/MCPResponse_Any_"}],"title":"Response Mcp Projects List Api V1 Mcp Projects Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/mcp/resources":{"get":{"tags":["mcp"],"summary":"Mcp Resources List","description":"List the eight canonical MCP resources per launch spec § 3.5.","operationId":"mcp_resources_list_api_v1_mcp_resources_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MCPResponse_ResourceListPayload_"}}}}}}},"/api/v1/mcp/resources/read":{"get":{"tags":["mcp"],"summary":"Mcp Resources Read","description":"Fetch a single resource body by canonical URI.","operationId":"mcp_resources_read_api_v1_mcp_resources_read_get","parameters":[{"name":"uri","in":"query","required":true,"schema":{"type":"string","description":"A civilquants:// URI from the manifest.","title":"Uri"},"description":"A civilquants:// URI from the manifest."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MCPResponse_ResourcePayload_"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/mcp/render":{"post":{"tags":["mcp"],"summary":"Mcp Render","description":"Render the supplied compute_results into the chosen deliverable format.\n\nFree-tier callers consume daily quota per launch spec § 3.6 alongside\n/api/v1/mcp/compute calls — render is computationally heavier than compute, so\nbracketing it under the same quota is essential. Paid callers bypass.\n\nValidation order is deliberate: skill validity → paid-only tier gate →\nrate-limit consumption. The tier gate runs FIRST so a free caller hitting\nthe paid Word skill is rejected at the gate without burning daily quota\n(closes the IP-based quota-exhaustion DoS where an attacker drains a\nvictim's free quota by repeatedly POSTing the paid skill from the\nvictim's IP — security-reviewer 2026-05-15 finding).","operationId":"mcp_render_api_v1_mcp_render_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RenderRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/MCPResponse_RenderResult_"},{"$ref":"#/components/schemas/MCPResponse_Any_"}],"title":"Response Mcp Render Api V1 Mcp Render Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/legal/terms":{"get":{"tags":["legal"],"summary":"Terms Markdown","operationId":"terms_markdown_legal_terms_get","responses":{"200":{"description":"Successful Response","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/legal/privacy-policy":{"get":{"tags":["legal"],"summary":"Privacy Policy Markdown","operationId":"privacy_policy_markdown_legal_privacy_policy_get","responses":{"200":{"description":"Successful Response","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/legal/cookie-policy":{"get":{"tags":["legal"],"summary":"Cookie Policy Markdown","operationId":"cookie_policy_markdown_legal_cookie_policy_get","responses":{"200":{"description":"Successful Response","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/legal/tos":{"get":{"tags":["legal"],"summary":"Tos Redirect","description":"Back-compat alias retained from Phase 3.3.b. New callers should\nuse /legal/terms — that's the slug the legal docs themselves cite.","operationId":"tos_redirect_legal_tos_get","responses":{"308":{"description":"Successful Response"}}}},"/api/legal/accept":{"post":{"tags":["legal"],"summary":"Post Legal Accept","description":"Record a click-wrap acceptance of ToS + Privacy + Cookie v<current>.\n\nRejects (409) if the caller submits stale policy versions — the page\nthey clicked through pre-dates a policy bump and they need to\nre-render against the live versions before accepting.\n\nIP is HMAC-SHA256 hashed before storage (GDPR Art 5(1)(c)). The plan\nlocks the gate at IP granularity: a single acceptance from an IP\ncovers both `free_ppc` and `free_mcp` for the 24h window. The\n`surface` field on the row records which landing page generated the\nclick for analytics.","operationId":"post_legal_accept_api_legal_accept_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/_AcceptanceRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Post Legal Accept Api Legal Accept Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/v1/mcp/accept":{"get":{"tags":["mcp"],"summary":"Mcp Accept Redirect","description":"Redirect LLM agents to the browser-rendered acceptance landing page.\n\nPer the phase-3.4.5 plan: the human-facing acceptance page lives in\nthe Next.js web app at `civilquants.com/mcp/accept`. The API-side\n`/api/v1/mcp/accept` is a 302 sending the agent's user (the human at the\nkeyboard) to the consent page. The target URL is configurable via\n`MCP_ACCEPT_PAGE_URL` so preview deploys can point elsewhere.","operationId":"mcp_accept_redirect_api_v1_mcp_accept_get","responses":{"302":{"description":"Successful Response"}}}},"/api/billing/checkout/pass-7day":{"post":{"tags":["billing"],"summary":"Create Pass 7Day Checkout","description":"Create a Stripe Checkout session for the £19 7-day pass.\n\nAnonymous (no auth) — the 7-day-pass UX deliberately has no account\nrequirement; the email captured at Stripe Checkout is the unlock-token\nanchor.","operationId":"create_pass_7day_checkout_api_billing_checkout_pass_7day_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutPass7DayRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutPass7DayResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/billing/subscription/checkout":{"post":{"tags":["billing"],"summary":"Create Subscription Checkout","description":"Create a Stripe Checkout Session in `mode=subscription`.\n\nAuth required (Clerk JWT). `metadata.clerk_user_id` is set from the\nvalidated JWT; the request body's `clerk_user_id` / similar fields\n(if present) are ignored — they're not even in the schema. This is\nthe Codex P3.3.g HIGH-2 lesson: trust anchor stays server-side.\n\nCurrency resolution: explicit override → cf-ipcountry → Accept-\nLanguage → GBP fallback. Same path as the 7-day-pass route.\n\nTier seat semantics:\n  * Solo:     quantity = 1 (single user)\n  * Studio:   quantity = 5 (5-seat pack, fixed at v1)\n  * Practice: quantity = 25 (25-seat minimum, hardcoded at v1)\nAbove-minimum expansion deferred to v1.1 via Customer Portal.\n\nNo `trial_period_days` anywhere on the path — the freemium MCP\ntier IS the trial; the £10 7-day pass is the second on-ramp;\nPASS_CREDIT_FIRST_MONTH is the bridge.","operationId":"create_subscription_checkout_api_billing_subscription_checkout_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutSubscriptionRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutSubscriptionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/billing/subscription/status":{"get":{"tags":["billing"],"summary":"Get Subscription Status","description":"Poll for Subscription-row landing after a successful Checkout.\n\nThe /welcome page hits this every ~1s up to ~10s after a Stripe\nCheckout redirect. Returns `pending` until the\n`customer.subscription.created` webhook lands and the row is linked\nto the authenticated user (via `metadata.clerk_user_id` — set\nserver-side on the Checkout Session creation).\n\nAuth: Clerk JWT only. API tokens / pass_holders are rejected — they\nare not the buy-flow surface. The DB lookup filters on user_id so\none user cannot poll arbitrary subscription IDs to infer existence;\na row belonging to another user collapses to the same `pending`\nresponse shape as a missing row.","operationId":"get_subscription_status_api_billing_subscription_status_get","parameters":[{"name":"stripe_subscription_id","in":"query","required":true,"schema":{"type":"string","title":"Stripe Subscription Id"}},{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionStatusResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/billing/subscription/active":{"get":{"tags":["billing"],"summary":"Get Subscription Active","description":"Answer \"does this signed-in user have ANY active subscription?\".\n\nThe web subscriber-unlock signal: catalogue + calculator pages poll\nthis on load (no `session_id` in hand, unlike `/subscription/status`)\nto drive `useSubscriptionActive()` → unlock paid cards / workspace.\n\nAuth: Clerk JWT only. API tokens / pass_holders are rejected at 401 —\npass-holders already have the `cqp_`-token web path (`usePassActive()`);\nthis endpoint is specifically the subscriber signal. The response is a\nbare boolean (no tier / IDs / email) to avoid enumeration. Entitlement\nis keyed on the actor's `user_id` OR `org_id`, so org-seat Studio /\nPractice members resolve active via their organisation's subscription.","operationId":"get_subscription_active_api_billing_subscription_active_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SubscriptionActiveResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/billing/portal/session":{"post":{"tags":["billing"],"summary":"Create Portal Session","description":"Create a Stripe Customer Portal session for the authenticated user.\n\nSurfaced from the /welcome page footer (\"Manage subscription\").\nAuth: Clerk JWT only — API tokens / pass_holders are not the\ncustomer-self-service surface. The customer's `stripe_customer_id`\nis read off any Subscription row owned by the user (Stripe creates\none Customer per email; multiple subscriptions share it).\n\nStripe SDK errors collapse to 502 — internal exception shape never\nleaks to the client.","operationId":"create_portal_session_api_billing_portal_session_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PortalSessionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/billing/welcome/send":{"post":{"tags":["billing"],"summary":"Send Welcome","description":"Fire Day 0 + scheduled Day 3 onboarding emails for the\nauthenticated user's most recent Subscription.\n\nIdempotent: a second call returns `already_sent` without invoking\nResend. Gated by `WELCOME_EMAILS_ENABLED` (pick #5 Dave-approval\ngate). Resend SDK errors collapse to 502 with the Subscription row\nuntouched, so the next /welcome poll-and-call retries.","operationId":"send_welcome_api_billing_welcome_send_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WelcomeSendResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/unlock/status":{"get":{"tags":["unlock"],"summary":"Unlock Status","operationId":"unlock_status_api_unlock_status_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UnlockStatusResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/webhooks/clerk":{"post":{"tags":["webhooks"],"summary":"Clerk Webhook","operationId":"clerk_webhook_api_webhooks_clerk_post","parameters":[{"name":"svix-id","in":"header","required":true,"schema":{"type":"string","title":"Svix-Id"}},{"name":"svix-timestamp","in":"header","required":true,"schema":{"type":"string","title":"Svix-Timestamp"}},{"name":"svix-signature","in":"header","required":true,"schema":{"type":"string","title":"Svix-Signature"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/webhooks/stripe":{"post":{"tags":["webhooks"],"summary":"Stripe Webhook","operationId":"stripe_webhook_api_webhooks_stripe_post","parameters":[{"name":"stripe-signature","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Stripe-Signature"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":{"type":"string"},"title":"Response Stripe Webhook Api Webhooks Stripe Post"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/health":{"get":{"summary":"Health","description":"Liveness + readiness probe.\n\nAlways returns 200 if the process is up — Fly's health check uses\nthis to decide whether the machine is alive. The `db` field reports\n\"up\" when a trivial `SELECT 1` round-trips successfully against the\nprimary database, \"down\" otherwise. DB-down does NOT trip the 200 →\n503 boundary (per liveness semantics: process-up ≠ deps-up, and\nmachine restart wouldn't fix a downstream DB outage).","operationId":"health_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"additionalProperties":true,"type":"object","title":"Response Health Health Get"}}}}}}}},"components":{"schemas":{"ArtefactRef":{"properties":{"format":{"type":"string","enum":["xlsx","dxf","pdf"],"title":"Format"},"url":{"type":"string","minLength":1,"title":"Url","description":"Pre-signed URL"},"size_bytes":{"type":"integer","minimum":0.0,"title":"Size Bytes"},"expires_at_iso":{"type":"string","title":"Expires At Iso","description":"ISO 8601 UTC, e.g. 2026-05-02T00:00:00Z"}},"additionalProperties":false,"type":"object","required":["format","url","size_bytes","expires_at_iso"],"title":"ArtefactRef","description":"Signed-URL pointer to a rendered artefact in object storage.\n\nLifetime is bounded by `expires_at_iso` (FREE_MCP_DELIVERABLE_URL_TTL_HOURS for\nfree tier, longer for paid). Caller must download before expiry."},"AuditEntry":{"properties":{"id":{"type":"string","minLength":1,"title":"Id"},"boq_row_index":{"type":"integer","minimum":0.0,"title":"Boq Row Index"},"derivation":{"type":"string","minLength":1,"title":"Derivation"},"formula":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Formula"},"parameters_referenced":{"items":{"type":"string"},"type":"array","title":"Parameters Referenced"}},"additionalProperties":false,"type":"object","required":["id","boq_row_index","derivation"],"title":"AuditEntry","description":"Schema § 1.7 — paid-tier audit-trail entry.\n\nOne entry per BoQ row; `boq_row_index` ties to the row position in\n`ComputeResult.boq_rows`. `derivation` is the human-readable formula\nwalk-through; `formula` is the optional formal expression."},"BoQRow":{"properties":{"work_section":{"type":"string","minLength":1,"title":"Work Section"},"description":{"type":"string","minLength":1,"title":"Description"},"quantity":{"type":"number","minimum":0.0,"title":"Quantity"},"unit":{"type":"string","minLength":1,"title":"Unit"},"rate":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Rate"},"amount":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Amount"},"attribution":{"type":"string","minLength":1,"title":"Attribution"},"audit_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Audit Id"}},"additionalProperties":false,"type":"object","required":["work_section","description","quantity","unit","attribution"],"title":"BoQRow","description":"Schema § 1.3 — a single bill row in the requested standard's format.\n\n`description` is the non-verbatim functional description per spec § 13.9 /\n§ 15 invariant — NEVER reproduces a standard's verbatim clause text.\n`rate` / `amount` ship null because rates are commodity data the customer\nowns; the engine's job is the measurement, not the pricing."},"BoQRowRef":{"properties":{"code":{"type":"string","title":"Code"},"description":{"type":"string","title":"Description"},"unit":{"type":"string","title":"Unit"},"quantity":{"type":"number","minimum":0.0,"title":"Quantity"},"rate":{"type":"number","minimum":0.0,"title":"Rate","default":0.0},"total":{"type":"number","minimum":0.0,"title":"Total","default":0.0},"source_assembly":{"type":"string","title":"Source Assembly"},"source_element":{"type":"string","title":"Source Element"},"nrm1_group_element":{"anyOf":[{"type":"integer","maximum":8.0,"minimum":0.0},{"type":"null"}],"title":"Nrm1 Group Element"},"nrm1_element":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Nrm1 Element"},"nrm1_sub_element":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Nrm1 Sub Element"}},"additionalProperties":false,"type":"object","required":["code","description","unit","quantity","source_assembly","source_element"],"title":"BoQRowRef","description":"Single bill row in API response shape — mirrors BoQItem dataclass.\n\nNRM 1 cross-standard codification per spec §5a invariant (always\npopulated post-2.4). NRM 2 / NRM 3 codification fields were\naspirational Build-Claude additions that did not survive the\nchat-Claude v49 engine swap (Step 1, engine-swap-v49); the v49\nengine carries only the NRM 1 triple."},"CalculationStepRef":{"properties":{"label":{"type":"string","title":"Label"},"formula":{"type":"string","title":"Formula"},"inputs":{"additionalProperties":true,"type":"object","title":"Inputs"},"result":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Result"},"unit":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Unit"}},"additionalProperties":false,"type":"object","required":["label","formula"],"title":"CalculationStepRef","description":"A single derivation step within a WorkItem's audit trail.\n\nMirrors `civilquants.core.types.CalculationStep` — label, formula,\ninput values, result, unit. Surfaced to the workspace Workings ·\nAudit Trail panel so the QS verifying engine numbers can read the\nsame derivation that lands in the per-row backup sheets of the\nXLSX export (CLAUDE.md pin 2 / spec §15 invariant: every quantity\nis traceable)."},"CataloguePayload":{"properties":{"l1":{"items":{"$ref":"#/components/schemas/_CatalogueL1"},"type":"array","title":"L1"},"assembly_count":{"type":"integer","title":"Assembly Count"},"l1_count":{"type":"integer","title":"L1 Count"}},"additionalProperties":false,"type":"object","required":["l1","assembly_count","l1_count"],"title":"CataloguePayload","description":"Picker hierarchy payload returned by ``GET /api/v1/mcp/catalogue``.\n\nWrapped inside ``MCPResponse[CataloguePayload]`` per schema § 0.1.\nPopulated via ``get_category_tree(include_empty=False)`` per launch\nspec § 5.2 step 8 — only L1/L2 leaves with at least one registered\nassembly are returned. The launch-readiness audit variant\n(``include_empty=True``) ships behind the Track B-Resources URI\n``civilquants://catalogue`` per spec § 3.5."},"CheckoutPass7DayRequest":{"properties":{"email":{"type":"string","format":"email","title":"Email"},"currency":{"anyOf":[{"$ref":"#/components/schemas/Currency"},{"type":"null"}]}},"type":"object","required":["email"],"title":"CheckoutPass7DayRequest"},"CheckoutPass7DayResponse":{"properties":{"checkout_url":{"type":"string","title":"Checkout Url"},"session_id":{"type":"string","title":"Session Id"}},"type":"object","required":["checkout_url","session_id"],"title":"CheckoutPass7DayResponse"},"CheckoutSubscriptionRequest":{"properties":{"tier":{"type":"string","enum":["solo","studio","practice"],"title":"Tier"},"billing_cycle":{"type":"string","enum":["monthly","annual"],"title":"Billing Cycle"},"currency":{"anyOf":[{"$ref":"#/components/schemas/Currency"},{"type":"null"}]},"coupon":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Coupon"}},"type":"object","required":["tier","billing_cycle"],"title":"CheckoutSubscriptionRequest","description":"Body for POST /api/billing/subscription/checkout.\n\nPydantic Literal types reject any tier / billing_cycle outside the\nlocked v1 set with a 422. The request schema deliberately does NOT\naccept `clerk_user_id` / `client_reference_id` / `customer_id` —\nthose are server-side fields derived from the JWT (Codex P3.3.g\nHIGH-2 trust-anchor discipline)."},"CheckoutSubscriptionResponse":{"properties":{"checkout_url":{"type":"string","title":"Checkout Url"},"session_id":{"type":"string","title":"Session Id"}},"type":"object","required":["checkout_url","session_id"],"title":"CheckoutSubscriptionResponse"},"ComputeMetadata":{"properties":{"assembly_slug":{"type":"string","title":"Assembly Slug"},"assembly_version":{"type":"string","title":"Assembly Version"},"standard_code":{"type":"string","enum":["CESMM4","NRM2","SMM7","MMHW"],"title":"Standard Code"},"hash_cell":{"type":"string","maxLength":6,"minLength":6,"pattern":"^[0-9a-f]{6}$","title":"Hash Cell","description":"6-char lowercase hex from civilquants.core.hash_cell"},"bill_total_lines":{"type":"integer","minimum":0.0,"title":"Bill Total Lines"},"export_gate_enabled":{"type":"boolean","title":"Export Gate Enabled"}},"additionalProperties":false,"type":"object","required":["assembly_slug","assembly_version","standard_code","hash_cell","bill_total_lines","export_gate_enabled"],"title":"ComputeMetadata","description":"Envelope: assembly + version + chosen standard + hash cell + export gate.\n\n`hash_cell` is the 6-char hex from civilquants.core.hash_cell — the cross-\nreference between this API response, the rendered artefacts, and any email\nor workspace UI referencing the same parameter set.\n\n`export_gate_enabled` reflects server-side EXPORT_GATE_<STANDARD>_ENABLED env\nvar per spec §15 invariant — UI never displays \"this export would be allowed\nif X\" client-side state. If false, signed URLs are NOT included in the response."},"ComputeResult":{"properties":{"assembly_slug":{"type":"string","minLength":1,"title":"Assembly Slug"},"standard":{"type":"string","enum":["CESMM4","NRM2","SMM7","MMHW"],"title":"Standard"},"parameters_used":{"$ref":"#/components/schemas/ParametersUsed"},"boq_rows":{"items":{"$ref":"#/components/schemas/BoQRow"},"type":"array","title":"Boq Rows"},"totals_summary":{"$ref":"#/components/schemas/TotalsSummary"},"text_summary":{"type":"string","maxLength":2000,"minLength":1,"title":"Text Summary"},"drawing":{"anyOf":[{"$ref":"#/components/schemas/DrawingPreview"},{"type":"null"}]},"drawings":{"anyOf":[{"items":{"$ref":"#/components/schemas/DrawingReference"},"type":"array"},{"type":"null"}],"title":"Drawings"},"audit_trail":{"anyOf":[{"items":{"$ref":"#/components/schemas/AuditEntry"},"type":"array"},{"type":"null"}],"title":"Audit Trail"},"warnings":{"items":{"$ref":"#/components/schemas/ComputeWarning"},"type":"array","title":"Warnings"}},"additionalProperties":false,"type":"object","required":["assembly_slug","standard","parameters_used","boq_rows","totals_summary","text_summary"],"title":"ComputeResult","description":"Schema § 1.1 — the typed payload returned inside `MCPResponse[ComputeResult]`.\n\nFree-tier responses omit `drawings` (the downloadable CAD artefact\npointers — the paid differentiator); the bridge enforces this. The\n`audit_trail` and the inline `drawing` preview are present on BOTH tiers\n(V1 ALIGN / 60th-fire D + C). `text_summary` is bounded to 2000 chars —\nkeeps the LLM-facing summary token-cheap and forces longer narrative into\nBoQ row descriptions where it belongs. `warnings` is the non-fatal\ngeometry advisory list (both tiers)."},"ComputeWarning":{"properties":{"code":{"type":"string","minLength":1,"title":"Code"},"severity":{"type":"string","enum":["info","advisory","caution"],"title":"Severity"},"message":{"type":"string","minLength":1,"title":"Message"},"affected_params":{"items":{"type":"string"},"type":"array","title":"Affected Params"},"detail":{"additionalProperties":{"type":"number"},"type":"object","title":"Detail"}},"additionalProperties":false,"type":"object","required":["code","severity","message"],"title":"ComputeWarning","description":"Schema § 1.8 — a non-fatal geometry advisory (56th-fire T2).\n\nThe wire projection of the engine's `core.GeometryWarning`. Advisory only:\na warning NEVER changes a measured quantity — it flags geometry that is\nvalid but likely a mistake (e.g. a stale dependent-height default). Present\non BOTH tiers — a correctness/safety signal, not a paid feature."},"Currency":{"type":"string","enum":["GBP","USD","EUR","AUD","NZD","SGD","CAD","MYR"],"title":"Currency"},"DeliverableURLs":{"properties":{"pdf_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pdf Url"},"xlsx_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Xlsx Url"},"dxf_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Dxf Url"},"docx_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Docx Url"},"expires_at":{"type":"string","minLength":1,"title":"Expires At"}},"additionalProperties":false,"type":"object","required":["expires_at"],"title":"DeliverableURLs","description":"Canonical schema § 0.3 — R2 signed URLs (24h TTL)."},"DependentHeightLink":{"properties":{"primary_field":{"type":"string","title":"Primary Field"},"dependent_field":{"type":"string","title":"Dependent Field"}},"additionalProperties":false,"type":"object","required":["primary_field","dependent_field"],"title":"DependentHeightLink","description":"The engine's stem→retained-height relationship for an assembly (mirrors\n``DEPENDENT_HEIGHT_RULE``). Exposed so a client can offer a freeboard\naffordance over it (60th-fire item M2). Named after the engine relationship,\nnot after freeboard — the schema states what is true about the assembly; the\nclient decides to render a freeboard control on top. ``None`` when the\nassembly has no such relationship."},"DrawingPreview":{"properties":{"svg_b64":{"type":"string","minLength":1,"title":"Svg B64"},"viewbox_x":{"type":"number","title":"Viewbox X"},"viewbox_y":{"type":"number","title":"Viewbox Y"},"viewbox_width":{"type":"number","exclusiveMinimum":0.0,"title":"Viewbox Width"},"viewbox_height":{"type":"number","exclusiveMinimum":0.0,"title":"Viewbox Height"},"entity_count":{"type":"integer","minimum":0.0,"title":"Entity Count"},"unit_of_drawing":{"type":"string","const":"m","title":"Unit Of Drawing","default":"m"}},"additionalProperties":false,"type":"object","required":["svg_b64","viewbox_x","viewbox_y","viewbox_width","viewbox_height","entity_count"],"title":"DrawingPreview","description":"Schema § 1.6a — inline server-rendered SVG preview of the geometry.\n\nThe MCP mirror of the web ``/compute`` response's ``drawing`` field\n(``api.schemas.compute.DrawingRef``): the same on-screen dimensioned\ndrawing both tiers see in the workspace, surfaced on the MCP compute\nresponse so a chat client can render it inline (60th-fire item C). This\nis the on-screen preview — present on BOTH tiers per the free/paid\nboundary; the downloadable CAD artefacts (DXF / dimensioned PDF) remain\nthe paid differentiator via the export tools.\n\nDefined here rather than importing ``DrawingRef`` because the web\n``schemas`` package imports back into this module (``schemas.render``),\nso reusing it would create an import cycle — the MCP wire contract keeps\nits own copy, the same pattern as ``BoQRow`` / ``DrawingReference``."},"DrawingRef":{"properties":{"svg_b64":{"type":"string","minLength":1,"title":"Svg B64","description":"Base64-encoded SVG document"},"viewbox_x":{"type":"number","title":"Viewbox X","description":"SVG viewBox x in engine units (m)"},"viewbox_y":{"type":"number","title":"Viewbox Y","description":"SVG viewBox y in engine units (m)"},"viewbox_width":{"type":"number","exclusiveMinimum":0.0,"title":"Viewbox Width","description":"SVG viewBox width in engine units (m)"},"viewbox_height":{"type":"number","exclusiveMinimum":0.0,"title":"Viewbox Height","description":"SVG viewBox height in engine units (m)"},"entity_count":{"type":"integer","minimum":0.0,"title":"Entity Count","description":"Number of DrawEntities rendered"},"unit_of_drawing":{"type":"string","const":"m","title":"Unit Of Drawing","description":"Engine geometry is in metres. Surfaced here so the client never has to guess; if a future engine switches to mm this becomes the discriminator.","default":"m"}},"additionalProperties":false,"type":"object","required":["svg_b64","viewbox_x","viewbox_y","viewbox_width","viewbox_height","entity_count"],"title":"DrawingRef","description":"Server-rendered SVG preview of the assembly geometry.\n\nPer G.6.3 / STATUS.md decision 6 (2026-05-19): one source of\ngeometry (`DrawEntity` list emitted by `Assembly.compute`), two\noutput paths (this SVG for inline preview, the `GET .../dxf`\nendpoint for the real CAD download). The browser displays\n`svg_b64` decoded inline; `viewbox` drives client zoom/pan\ncontrols without re-parsing the SVG.\n\n`svg_b64` is base64 of a UTF-8 SVG document with one `<g\nclass=\"cq-layer cq-layer-<name>\">` per layer. Styling defaults are\ninlined so the preview renders without an external stylesheet;\nfront-end CSS may override via the class names."},"DrawingReference":{"properties":{"drawing_type":{"type":"string","enum":["section","plan","elevation","isometric","detail"],"title":"Drawing Type"},"format":{"type":"string","enum":["DXF","PDF","SVG"],"title":"Format"},"url":{"type":"string","minLength":1,"title":"Url"},"sheet_size":{"type":"string","enum":["A1","A2","A3","A4"],"title":"Sheet Size"},"scale":{"type":"string","minLength":1,"title":"Scale"},"layers":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Layers"}},"additionalProperties":false,"type":"object","required":["drawing_type","format","url","sheet_size","scale"],"title":"DrawingReference","description":"Schema § 1.6 — paid-tier drawing artefact pointer.\n\n`url` is an R2-signed URL; expiry is carried in `_meta.deliverables.expires_at`\non the envelope (not duplicated here). `layers` is DXF-only; PDF/SVG\ncallers omit it."},"ErrorCode":{"type":"string","enum":["RATE_LIMIT_EXCEEDED","TIER_INSUFFICIENT","INVALID_INPUT","SCHEMA_VERSION_MISMATCH","SCHEMA_INVARIANT_VIOLATION","UPSTREAM_TIMEOUT","BACKEND_FAILURE","IP_BLOCKED","RESOURCE_NOT_FOUND","RENDER_FAILURE","OCR_LOW_CONFIDENCE","MANUAL_REVIEW_REQUIRED","ACCEPTANCE_REQUIRED","INVALID_TOKEN"],"title":"ErrorCode","description":"Canonical schema § 0.5 catalogue.\n\nEach member maps to a documented `recoverable` flag (see\n`IS_RECOVERABLE`) — clients use the flag to decide retry vs surface."},"ErrorEnvelope":{"properties":{"code":{"$ref":"#/components/schemas/ErrorCode"},"message":{"type":"string","minLength":1,"title":"Message"},"recoverable":{"type":"boolean","title":"Recoverable"},"retry_after_seconds":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Retry After Seconds"},"details":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Details"}},"additionalProperties":false,"type":"object","required":["code","message","recoverable"],"title":"ErrorEnvelope","description":"Canonical schema § 0.4 — error payload returned in `MCPResponse.error`."},"FieldSpec":{"properties":{"name":{"type":"string","minLength":1,"title":"Name"},"label":{"type":"string","minLength":1,"title":"Label"},"default":{"anyOf":[{"type":"number"},{"type":"integer"},{"type":"string"},{"type":"boolean"},{"type":"null"}],"title":"Default"},"group":{"type":"string","minLength":1,"title":"Group"},"unit":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Unit"},"min":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Min"},"max":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Max"},"help_text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Help Text"},"is_run_length":{"type":"boolean","title":"Is Run Length","default":false},"load_bearing":{"type":"boolean","title":"Load Bearing","default":false}},"additionalProperties":false,"type":"object","required":["name","label","default","group"],"title":"FieldSpec","description":"One Parameters field, with the metadata a caller fills a value from."},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"HealthPayload":{"properties":{"status":{"type":"string","title":"Status"},"component":{"type":"string","title":"Component"},"fastmcp_version":{"type":"string","title":"Fastmcp Version"},"phase":{"type":"string","title":"Phase"}},"additionalProperties":false,"type":"object","required":["status","component","fastmcp_version","phase"],"title":"HealthPayload","description":"Liveness data shape — `MCPResponse[HealthPayload]` is the wire shape."},"MCPResponse_Any_":{"properties":{"data":{"title":"Data"},"error":{"anyOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"type":"null"}]},"_meta":{"$ref":"#/components/schemas/ResponseMeta"}},"additionalProperties":false,"type":"object","required":["_meta"],"title":"MCPResponse[Any]"},"MCPResponse_CataloguePayload_":{"properties":{"data":{"anyOf":[{"$ref":"#/components/schemas/CataloguePayload"},{"type":"null"}]},"error":{"anyOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"type":"null"}]},"_meta":{"$ref":"#/components/schemas/ResponseMeta"}},"additionalProperties":false,"type":"object","required":["_meta"],"title":"MCPResponse[CataloguePayload]"},"MCPResponse_ComputeResult_":{"properties":{"data":{"anyOf":[{"$ref":"#/components/schemas/ComputeResult"},{"type":"null"}]},"error":{"anyOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"type":"null"}]},"_meta":{"$ref":"#/components/schemas/ResponseMeta"}},"additionalProperties":false,"type":"object","required":["_meta"],"title":"MCPResponse[ComputeResult]"},"MCPResponse_HealthPayload_":{"properties":{"data":{"anyOf":[{"$ref":"#/components/schemas/HealthPayload"},{"type":"null"}]},"error":{"anyOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"type":"null"}]},"_meta":{"$ref":"#/components/schemas/ResponseMeta"}},"additionalProperties":false,"type":"object","required":["_meta"],"title":"MCPResponse[HealthPayload]"},"MCPResponse_MultiSectionResult_":{"properties":{"data":{"anyOf":[{"$ref":"#/components/schemas/MultiSectionResult"},{"type":"null"}]},"error":{"anyOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"type":"null"}]},"_meta":{"$ref":"#/components/schemas/ResponseMeta"}},"additionalProperties":false,"type":"object","required":["_meta"],"title":"MCPResponse[MultiSectionResult]"},"MCPResponse_RenderResult_":{"properties":{"data":{"anyOf":[{"$ref":"#/components/schemas/RenderResult"},{"type":"null"}]},"error":{"anyOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"type":"null"}]},"_meta":{"$ref":"#/components/schemas/ResponseMeta"}},"additionalProperties":false,"type":"object","required":["_meta"],"title":"MCPResponse[RenderResult]"},"MCPResponse_ResolveResult_":{"properties":{"data":{"anyOf":[{"$ref":"#/components/schemas/ResolveResult"},{"type":"null"}]},"error":{"anyOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"type":"null"}]},"_meta":{"$ref":"#/components/schemas/ResponseMeta"}},"additionalProperties":false,"type":"object","required":["_meta"],"title":"MCPResponse[ResolveResult]"},"MCPResponse_ResourceListPayload_":{"properties":{"data":{"anyOf":[{"$ref":"#/components/schemas/ResourceListPayload"},{"type":"null"}]},"error":{"anyOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"type":"null"}]},"_meta":{"$ref":"#/components/schemas/ResponseMeta"}},"additionalProperties":false,"type":"object","required":["_meta"],"title":"MCPResponse[ResourceListPayload]"},"MCPResponse_ResourcePayload_":{"properties":{"data":{"anyOf":[{"$ref":"#/components/schemas/ResourcePayload"},{"type":"null"}]},"error":{"anyOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"type":"null"}]},"_meta":{"$ref":"#/components/schemas/ResponseMeta"}},"additionalProperties":false,"type":"object","required":["_meta"],"title":"MCPResponse[ResourcePayload]"},"MCPResponse__AddAssemblyResponse_":{"properties":{"data":{"anyOf":[{"$ref":"#/components/schemas/_AddAssemblyResponse"},{"type":"null"}]},"error":{"anyOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"type":"null"}]},"_meta":{"$ref":"#/components/schemas/ResponseMeta"}},"additionalProperties":false,"type":"object","required":["_meta"],"title":"MCPResponse[_AddAssemblyResponse]"},"MCPResponse__AssemblyDeleteResponse_":{"properties":{"data":{"anyOf":[{"$ref":"#/components/schemas/_AssemblyDeleteResponse"},{"type":"null"}]},"error":{"anyOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"type":"null"}]},"_meta":{"$ref":"#/components/schemas/ResponseMeta"}},"additionalProperties":false,"type":"object","required":["_meta"],"title":"MCPResponse[_AssemblyDeleteResponse]"},"MCPResponse__ProjectDeleteResponse_":{"properties":{"data":{"anyOf":[{"$ref":"#/components/schemas/_ProjectDeleteResponse"},{"type":"null"}]},"error":{"anyOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"type":"null"}]},"_meta":{"$ref":"#/components/schemas/ResponseMeta"}},"additionalProperties":false,"type":"object","required":["_meta"],"title":"MCPResponse[_ProjectDeleteResponse]"},"MCPResponse__ProjectDxfBundleResponse_":{"properties":{"data":{"anyOf":[{"$ref":"#/components/schemas/_ProjectDxfBundleResponse"},{"type":"null"}]},"error":{"anyOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"type":"null"}]},"_meta":{"$ref":"#/components/schemas/ResponseMeta"}},"additionalProperties":false,"type":"object","required":["_meta"],"title":"MCPResponse[_ProjectDxfBundleResponse]"},"MCPResponse__ProjectExcelResponse_":{"properties":{"data":{"anyOf":[{"$ref":"#/components/schemas/_ProjectExcelResponse"},{"type":"null"}]},"error":{"anyOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"type":"null"}]},"_meta":{"$ref":"#/components/schemas/ResponseMeta"}},"additionalProperties":false,"type":"object","required":["_meta"],"title":"MCPResponse[_ProjectExcelResponse]"},"MCPResponse__ProjectFetchResponse_":{"properties":{"data":{"anyOf":[{"$ref":"#/components/schemas/_ProjectFetchResponse"},{"type":"null"}]},"error":{"anyOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"type":"null"}]},"_meta":{"$ref":"#/components/schemas/ResponseMeta"}},"additionalProperties":false,"type":"object","required":["_meta"],"title":"MCPResponse[_ProjectFetchResponse]"},"MCPResponse__ProjectListResponse_":{"properties":{"data":{"anyOf":[{"$ref":"#/components/schemas/_ProjectListResponse"},{"type":"null"}]},"error":{"anyOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"type":"null"}]},"_meta":{"$ref":"#/components/schemas/ResponseMeta"}},"additionalProperties":false,"type":"object","required":["_meta"],"title":"MCPResponse[_ProjectListResponse]"},"MCPResponse__ProjectPdfBundleResponse_":{"properties":{"data":{"anyOf":[{"$ref":"#/components/schemas/_ProjectPdfBundleResponse"},{"type":"null"}]},"error":{"anyOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"type":"null"}]},"_meta":{"$ref":"#/components/schemas/ResponseMeta"}},"additionalProperties":false,"type":"object","required":["_meta"],"title":"MCPResponse[_ProjectPdfBundleResponse]"},"MCPResponse__ProjectRecomputeResponse_":{"properties":{"data":{"anyOf":[{"$ref":"#/components/schemas/_ProjectRecomputeResponse"},{"type":"null"}]},"error":{"anyOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"type":"null"}]},"_meta":{"$ref":"#/components/schemas/ResponseMeta"}},"additionalProperties":false,"type":"object","required":["_meta"],"title":"MCPResponse[_ProjectRecomputeResponse]"},"MCPResponse__ProjectSaveResponse_":{"properties":{"data":{"anyOf":[{"$ref":"#/components/schemas/_ProjectSaveResponse"},{"type":"null"}]},"error":{"anyOf":[{"$ref":"#/components/schemas/ErrorEnvelope"},{"type":"null"}]},"_meta":{"$ref":"#/components/schemas/ResponseMeta"}},"additionalProperties":false,"type":"object","required":["_meta"],"title":"MCPResponse[_ProjectSaveResponse]"},"MatchTrace":{"properties":{"slug":{"type":"string","title":"Slug"},"score":{"type":"number","title":"Score"},"matched_terms":{"items":{"type":"string"},"type":"array","title":"Matched Terms"}},"additionalProperties":false,"type":"object","required":["slug","score"],"title":"MatchTrace","description":"One row of the optional resolution log (return_resolution_log)."},"MultiSectionResult":{"properties":{"assembly_slug":{"type":"string","minLength":1,"title":"Assembly Slug"},"standard":{"type":"string","enum":["CESMM4","NRM2","SMM7","MMHW"],"title":"Standard"},"section_count":{"type":"integer","minimum":1.0,"title":"Section Count"},"section_summary":{"items":{"$ref":"#/components/schemas/SectionSummary"},"type":"array","title":"Section Summary"},"aggregated_boq_rows":{"items":{"$ref":"#/components/schemas/BoQRow"},"type":"array","title":"Aggregated Boq Rows"},"section_results":{"anyOf":[{"items":{"$ref":"#/components/schemas/ComputeResult"},"type":"array"},{"type":"null"}],"title":"Section Results"},"warnings":{"items":{"$ref":"#/components/schemas/SectionWarning"},"type":"array","title":"Warnings"},"parameters_used":{"additionalProperties":true,"type":"object","title":"Parameters Used"}},"additionalProperties":false,"type":"object","required":["assembly_slug","standard","section_count","section_summary","aggregated_boq_rows"],"title":"MultiSectionResult","description":"Aggregated + per-section result for a sectioned assembly run."},"OutputModeInfo":{"properties":{"mode":{"type":"string","enum":["summary","material_schedule","boq","boq_with_backup"],"title":"Mode"},"omitted_fields":{"items":{"type":"string"},"type":"array","minItems":1,"title":"Omitted Fields"},"retrieve_with":{"type":"string","minLength":1,"title":"Retrieve With"}},"additionalProperties":false,"type":"object","required":["mode","omitted_fields","retrieve_with"],"title":"OutputModeInfo","description":"Surfaces what a trimmed ``output_mode`` withheld from ``data``.\n\nA trimmed mode (``summary`` / ``material_schedule`` / ``boq`` /\n``boq_with_backup``) withholds\nalready-computed fields to keep the chat payload small. The withheld value\nreads to a client agent as \"capability missing\" (the box-culvert \"geometry\nbuilder isn't firing\" scare) rather than \"pared for size\": ``drawing`` and\n``audit_trail`` appear as ``null``, ``boq_rows`` as an empty list ``[]``\n(its type forbids ``null``) — both indistinguishable from a genuine absent\nresult. This names the withheld fields and the exact mode that brings each\nback, so the value is unambiguous. Absent on the default ``full_json`` mode\n— nothing is withheld, nothing to point at."},"ParameterFieldSchema":{"properties":{"key":{"type":"string","title":"Key","description":"Dataclass field name; the Compute request key"},"label":{"type":"string","title":"Label","description":"Human-readable display name"},"unit":{"type":"string","title":"Unit","description":"Engineering unit (m, m³, kg, mm, no, etc.)"},"type":{"type":"string","enum":["number","integer","boolean","string","array"],"title":"Type"},"default":{"anyOf":[{"type":"number"},{"type":"integer"},{"type":"boolean"},{"type":"string"},{"items":{"type":"number"},"type":"array"},{"type":"null"}],"title":"Default","description":"Scalar default for slider / select / text inputs. `list[float]` supports tuple-of-floats Parameters fields (e.g. anchor row levels) — surfaced as a comma-separated text input when bespoke=True. Array-typed fields (list-of-object Parameters like the services bundle) always default to an empty list engine-side; their starter rows come from the workspace default overrides, so `default` stays null here."},"min":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Min"},"max":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Max"},"step":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Step"},"choices":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Choices","description":"Enum values for select dropdowns; null for free-typed inputs"},"bespoke":{"type":"boolean","title":"Bespoke","description":"UI shows a free-typed override alongside a slider","default":false},"help_text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Help Text"},"item_fields":{"anyOf":[{"items":{"$ref":"#/components/schemas/ParameterFieldSchema"},"type":"array"},{"type":"null"}],"title":"Item Fields","description":"For type='array' only: the per-row field schemas of the list's item object (one entry per dataclass field of the item type). Drives the workspace row editor — each row renders one input per item field. Null for scalar parameters."}},"additionalProperties":false,"type":"object","required":["key","label","unit","type"],"title":"ParameterFieldSchema","description":"One input on a workspace form. Mirrors `ParameterMeta` engine-side plus\ntype + default lifted from the dataclass field annotation."},"ParameterGroupSchema":{"properties":{"key":{"type":"string","title":"Key"},"fields":{"items":{"$ref":"#/components/schemas/ParameterFieldSchema"},"type":"array","title":"Fields"}},"additionalProperties":false,"type":"object","required":["key","fields"],"title":"ParameterGroupSchema","description":"A collapsible section in the workspace form (geometry / materials /\nexcavation etc.). Order of `fields` is the order they're declared on the\nParameters dataclass; order of groups is the order they first appear."},"ParametersUsed":{"properties":{"user_supplied":{"additionalProperties":true,"type":"object","title":"User Supplied"},"engine_defaults":{"additionalProperties":true,"type":"object","title":"Engine Defaults"},"assumptions":{"items":{"type":"string"},"type":"array","title":"Assumptions"}},"additionalProperties":false,"type":"object","title":"ParametersUsed","description":"Schema § 1.2 — the parameter snapshot the engine actually used.\n\nSplits user-supplied values from engine defaults so customers can see\nwhich numbers came from them and which the engine filled in. Every\ndefaulted parameter SHOULD have a corresponding entry in `assumptions`\n— the bridge layer enforces this when wrapping engine output."},"PassCreditOffer":{"properties":{"amount_local":{"type":"number","title":"Amount Local"},"currency":{"type":"string","title":"Currency"},"redeemable_until":{"type":"string","title":"Redeemable Until"},"applies_to":{"type":"string","const":"solo_first_month","title":"Applies To","default":"solo_first_month"}},"additionalProperties":false,"type":"object","required":["amount_local","currency","redeemable_until"],"title":"PassCreditOffer","description":"Canonical schema § 0.7 — pass-credit upgrade hook."},"PortalSessionResponse":{"properties":{"portal_url":{"type":"string","title":"Portal Url"}},"type":"object","required":["portal_url"],"title":"PortalSessionResponse"},"ProjectMeta":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Name","description":"Free-text project name, e.g. \"Phase 2 Drainage Works\"."},"reference":{"anyOf":[{"type":"string","maxLength":80},{"type":"null"}],"title":"Reference","description":"Free-text project reference, e.g. \"PRJ-2026-042\"."}},"additionalProperties":false,"type":"object","title":"ProjectMeta","description":"Option C free-text Project entity per 25th-fire (filed 2026-05-19,\nin-scope for the architectural rewrite per INBOX 26th-fire Amendment C).\n\nThin-slice precursor to a full Project entity in v1.1: just two\noptional free-text fields that the workspace surfaces at the top of\nevery assembly form and that propagate to the BoQ header, the PDF\ncover sheet, and the DXF title block. No DB persistence, no\ncross-assembly rollup — those land in v1.1."},"RenderRequest":{"properties":{"skill":{"type":"string","title":"Skill","description":"One of ['civilquants/qs_boq_export', 'civilquants/qs_boq_word_report']. Word skill ('civilquants/qs_boq_word_report') is paid-only; Excel skill is open to free callers with sheet protection + watermark."},"compute_results":{"items":{"$ref":"#/components/schemas/ComputeResult"},"type":"array","maxItems":100,"minItems":1,"title":"Compute Results","description":"Up to 100 ComputeResult entries per call — comfortably above the 46-assembly v1 catalogue. Caps in-memory render at a sane ceiling to defend against denial-of-wallet from a compromised paid token."},"options":{"$ref":"#/components/schemas/_RenderRequestOptions"},"personalisation":{"$ref":"#/components/schemas/_PersonalisationFields"}},"additionalProperties":false,"type":"object","required":["skill","compute_results"],"title":"RenderRequest","description":"Wire shape for POST /api/v1/mcp/render."},"RenderResult":{"properties":{"xlsx_b64":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Xlsx B64","description":"Base64-encoded .xlsx workbook bytes (Excel skill, local store mode only)."},"docx_b64":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Docx B64","description":"Base64-encoded .docx document bytes (Word skill, local store mode only)."},"xlsx_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Xlsx Url","description":"Pre-signed R2 GET URL for .xlsx (Excel skill, R2 store mode)."},"docx_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Docx Url","description":"Pre-signed R2 GET URL for .docx (Word skill, R2 store mode)."},"pdf_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pdf Url","description":"Reserved for a future PDF skill backend."},"watermark_state":{"type":"string","enum":["free","none"],"title":"Watermark State","description":"`free` when the free-tier sheet protection + watermark is applied; `none` for paid output."},"byte_size":{"type":"integer","minimum":0.0,"title":"Byte Size","description":"Decoded size of whichever deliverable was rendered."},"skill":{"type":"string","title":"Skill","description":"Echoes the resolved skill slug."}},"additionalProperties":false,"type":"object","required":["watermark_state","byte_size","skill"],"title":"RenderResult","description":"Wire response shape — schema § 4 RenderResult.\n\nPer Phase 3.4 A.3: each skill has TWO possible delivery modes that\nmap to ``CIVILQUANTS_ARTEFACT_STORE``:\n  * ``local`` (dev default) — base64-inline in ``xlsx_b64`` / ``docx_b64``\n  * ``r2`` (production) — pre-signed URL in ``xlsx_url`` / ``docx_url``\nThe validator enforces \"exactly one delivery channel per skill\" — a\nsingle render call returns exactly one of {b64, url} for its target\nformat, never both. The other skill's fields stay null."},"ResolveResult":{"properties":{"query":{"type":"string","title":"Query"},"preferred_standard":{"anyOf":[{"type":"string","enum":["CESMM4","NRM2","SMM7","MMHW"]},{"type":"null"}],"title":"Preferred Standard"},"candidates":{"items":{"$ref":"#/components/schemas/ResolvedAssembly"},"type":"array","title":"Candidates"},"confidence":{"type":"string","enum":["high","medium","low"],"title":"Confidence"},"questions":{"items":{"type":"string"},"type":"array","title":"Questions"},"resolution_log":{"anyOf":[{"items":{"$ref":"#/components/schemas/MatchTrace"},"type":"array"},{"type":"null"}],"title":"Resolution Log"}},"additionalProperties":false,"type":"object","required":["query","confidence"],"title":"ResolveResult","description":"The typed payload wrapped in MCPResponse[ResolveResult]."},"ResolvedAssembly":{"properties":{"slug":{"type":"string","minLength":1,"title":"Slug"},"display_name":{"type":"string","minLength":1,"title":"Display Name"},"tier":{"type":"string","enum":["free","paid"],"title":"Tier"},"category_l1":{"type":"string","title":"Category L1"},"category_l2":{"type":"string","title":"Category L2"},"compute_tool":{"type":"string","minLength":1,"title":"Compute Tool"},"supports_multi_section":{"type":"boolean","title":"Supports Multi Section","default":false},"multi_section_tool":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Multi Section Tool"},"standard_note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Standard Note"},"free_alternative":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Free Alternative"},"fields":{"items":{"$ref":"#/components/schemas/FieldSpec"},"type":"array","title":"Fields"},"suggested_params":{"items":{"$ref":"#/components/schemas/SuggestedParam"},"type":"array","title":"Suggested Params"}},"additionalProperties":false,"type":"object","required":["slug","display_name","tier","category_l1","category_l2","compute_tool"],"title":"ResolvedAssembly","description":"A candidate assembly the description routed to."},"ResourceListPayload":{"properties":{"resources":{"items":{"$ref":"#/components/schemas/_ResourceEntry"},"type":"array","title":"Resources"}},"additionalProperties":false,"type":"object","required":["resources"],"title":"ResourceListPayload","description":"List endpoint payload."},"ResourcePayload":{"properties":{"uri":{"type":"string","title":"Uri","description":"Echoes the requested URI."},"mime":{"type":"string","title":"Mime","description":"MIME type — clients render accordingly."},"content":{"type":"string","title":"Content","description":"Resource body as a string."}},"additionalProperties":false,"type":"object","required":["uri","mime","content"],"title":"ResourcePayload","description":"Read endpoint payload."},"ResponseMeta":{"properties":{"schema_version":{"type":"string","const":"1.0.0","title":"Schema Version","default":"1.0.0"},"request_id":{"type":"string","minLength":1,"title":"Request Id"},"issued_at":{"type":"string","minLength":1,"title":"Issued At"},"tier":{"type":"string","enum":["free","paid"],"title":"Tier"},"document_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Document Id"},"deliverables":{"anyOf":[{"$ref":"#/components/schemas/DeliverableURLs"},{"type":"null"}]},"rate_limit_remaining":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Rate Limit Remaining"},"upgrade_hint":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Upgrade Hint"},"tos_url":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tos Url"},"warranty_disclaimer":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Warranty Disclaimer"},"scope_boundary":{"anyOf":[{"$ref":"#/components/schemas/ScopeBoundary"},{"type":"null"}]},"tier_upgrade":{"anyOf":[{"$ref":"#/components/schemas/TierUpgrade"},{"type":"null"}]},"hint":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Hint"},"output_mode":{"anyOf":[{"$ref":"#/components/schemas/OutputModeInfo"},{"type":"null"}]}},"additionalProperties":false,"type":"object","required":["request_id","issued_at","tier"],"title":"ResponseMeta","description":"Canonical schema § 0.2 — present on every MCPResponse.\n\nRequired fields: `schema_version`, `request_id`, `issued_at`, `tier`.\nThe free-tier-only fields (`upgrade_hint`, `tos_url`, `warranty_disclaimer`,\n`tier_upgrade`, `rate_limit_remaining`) are populated by the middleware\nwhen `tier == \"free\"`; absent on paid-tier responses."},"ScopeBoundary":{"properties":{"produced":{"items":{"type":"string"},"type":"array","title":"Produced"},"not_in_scope":{"items":{"type":"string"},"type":"array","title":"Not In Scope"},"recommended_companions":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Recommended Companions"}},"additionalProperties":false,"type":"object","required":["produced","not_in_scope"],"title":"ScopeBoundary","description":"Canonical schema § 0.6 — surfaces what this assembly produces vs not."},"SectionSummary":{"properties":{"label":{"type":"string","minLength":1,"title":"Label"},"run_length":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Run Length"},"totals_summary":{"$ref":"#/components/schemas/TotalsSummary"}},"additionalProperties":false,"type":"object","required":["label","totals_summary"],"title":"SectionSummary","description":"Cheap always-on per-section line: label + run length + totals."},"SectionWarning":{"properties":{"section_label":{"type":"string","minLength":1,"title":"Section Label"},"warning":{"$ref":"#/components/schemas/ComputeWarning"}},"additionalProperties":false,"type":"object","required":["section_label","warning"],"title":"SectionWarning","description":"A section-labelled geometry/advisory warning."},"SubscriptionActiveResponse":{"properties":{"active":{"type":"boolean","title":"Active"}},"type":"object","required":["active"],"title":"SubscriptionActiveResponse"},"SubscriptionStatusResponse":{"properties":{"status":{"type":"string","enum":["ready","pending"],"title":"Status"},"tier":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Tier"},"subscription_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Subscription Status"},"current_period_end":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Current Period End"}},"type":"object","required":["status"],"title":"SubscriptionStatusResponse","description":"Polling response for the /welcome page.\n\n`status=\"ready\"` means the Subscription row exists in our DB for the\nauthenticated user; the page can flip from \"Confirming…\" to the\nwelcomed state. `status=\"pending\"` means the webhook hasn't yet\nlanded (or the row is orphaned, or the row belongs to another user\n— leakage-safe collapsing).\n\nThe endpoint deliberately does NOT echo customer-email, Stripe\ncustomer-id, or any field that would let a poll-and-iterate attacker\nlearn anything beyond \"you own this subscription_id.\""},"SuggestedParam":{"properties":{"name":{"type":"string","minLength":1,"title":"Name"},"value":{"type":"number","title":"Value"},"unit":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Unit"},"source":{"type":"string","minLength":1,"title":"Source"}},"additionalProperties":false,"type":"object","required":["name","value","source"],"title":"SuggestedParam","description":"A value the description stated with a clear anchor — a caller-confirmed\nsuggestion, NOT an authoritative value and never auto-fed to compute."},"TierOption":{"properties":{"tier_slug":{"type":"string","enum":["7_day_pass","solo","studio","practice"],"title":"Tier Slug"},"display_name":{"type":"string","title":"Display Name"},"price_local":{"type":"number","title":"Price Local"},"currency":{"type":"string","enum":["GBP","SGD","EUR","USD","CAD","AUD","NZD","MYR"],"title":"Currency"},"billing":{"type":"string","enum":["one_off","monthly","annual"],"title":"Billing"},"unlock_summary":{"type":"string","minLength":1,"title":"Unlock Summary"}},"additionalProperties":false,"type":"object","required":["tier_slug","display_name","price_local","currency","billing","unlock_summary"],"title":"TierOption","description":"Canonical schema § 0.7 — single tier in the upgrade carousel."},"TierUpgrade":{"properties":{"current_tier":{"type":"string","const":"free","title":"Current Tier","default":"free"},"available_tiers":{"items":{"$ref":"#/components/schemas/TierOption"},"type":"array","title":"Available Tiers"},"pricing_url":{"type":"string","title":"Pricing Url","default":"https://civilquants.com/pricing"},"pass_credit":{"anyOf":[{"$ref":"#/components/schemas/PassCreditOffer"},{"type":"null"}]}},"additionalProperties":false,"type":"object","required":["available_tiers"],"title":"TierUpgrade","description":"Canonical schema § 0.7 — present on free-tier responses only."},"TotalQuantity":{"properties":{"quantity":{"type":"number","minimum":0.0,"title":"Quantity"},"unit":{"type":"string","minLength":1,"title":"Unit"}},"additionalProperties":false,"type":"object","required":["quantity","unit"],"title":"TotalQuantity","description":"Schema § 1.4 — single material/work-class total entry."},"TotalsSummary":{"properties":{"entries":{"additionalProperties":{"$ref":"#/components/schemas/TotalQuantity"},"type":"object","title":"Entries"}},"additionalProperties":false,"type":"object","title":"TotalsSummary","description":"Schema § 1.4 — totals_summary as a typed wrapper around the free-form dict.\n\nWire shape is a flat object (`{\"concrete_m3\": {...}, \"rebar_t\": {...}}`).\nWe model it as `RootModel`-equivalent via `model_config(extra=\"allow\")`\n+ a single `__root__`-style `entries` dict. Pydantic v2 recommends\n`RootModel` for free-form roots; using a wrapper class keeps the import\nsurface uniform with the rest of this module."},"UnlockStatusResponse":{"properties":{"active":{"type":"boolean","title":"Active"},"expires_at":{"type":"integer","title":"Expires At"}},"type":"object","required":["active","expires_at"],"title":"UnlockStatusResponse"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"WarningRef":{"properties":{"code":{"type":"string","title":"Code"},"severity":{"type":"string","enum":["info","advisory","caution"],"title":"Severity"},"message":{"type":"string","title":"Message"},"affected_params":{"items":{"type":"string"},"type":"array","title":"Affected Params"},"detail":{"additionalProperties":{"type":"number"},"type":"object","title":"Detail"}},"additionalProperties":false,"type":"object","required":["code","severity","message"],"title":"WarningRef","description":"One non-fatal geometry advisory, surfaced to the workspace warnings\npanel (56th-fire T2). The web projection of the engine's GeometryWarning.\n\nAdvisory only — never changes a measured quantity; flags geometry that is\nvalid but likely a mistake (e.g. a stale dependent-height default). Present\non both tiers — a correctness signal, not a paid feature."},"WelcomeSendResponse":{"properties":{"status":{"type":"string","enum":["sent","already_sent","skipped"],"title":"Status"}},"type":"object","required":["status"],"title":"WelcomeSendResponse"},"WorkingRef":{"properties":{"category":{"type":"string","title":"Category","description":"WorkCategory string value, e.g. \"concrete_rc\" or \"excavation_general\"."},"quantity":{"type":"number","title":"Quantity"},"unit":{"type":"string","title":"Unit"},"source_assembly":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Assembly"},"source_element":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source Element"},"sub_category":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sub Category"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"calculation_steps":{"items":{"$ref":"#/components/schemas/CalculationStepRef"},"type":"array","title":"Calculation Steps"}},"additionalProperties":false,"type":"object","required":["category","quantity","unit"],"title":"WorkingRef","description":"One WorkItem from the engine output stream, surfaced for the\nworkspace Workings · Audit Trail panel.\n\nA WorkItem is the canonical, standard-agnostic unit of measured\nwork (one assembly may emit many — concrete volumes, reinforcement\nmasses, formwork areas, etc.); the BoQ rows in `_ComputeResponse.bill`\nare produced by the standard's renderer aggregating these items.\nThe Workings panel renders the WorkItem stream directly so the user\ncan see the pre-aggregation derivation behind every bill row.\n\n`calculation_steps` carries the per-step audit trail when the\nassembly emits it. Assemblies that don't emit it (legacy / simple\ncases) return an empty list — the panel still surfaces the\ncategory + quantity + unit + source."},"_AcceptanceRequest":{"properties":{"tos_version":{"type":"string","maxLength":16,"minLength":1,"title":"Tos Version"},"privacy_version":{"type":"string","maxLength":16,"minLength":1,"title":"Privacy Version"},"cookie_version":{"type":"string","maxLength":16,"minLength":1,"title":"Cookie Version"},"surface":{"type":"string","enum":["free_ppc","free_agg","free_mcp"],"title":"Surface"}},"additionalProperties":false,"type":"object","required":["tos_version","privacy_version","cookie_version","surface"],"title":"_AcceptanceRequest","description":"Body schema for POST /legal/accept."},"_AddAssemblyRequest":{"properties":{"slug":{"type":"string","minLength":1,"title":"Slug"},"parameters":{"additionalProperties":true,"type":"object","title":"Parameters"},"label":{"type":"string","title":"Label","default":""},"notes":{"type":"string","title":"Notes","default":""}},"additionalProperties":false,"type":"object","required":["slug"],"title":"_AddAssemblyRequest","description":"Body for ``POST /api/v1/mcp/project/{id}/assembly`` — add a single\nassembly to an existing project. Mirrors `_AssemblySaveSpec` shape\nbut ships standalone since the add-one flow takes one row, not a list."},"_AddAssemblyResponse":{"properties":{"project_id":{"type":"string","title":"Project Id"},"assembly_id":{"type":"string","title":"Assembly Id"},"position":{"type":"integer","title":"Position"}},"additionalProperties":false,"type":"object","required":["project_id","assembly_id","position"],"title":"_AddAssemblyResponse"},"_AssembliesListResponse":{"properties":{"assemblies":{"items":{"$ref":"#/components/schemas/_AssemblyBrief"},"type":"array","title":"Assemblies"}},"additionalProperties":false,"type":"object","required":["assemblies"],"title":"_AssembliesListResponse"},"_AssemblyBrief":{"properties":{"slug":{"type":"string","title":"Slug"},"display_name":{"type":"string","title":"Display Name"},"category":{"type":"string","title":"Category"},"description":{"type":"string","title":"Description"},"version":{"type":"string","title":"Version"},"tier":{"type":"string","enum":["free","paid"],"title":"Tier"}},"additionalProperties":false,"type":"object","required":["slug","display_name","category","description","version","tier"],"title":"_AssemblyBrief","description":"Brief view returned in the list endpoint."},"_AssemblyDeleteResponse":{"properties":{"project_id":{"type":"string","title":"Project Id"},"assembly_id":{"type":"string","title":"Assembly Id"},"deleted":{"type":"boolean","title":"Deleted"}},"additionalProperties":false,"type":"object","required":["project_id","assembly_id","deleted"],"title":"_AssemblyDeleteResponse"},"_AssemblyDetailResponse":{"properties":{"slug":{"type":"string","title":"Slug"},"display_name":{"type":"string","title":"Display Name"},"category":{"type":"string","title":"Category"},"description":{"type":"string","title":"Description"},"version":{"type":"string","title":"Version"},"tier":{"type":"string","enum":["free","paid"],"title":"Tier"},"parameter_groups":{"items":{"$ref":"#/components/schemas/ParameterGroupSchema"},"type":"array","title":"Parameter Groups"},"dependent_height_link":{"anyOf":[{"$ref":"#/components/schemas/DependentHeightLink"},{"type":"null"}]}},"additionalProperties":false,"type":"object","required":["slug","display_name","category","description","version","tier","parameter_groups"],"title":"_AssemblyDetailResponse","description":"Detail view including auto-generated parameter schema.\n\n`parameter_groups` is the structured form used by the G.6.4 schema-driven\nLiveAssemblyPage: each group is a collapsible section, each field is one\ninput control. Order is preserved from Parameters dataclass declaration."},"_AssemblyDrawingOut":{"properties":{"assembly_id":{"type":"string","title":"Assembly Id"},"slug":{"type":"string","title":"Slug"},"label":{"type":"string","title":"Label"},"position":{"type":"integer","title":"Position"},"drawing":{"anyOf":[{"$ref":"#/components/schemas/DrawingRef"},{"type":"null"}]}},"additionalProperties":false,"type":"object","required":["assembly_id","slug","label","position"],"title":"_AssemblyDrawingOut","description":"One enabled assembly's drawing payload for the project recompute\nresponse (G.4.4b Step 2).\n\nMirrors the per-assembly drawing surface from\n``POST /api/assemblies/{slug}/compute`` (same ``DrawingRef`` shape,\nsame SVG renderer via ``build_drawing_ref``) so the project view's\ncontact-sheet centre pane renders identically to the single-assembly\ncalculator preview. ``drawing`` is nullable to stay backward-\ncompatible with assemblies that don't emit ``draw_entities`` (older\nshape); the few assemblies emitting an empty list still get a tiny\nplaceholder SVG so the UI never has to special-case absence."},"_AssemblySaveSpec":{"properties":{"slug":{"type":"string","minLength":1,"title":"Slug"},"parameters":{"additionalProperties":true,"type":"object","title":"Parameters"},"label":{"type":"string","title":"Label","default":""},"notes":{"type":"string","title":"Notes","default":""}},"additionalProperties":false,"type":"object","required":["slug"],"title":"_AssemblySaveSpec","description":"One assembly entry inside a project save request."},"_AssemblyView":{"properties":{"id":{"type":"string","title":"Id"},"slug":{"type":"string","title":"Slug"},"label":{"type":"string","title":"Label"},"position":{"type":"integer","title":"Position"},"parameters":{"additionalProperties":true,"type":"object","title":"Parameters"},"notes":{"type":"string","title":"Notes"},"enabled":{"type":"boolean","title":"Enabled"}},"additionalProperties":false,"type":"object","required":["id","slug","label","position","parameters","notes","enabled"],"title":"_AssemblyView","description":"Single assembly row inside a fetched project payload."},"_BoQRowOut":{"properties":{"code":{"type":"string","title":"Code"},"description":{"type":"string","title":"Description"},"unit":{"type":"string","title":"Unit"},"quantity":{"type":"number","minimum":0.0,"title":"Quantity"},"rate":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Rate"},"amount":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Amount"},"group":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Group"},"sub_group":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sub Group"},"nrm1_group_element":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Nrm1 Group Element"},"nrm1_element":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Nrm1 Element"},"nrm1_sub_element":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Nrm1 Sub Element"}},"additionalProperties":false,"type":"object","required":["code","description","unit","quantity"],"title":"_BoQRowOut","description":"Slim BoQ row for the recompute response. Matches the engine's own\n``BoQItem`` columns; rate / amount stay null per the freemium-MCP\npricing-is-yours discipline (schema § 1.3)."},"_CatalogueAssembly":{"properties":{"slug":{"type":"string","title":"Slug"},"display_name":{"type":"string","title":"Display Name"},"description":{"type":"string","title":"Description"},"tier":{"type":"string","enum":["free","paid"],"title":"Tier"},"version":{"type":"string","title":"Version"}},"additionalProperties":false,"type":"object","required":["slug","display_name","description","tier","version"],"title":"_CatalogueAssembly","description":"Slim assembly entry inside the catalogue picker tree.\n\nCarries enough for the calling LLM to surface a usable picker without\nleaking implementation details: ``slug`` for the compute_* tool name,\n``display_name`` + ``description`` for human-facing labels,\n``tier`` so free callers know which tools they can hit, ``version``\nfor cache busting."},"_CatalogueL1":{"properties":{"key":{"type":"string","title":"Key"},"display":{"type":"string","title":"Display"},"children":{"items":{"$ref":"#/components/schemas/_CatalogueL2"},"type":"array","title":"Children"}},"additionalProperties":false,"type":"object","required":["key","display","children"],"title":"_CatalogueL1","description":"L1 family in the picker tree (e.g. drainage)."},"_CatalogueL2":{"properties":{"key":{"type":"string","title":"Key"},"display":{"type":"string","title":"Display"},"assemblies":{"items":{"$ref":"#/components/schemas/_CatalogueAssembly"},"type":"array","title":"Assemblies"}},"additionalProperties":false,"type":"object","required":["key","display","assemblies"],"title":"_CatalogueL2","description":"L2 leaf in the picker tree (e.g. drainage_chambers)."},"_CoBrandFields":{"properties":{"firm_name":{"type":"string","maxLength":200,"minLength":1,"title":"Firm Name"},"logo_url":{"anyOf":[{"type":"string","minLength":1,"format":"uri"},{"type":"null"}],"title":"Logo Url","description":"Accepted for the v1 wire contract; logo embedding is deferred. Validated as an http(s) URL today so a future fetch never sees file:// / ftp:// / SSRF payloads."},"brand_color_hex":{"anyOf":[{"type":"string","pattern":"^#?[0-9A-Fa-f]{6}$"},{"type":"null"}],"title":"Brand Color Hex","description":"Accepted for the v1 wire contract; theme swap is deferred to a follow-up."}},"additionalProperties":false,"type":"object","required":["firm_name"],"title":"_CoBrandFields","description":"Optional customer dual-branding payload per launch spec § 3.4.0."},"_ComputeResponse":{"properties":{"metadata":{"$ref":"#/components/schemas/ComputeMetadata"},"bill":{"items":{"$ref":"#/components/schemas/BoQRowRef"},"type":"array","title":"Bill"},"totals_summary":{"$ref":"#/components/schemas/TotalsSummary","description":"Aggregate per-material/work-class quantities (Schema § 1.4) — the BoQ rows summed by (work_category, unit). Drives the workspace Material Schedule panel: a supplier-neutral procurement take-off (what to order) drawn from the same compute. Standard-agnostic (aggregated off the WorkItem stream, like `workings`); identical across CESMM4 / NRM2 / SMM7 / MMHW. Present on both tiers. NOT a time-phased delivery schedule — a total, not quantities over time."},"workings":{"items":{"$ref":"#/components/schemas/WorkingRef"},"type":"array","title":"Workings","description":"WorkItem stream behind the BoQ — every measured quantity with its derivation (calculation_steps). Surfaces to the workspace Workings · Audit Trail panel. Standard-agnostic; identical across the four standards because the BoQ rendering is the only standard-aware step. CLAUDE.md pin 10 §15 invariant — every quantity is traceable; \"the only margin for error is user input\"."},"warnings":{"items":{"$ref":"#/components/schemas/WarningRef"},"type":"array","title":"Warnings","description":"Non-fatal geometry advisories (56th-fire T2). Advisory only — never changes a measured quantity; flags geometry that is valid but likely a mistake (e.g. a dependent height left at a stale default after the primary height was raised). Surfaces to the workspace warnings panel. Present on both tiers."},"drawing":{"anyOf":[{"$ref":"#/components/schemas/DrawingRef"},{"type":"null"}],"description":"Server-rendered SVG preview of the assembly geometry. None when the assembly emits no DrawEntities (rare — most assemblies produce geometry alongside the BoQ). The full DXF is served from `GET /api/assemblies/{slug}/dxf` per G.6.3."},"artefacts":{"items":{"$ref":"#/components/schemas/ArtefactRef"},"type":"array","title":"Artefacts","description":"Signed-URL pointers to rendered Excel + DXF + PDF artefacts. Empty when EXPORT_GATE_<STANDARD>_ENABLED is false (spec §15 invariant: gating is server-side, never client-side)."}},"additionalProperties":false,"type":"object","required":["metadata","bill","totals_summary"],"title":"_ComputeResponse"},"_MultiSectionRequest":{"properties":{"assembly_slug":{"type":"string","minLength":1,"title":"Assembly Slug"},"sections":{"items":{"$ref":"#/components/schemas/_SectionInput"},"type":"array","maxItems":50,"minItems":1,"title":"Sections"},"standard":{"type":"string","enum":["CESMM4","NRM2","SMM7","MMHW"],"title":"Standard","default":"MMHW"},"shared_params":{"additionalProperties":true,"type":"object","title":"Shared Params"},"aggregate_boq":{"type":"boolean","title":"Aggregate Boq","default":true},"include_section_boq":{"type":"boolean","title":"Include Section Boq","default":false},"include_backup":{"type":"boolean","title":"Include Backup","default":false},"deliverables":{"anyOf":[{"items":{"type":"string","enum":["xlsx","dxf","pdf"]},"type":"array"},{"type":"null"}],"title":"Deliverables"}},"additionalProperties":false,"type":"object","required":["assembly_slug","sections"],"title":"_MultiSectionRequest","description":"Request body for `POST /api/v1/mcp/compute-multi-section` (item K).\n\nMirrors the schema design in services/multi_section.py: one assembly slug,\none standard, N sections with per-section geometry overrides, and optional\nshared_params that apply to every section before per-section overrides."},"_PersonalisationFields":{"properties":{"project_name":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Project Name"},"client":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Client"},"prepared_by":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Prepared By"},"branding_mode":{"type":"string","enum":["unbranded","customer","dual"],"title":"Branding Mode","description":"Paid-tier branding mode per launch spec § 3.4.0. `unbranded` (default) = clean white. `customer` = customer firm only, CQ stripped. `dual` = customer + CQ side-by-side. Free-tier callers have this field ignored; their output is always CQ-branded + locked + watermarked.","default":"unbranded"},"co_brand":{"anyOf":[{"$ref":"#/components/schemas/_CoBrandFields"},{"type":"null"}],"description":"Customer dual-branding payload per launch spec § 3.4.0. Required when `branding_mode` is `customer` or `dual`; must be null when `branding_mode` is `unbranded`."}},"additionalProperties":false,"type":"object","title":"_PersonalisationFields","description":"Personalisation injected on the cover sheet per schema § 4.3."},"_ProcurementLineOut":{"properties":{"category":{"type":"string","title":"Category"},"spec":{"type":"string","title":"Spec"},"quantity":{"type":"number","minimum":0.0,"title":"Quantity"},"unit":{"type":"string","title":"Unit"},"source_assemblies":{"items":{"type":"string"},"type":"array","title":"Source Assemblies"}},"additionalProperties":false,"type":"object","required":["category","spec","quantity","unit","source_assemblies"],"title":"_ProcurementLineOut","description":"One row of the project's procurement summary (G.4.4b Step 1).\n\nThe procurement summary is the standard-agnostic material rollup keyed\non ``(category, spec, unit)`` — distinct from ``boqs_by_standard`` (a\nmeasurement-standard projection) and identical across all four\nstandards by construction. Drives the right pane of\n``/workspace/[project_id]`` and the ``Procurement`` sheet of the\nproject XLSX (per launch spec § 3.4.2)."},"_ProjectDeleteResponse":{"properties":{"project_id":{"type":"string","title":"Project Id"},"deleted":{"type":"boolean","title":"Deleted"}},"additionalProperties":false,"type":"object","required":["project_id","deleted"],"title":"_ProjectDeleteResponse"},"_ProjectDxfBundleRequest":{"properties":{},"additionalProperties":false,"type":"object","title":"_ProjectDxfBundleRequest","description":"Body for ``POST /api/v1/mcp/project/{id}/dxf-bundle``. Empty body accepted —\nbundle inputs are sourced entirely from the persisted project."},"_ProjectDxfBundleResponse":{"properties":{"project_id":{"type":"string","title":"Project Id"},"assembly_count":{"type":"integer","title":"Assembly Count"},"artefact":{"$ref":"#/components/schemas/ArtefactRef"}},"additionalProperties":false,"type":"object","required":["project_id","assembly_count","artefact"],"title":"_ProjectDxfBundleResponse","description":"Project DXF bundle response. ``artefact`` carries the presigned\ndownload URL + size + expiry for the ZIP archive (one DXF per\nenabled assembly + an index DXF listing every entry's hash cell)."},"_ProjectExcelRequest":{"properties":{},"additionalProperties":false,"type":"object","title":"_ProjectExcelRequest","description":"Body for ``POST /api/v1/mcp/project/{id}/excel``. Empty body accepted —\neverything the renderer needs is on the persisted project."},"_ProjectExcelResponse":{"properties":{"project_id":{"type":"string","title":"Project Id"},"standard_rendered":{"type":"string","title":"Standard Rendered"},"assembly_count":{"type":"integer","title":"Assembly Count"},"artefact":{"$ref":"#/components/schemas/ArtefactRef"}},"additionalProperties":false,"type":"object","required":["project_id","standard_rendered","assembly_count","artefact"],"title":"_ProjectExcelResponse","description":"Project XLSX bundle response. ``artefact`` carries the presigned\ndownload URL + size + expiry. ``standard_rendered`` echoes the\nproject's ``default_standard`` so the UI doesn't have to re-fetch\nthe project just to label the download button."},"_ProjectFetchResponse":{"properties":{"project_id":{"type":"string","title":"Project Id"},"name":{"type":"string","title":"Name"},"client":{"type":"string","title":"Client"},"project_number":{"type":"string","title":"Project Number"},"location":{"type":"string","title":"Location"},"description":{"type":"string","title":"Description"},"status":{"type":"string","title":"Status"},"default_standard":{"type":"string","title":"Default Standard"},"currency":{"type":"string","title":"Currency"},"assemblies":{"items":{"$ref":"#/components/schemas/_AssemblyView"},"type":"array","title":"Assemblies"}},"additionalProperties":false,"type":"object","required":["project_id","name","client","project_number","location","description","status","default_standard","currency","assemblies"],"title":"_ProjectFetchResponse"},"_ProjectListResponse":{"properties":{"projects":{"items":{"$ref":"#/components/schemas/_ProjectSummary"},"type":"array","title":"Projects"}},"additionalProperties":false,"type":"object","required":["projects"],"title":"_ProjectListResponse","description":"Response payload for ``GET /api/v1/mcp/projects``."},"_ProjectPatchRequest":{"properties":{"name":{"anyOf":[{"type":"string","maxLength":200,"minLength":1},{"type":"null"}],"title":"Name"},"client":{"anyOf":[{"type":"string","maxLength":200},{"type":"null"}],"title":"Client"},"project_number":{"anyOf":[{"type":"string","maxLength":60},{"type":"null"}],"title":"Project Number"},"location":{"anyOf":[{"type":"string","maxLength":300},{"type":"null"}],"title":"Location"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"default_standard":{"anyOf":[{"type":"string","enum":["CESMM4","NRM2","SMM7","MMHW"]},{"type":"null"}],"title":"Default Standard"},"currency":{"anyOf":[{"type":"string","maxLength":3,"minLength":3},{"type":"null"}],"title":"Currency"}},"additionalProperties":false,"type":"object","title":"_ProjectPatchRequest","description":"Body for ``PATCH /api/v1/mcp/project/{id}``.\n\nSparse update — every field is optional. The route ignores ``None``\nvalues so a partial payload (e.g. just ``{\"name\": \"Renamed\"}``)\nleaves other columns untouched. Length / standard / currency\nconstraints mirror ``_ProjectSaveRequest`` so the PATCH surface\ncan't widen the schema beyond what save accepts."},"_ProjectPdfBundleRequest":{"properties":{},"additionalProperties":false,"type":"object","title":"_ProjectPdfBundleRequest","description":"Body for ``POST /api/v1/mcp/project/{id}/pdf-bundle``. Empty body accepted —\nbundle inputs are sourced entirely from the persisted project."},"_ProjectPdfBundleResponse":{"properties":{"project_id":{"type":"string","title":"Project Id"},"standard_rendered":{"type":"string","title":"Standard Rendered"},"assembly_count":{"type":"integer","title":"Assembly Count"},"artefact":{"$ref":"#/components/schemas/ArtefactRef"}},"additionalProperties":false,"type":"object","required":["project_id","standard_rendered","assembly_count","artefact"],"title":"_ProjectPdfBundleResponse","description":"Project PDF bundle response. ``artefact`` carries the presigned\ndownload URL + size + expiry for the single multi-page PDF (cover +\nper-assembly drawing pages + project BoQ + procurement appendix)."},"_ProjectRecomputeRequest":{"properties":{"standard_codes":{"anyOf":[{"items":{"type":"string","enum":["CESMM4","NRM2","SMM7","MMHW"]},"type":"array"},{"type":"null"}],"title":"Standard Codes"}},"additionalProperties":false,"type":"object","title":"_ProjectRecomputeRequest","description":"Body for ``POST /api/v1/mcp/project/{id}/recompute``. Empty body is allowed —\nomits the standards filter and renders against all four."},"_ProjectRecomputeResponse":{"properties":{"project_id":{"type":"string","title":"Project Id"},"standards_rendered":{"items":{"type":"string"},"type":"array","title":"Standards Rendered"},"assembly_count":{"type":"integer","title":"Assembly Count"},"work_item_count":{"type":"integer","title":"Work Item Count"},"boqs_by_standard":{"additionalProperties":{"items":{"$ref":"#/components/schemas/_BoQRowOut"},"type":"array"},"type":"object","title":"Boqs By Standard"},"procurement_summary":{"items":{"$ref":"#/components/schemas/_ProcurementLineOut"},"type":"array","title":"Procurement Summary"},"assembly_drawings":{"items":{"$ref":"#/components/schemas/_AssemblyDrawingOut"},"type":"array","title":"Assembly Drawings"}},"additionalProperties":false,"type":"object","required":["project_id","standards_rendered","assembly_count","work_item_count","boqs_by_standard","procurement_summary","assembly_drawings"],"title":"_ProjectRecomputeResponse"},"_ProjectSaveRequest":{"properties":{"name":{"type":"string","maxLength":200,"minLength":1,"title":"Name"},"client":{"type":"string","maxLength":200,"title":"Client","default":""},"project_number":{"type":"string","maxLength":60,"title":"Project Number","default":""},"location":{"type":"string","maxLength":300,"title":"Location","default":""},"description":{"type":"string","title":"Description","default":""},"default_standard":{"type":"string","enum":["CESMM4","NRM2","SMM7","MMHW"],"title":"Default Standard","default":"CESMM4"},"currency":{"type":"string","maxLength":3,"minLength":3,"title":"Currency","default":"GBP"},"assemblies":{"items":{"$ref":"#/components/schemas/_AssemblySaveSpec"},"type":"array","title":"Assemblies"}},"additionalProperties":false,"type":"object","required":["name"],"title":"_ProjectSaveRequest","description":"Body for ``POST /api/v1/mcp/project/save``."},"_ProjectSaveResponse":{"properties":{"project_id":{"type":"string","title":"Project Id"},"assembly_ids":{"items":{"type":"string"},"type":"array","title":"Assembly Ids"}},"additionalProperties":false,"type":"object","required":["project_id","assembly_ids"],"title":"_ProjectSaveResponse"},"_ProjectSummary":{"properties":{"project_id":{"type":"string","title":"Project Id"},"name":{"type":"string","title":"Name"},"client":{"type":"string","title":"Client"},"project_number":{"type":"string","title":"Project Number"},"status":{"type":"string","title":"Status"},"default_standard":{"type":"string","title":"Default Standard"},"currency":{"type":"string","title":"Currency"},"assembly_count":{"type":"integer","title":"Assembly Count"},"created_at":{"type":"string","title":"Created At"},"updated_at":{"type":"string","title":"Updated At"}},"additionalProperties":false,"type":"object","required":["project_id","name","client","project_number","status","default_standard","currency","assembly_count","created_at","updated_at"],"title":"_ProjectSummary","description":"One row inside the ``GET /api/v1/mcp/projects`` list response. Narrowed\nshape — no parameters, no assembly details — keeps the list endpoint\ncheap to serialise even when an owner has many projects."},"_RenderRequestOptions":{"properties":{"standards":{"anyOf":[{"items":{"type":"string","enum":["CESMM4","NRM2","SMM7","MMHW"]},"type":"array"},{"type":"null"}],"title":"Standards","description":"Subset of {CESMM4, NRM2, SMM7, MMHW} to render BoQ tabs for. Omit for every standard present in the compute_results."},"include_backup_tabs":{"type":"boolean","title":"Include Backup Tabs","description":"Per launch spec § 3.4.1b — emit one Backup_<STD>_<NNN> sheet per BoQ row. Excel skill only.","default":true},"include_procurement_summary":{"type":"boolean","title":"Include Procurement Summary","description":"Per launch spec § 3.4.1c — emit an aggregated Procurement_Summary sheet alongside the BoQ tabs. Excel skill only.","default":false}},"additionalProperties":false,"type":"object","title":"_RenderRequestOptions","description":"Caller-tunable render options per launch spec § 3.4.1 a/b/c."},"_ResolveRequest":{"properties":{"description":{"type":"string","maxLength":2000,"minLength":1,"title":"Description"},"preferred_standard":{"anyOf":[{"type":"string","enum":["CESMM4","NRM2","SMM7","MMHW"]},{"type":"null"}],"title":"Preferred Standard"},"return_resolution_log":{"type":"boolean","title":"Return Resolution Log","default":false}},"additionalProperties":false,"type":"object","required":["description"],"title":"_ResolveRequest","description":"Request body for POST /api/v1/mcp/resolve-description (item L)."},"_ResourceEntry":{"properties":{"uri":{"type":"string","title":"Uri","description":"Canonical civilquants:// URI."},"mime":{"type":"string","title":"Mime","description":"MIME type of the body."},"description":{"type":"string","title":"Description","description":"Human-readable purpose statement."}},"additionalProperties":false,"type":"object","required":["uri","mime","description"],"title":"_ResourceEntry","description":"Manifest entry for a single resource — list endpoint surface."},"_SectionInput":{"properties":{"label":{"type":"string","maxLength":200,"minLength":1,"title":"Label"},"run_length":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Run Length"},"freeboard":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Freeboard"}},"additionalProperties":true,"type":"object","required":["label"],"title":"_SectionInput","description":"One section of a multi-section run.\n\nTyped fields cover the universal run-length and freeboard inputs.\nAssembly-specific geometry keys (e.g. stem_height, depth) are accepted\nas Pydantic extras and forwarded to the engine via section.params so that\nno new typed field is needed for every assembly's geometry vocabulary."},"civilquants__api__routes__assemblies___ComputeRequest":{"properties":{"parameters":{"additionalProperties":true,"type":"object","title":"Parameters"},"standard":{"type":"string","enum":["CESMM4","NRM2","SMM7","MMHW"],"title":"Standard"},"project":{"anyOf":[{"$ref":"#/components/schemas/ProjectMeta"},{"type":"null"}],"description":"Optional Option C Project entity. When supplied, name + reference propagate to the BoQ header, PDF cover, and DXF title block per INBOX 26th-fire Amendment C (in-scope for the G.6 architectural rewrite, 25th-fire thin-slice precursor to a full Project model in v1.1)."},"render_artefacts":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Render Artefacts","description":"Opt-in to eagerly render + persist the Excel/DXF/PDF export bundle (signed-URL artefacts) inline on this compute. Omitted / null / false means OFF, so the interactive workspace recompute path — debounced POST /compute on every parameter change — stays pure-engine fast and never launches the Puppeteer/Chromium PDF sidecar. Exports are fetched on demand via the dedicated /excel /pdf /dxf endpoints; only callers that genuinely want inline download URLs in one round-trip (e.g. an agent batch) set this True. Nullable default (mirroring `project`) keeps it an optional request field in the generated client. Still subject to the same server-side entitlement + export-gate checks."}},"additionalProperties":false,"type":"object","required":["standard"],"title":"_ComputeRequest"},"civilquants__api__routes__mcp___ComputeRequest":{"properties":{"tool":{"type":"string","minLength":1,"title":"Tool","description":"e.g. 'compute_attenuation_tank' or 'attenuation_tank'"},"arguments":{"additionalProperties":true,"type":"object","title":"Arguments"},"standard":{"type":"string","enum":["CESMM4","NRM2","SMM7","MMHW"],"title":"Standard","default":"MMHW"},"freeboard":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Freeboard","description":"Optional clearance (m) of the wall top above the retained fill, for retaining walls only. The engine back-calculates the retained height as stem − freeboard. Supplying both freeboard and the retained-height field is rejected."}},"additionalProperties":false,"type":"object","required":["tool"],"title":"_ComputeRequest","description":"Request body for the universal /api/v1/mcp/compute entry point.\n\nMatches the schema § 1.5 example input pattern: caller specifies the\ntool name (with or without the `compute_` prefix) plus the arguments\ndict the engine expects. `standard` is optional (defaults to MMHW —\nthe only standard free-tier callers can use)."}}}}