Skip to content

ID-71 {71.3} TECH — AI tooling surface rationalisation (outcome-grouped server + headless fleet substrate)

ID-71 {71.3} TECH — AI tooling surface rationalisation

Section titled “ID-71 {71.3} TECH — AI tooling surface rationalisation”

Status: {71.3} TECH spec. Authored by a FRESH Planner instance (Q-PLANNER-2 / B4 — NOT the {71.2} PRODUCT Planner) against the ratified predecessors: {71.1} RESEARCH.md, {71.2} PRODUCT.md (B-INV-1..43 — amended 15/06/2026, B-INV-43 added), and headless-requirement-refinement.md (HR/HC contract). Every Proposed change below maps 1:1 to a numbered B-INV invariant via the mapping table in §M; the Checker + MCP eval L4 verify that mapping. British English throughout. DD/MM/YYYY dates.

Five TECH OQs resolved in §OQ. The four PRODUCT OQs are RESOLVED inputs (not re-opened): create_content_item=write-to-store (TECH-GATED → see OQ-1), renewals grouped with O2 later, fact-check-first, PI-15 retired.

This spec fixes the mechanics; it does not re-litigate behaviour. Settled context (do NOT re-open): surface = Option A outcome-grouped server + Option C headless fleet; one queue + facet (B-INV-30); headless-complete launch set = O1/O4/O6 reads + W5.6; full fleet = post-launch graduation.


ID-71 rationalises KH’s AI-consumption surface (MCP tools + resources + prompts + MCP Apps

  • plugin commands/skills + inline lib/ai/ touchpoints) into one outcome-grouped MCP server (Option A) with a headless-agent fleet (Option C) layered additively, and ships it with a testable headless-completeness guarantee verified against the MCP eval L4 layer + a per-agent ID-104 eval contract. The behaviour is fixed in {71.2} PRODUCT; this spec fixes how each invariant lands in the codebase.

Code-intelligence orientation (gitnexus + ast-dataflow + ledger, knowledge-hub, 15/06/2026)

Section titled “Code-intelligence orientation (gitnexus + ast-dataflow + ledger, knowledge-hub, 15/06/2026)”

Per the binding orientation rule, the symbols this spec mandates be modified were queried before drafting. Outputs cited verbatim (not paraphrased) so the Checker can confirm the step:

  • Single tool-registry insertion point (Option A anchor). gitnexus_query("MCP tool registration search consolidation") returns registerTools (lib/mcp/tools/index.ts ~48-75, module Tools) as the single registry barrel, with registerSearchToolsfindSimilarItemsImpl (lib/mcp/tools/search.ts:381-497) and loadDomainNames (search.ts:44-58) as the find-consolidation surface, and the L4 layer runSearchToolChecks (scripts/mcp-eval/functional-correctness.ts:260-618) + callTool (functional-correctness.ts:171-228) as the eval substrate. The search formatters are formatSearchResults (lib/mcp/formatters/search.ts:49-90) and formatChunkSearchResults (search.ts:195-241).
  • MCP tool names are string literals, not graph symbols. gitnexus_context for create_content_item and list_user_workspaces BOTH returned "Symbol '…' not found" — because they are defineTool(server, 'create_content_item', …) string-literal registrations, not function names. Tool-name modification is therefore an ast-dataflow string-literal-uses + grep surface, NOT a gitnexus_rename surface. This is load-bearing for the bid→forms rename mechanics (OQ-4).
  • The one-queue seam already exists. gitnexus_context("unifiedToDashboardData") returns unifiedToDashboardData (lib/dashboard.ts ~104-122) — a legacy adapter mapping the newer UnifiedDashboardData shape back to DashboardData, called only by app/api/dashboard/route.ts:GET. The genuinely-unified substrate is lib/attention.ts: AttentionSourceData (attention.ts:80-101) already merges governance_review_count + stale_content_count + quality_flag_count + coverage_gap_count into one source, and produceGovernanceReviewItems (attention.ts:122-141) + SEVERITY_ORDER (attention.ts:107-112) emit one AttentionItem[] list with a type discriminator. The governance MCP facet is formatGovernanceQueue (lib/mcp/formatters/governance.ts ~161-207, called only by registerGovernanceTools); the content facet is PublicationReviewActionBar (components/review/publication-review-action-bar.tsx:51-161)
    • fetchPublicationReviewQueue (lib/query/fetchers.ts:542-570) + query-keys queue (lib/query/query-keys.ts:38-39) / publicationReviewQueue (query-keys.ts:50-51).
  • OQ-1 cutover state (re-verified, all four cites + prod-state update). app/api/ingest/url/route.ts:206-212 calls supabase.rpc('reference_ingest', …) unconditionally (no flag/env guard); the RPC migration 20260614010200_id110_reference_ingest_rpc.sql:48-50 apply-log reads “applied to staging … PROD push is a GATED operator step (NOT applied here)” — but that comment is point-in-time: the gated operator step has SINCE executed, and reference_ingest is now LIVE ON PROD on both prod projects (Orchestrator-verified 15/06/2026 via Supabase MCP pg_procrovrymhhffssilaftdwd + zjqbrdctesqvouboziae, 14 args). lib/edit-intent/write-back.ts:132-283 writeBackFileFirst operates only on EXISTING content_items with a linked source_document (the source-less branch at :178-191 explicitly does NOT auto-create — it writes KH-DB-only and logs an anomaly); and create_content_item (lib/mcp/tools/content.ts:247-527, tool #12, editor+ gated) writes directly via supabase.from('content_items').insert(insertData) (content.ts:523-527) — no reference layer. Conclusion: file-first / reference-layer ingest is LIVE ON PROD; the cutover gate is SATISFIED; and writeBackFileFirst is an edit-path affordance, not a create verb (a NEW create-into-store leg is needed — M-CREATE). See OQ-1.
  • ID-104 is spec_needed, not built. bun scripts/ledger-cli.ts show task 104 confirms ID-104 owns scripts/eval-runner.ts (exit 0/1/2), severity block|warn|info|infra + variance_band extending lib/eval/baseline.ts, baseline lifecycle (promoteBaseline + git-audited history + compareBaselines), the touchpoint-registry concept (every tool/prompt/skill: owner + eval + version), the Claude-as-judge tool-description rubric, and recordAiCall() cost tracking. The existing substrate is only lib/eval/baseline.ts (EvalBaseline with metrics + thresholds:{min,max_drop}lib/eval/types.ts:38-43) and the L1/L3/L4 mcp-eval suite; recordAiCall does not yet exist (grep returns nothing). See OQ-2.
  • Rename surface (ast-dataflow + grep, mixed bid/procurement). gitnexus_rename preview of useBidActionsuseFormActions is a single high-confidence graph edit in app/procurement/[id]/page.tsx:68 (importing from the already-procurement-named hooks/procurement/use-procurement-actions.ts). The DB layer is ALREADY renamed (form_responses, form_questions, get_form_question_stats per migration 20260601180058_id64_origin_kind_rename_form_response.sql); the residual bid naming is at the MCP-surface string-literal + plugin layer: tool get_bid_question (lib/mcp/tools/procurement.ts:334), resources bid_workspace/kb://bids/{id} (resources.ts:148-149), ui://bid-dashboard/app.html (resources.ts:662), prompts bid_briefing (resources.ts:835) / bid_pipeline_review (resources.ts:1029), plugin commands bid-pipeline-review.md / bid-status.md, plugin skill bid-writing, queue job_type='bid_draft_all' + bid_id body field (lib/queue/handlers/procurement-draft-all.ts), and fixtures (scripts/mcp-eval/fixtures.ts:95-97,242-267). See OQ-4.
  • Remote-MCP + IONOS/Coolify substrate exists. The remote MCP transport route is app/api/mcp/[transport]/route.ts (B-INV-10 incoming connectivity), guarded by the proxy.ts publicRoutes allowlist; the Option A role-separation seam is createMcpClient (lib/mcp/auth.ts:42, per-user RLS) + checkMcpRole (auth.ts:99). The IONOS/Coolify box already hosts the cocoindex pipeline image via .github/workflows/onprem-deploy.yml (the goose co-location target). See OQ-3.

Orientation outcome: not greenfield — every mandated change refines an existing, indexed substrate. No ccc search fallback or greenfield disclaimer applies. Tool/query catalogues: .gitnexus/CLAUDE.md, .ast-dataflow/CLAUDE.md.

Line anchors are indicative, not exact. Cited line numbers locate the symbol at authoring time and may drift by a line or two as files change; the symbol name + file path is the durable anchor. The Executor should resolve by symbol, not line.


OQ-1 — create_content_item ingest shape: WRITE-TO-CANONICAL-STORE (cutover SATISFIED) {#OQ-1}

Section titled “OQ-1 — create_content_item ingest shape: WRITE-TO-CANONICAL-STORE (cutover SATISFIED) {#OQ-1}”

Binding outcome: create_content_item = write-to-canonical-store (PRODUCT OQ-1 DEFAULT, Option A). The TECH-GATE precondition is now MET: the earlier DEFER verdict was premised on reference_ingest being staging-only, and that premise is now FALSE.

Cutover SATISFIED (Orchestrator-verified 15/06/2026 via Supabase MCP pg_proc; cite, do NOT re-query): the reference_ingest RPC is LIVE ON PROD — present on BOTH prod projects, knowledge-hub-phew (ref rovrymhhffssilaftdwd) and knowledge-hub-platform (ref zjqbrdctesqvouboziae), 14 args, routine_count=1. The migration-file comment in 20260614010200_id110_reference_ingest_rpc.sql:48-50 (“PROD push is a GATED operator step, NOT applied”) was point-in-time; the gated operator step has since executed. File-first / reference-layer ingest is therefore the live prod path.

Resolution mechanics (grounded on Task ID-59 — the in-progress edit-intent / file-first write-back substrate, ledger status: in_progress, title “Per-UC write-back” — the canonical write-back home; NOT onboarding):

  1. create_content_item gains a write-to-canonical-store create-leg (NEW change M-CREATE), NOT a direct-DB .insert and NOT the edit-path writeBackFileFirst. The create-verb creates the source_document / reference FIRST (the reference layer / reference_ingest, LIVE on prod, provides this), THEN materialises the content_item file-first on ID-59’s substrate. This satisfies the {59.10} source-less GUARD (bl-266), which currently BLOCKS file writes for a source-less content_item (write-back.ts:178-191) — the create-leg must therefore mint the source binding BEFORE the file leg, so the guard passes rather than trips.
  2. Shape-mismatch reconciled (the one I flagged in PRODUCT OQ-1): writeBackFileFirst (write-back.ts:132-283) is the EDIT path (PATCH /api/items/[id], runSweep/rollbackSweep) requiring an existing content_items row + linked source_document — its source-less branch (:178-191) refuses to auto-create. The create-verb needs a NEW create / propose-into-store leg on ID-59’s substrate, adjacent to the {59.19}/{59.22} corpus-promotion pattern (promoteCorpusExtractions — the existing create-into-store shape). writeBackFileFirst stays the sanctioned HC-4 edit-back destination (B-INV-12); M-CREATE is the create-into-store leg.
  3. v1.1 framing: per themes/canonical-pipeline/reference/deferral-register.md, the project moves to v1.1 imminently; the create-verb write-to-store is v1.1-cycle canonical-pipeline scope on the ID-59 substrate — NO new deferral-register row, NOT routed to onboarding. ID-71 couples to ID-59 (the write-back substrate owner) for sequencing; this REPLACES the old “ID-110 cutover → onboarding” intent (§X).

OQ-2 — per-agent eval-contract: BOTTOM-UP, ID-104-OWNED (hard upstream dep) {#OQ-2}

Section titled “OQ-2 — per-agent eval-contract: BOTTOM-UP, ID-104-OWNED (hard upstream dep) {#OQ-2}”

Binding outcome (Liam steer 15/06/2026 — did NOT ratify reuse+extend): the per-agent AgentEvalContract is designed BOTTOM-UP as the long-term agent-eval layer, OWNED by ID-104 (the eval-engine Task). ID-71 CONSUMES the contract only. Liam’s steer: ID-71 + ID-104 are a BOTTOM-UP rebuild, not a “build onto what we already have” shortcut — nearly all current MCP tooling is verdicted review/adapt/retire, so for something as load-bearing as the new agent-eval layer, the approach chosen NOW must be the long-term one, even if it means splitting into separate standalone tasks/subtasks.

Consequences:

  • The reuse-vs-rebuild decision is DEFERRED to ID-104’s bottom-up design — do NOT pre-commit to reusing the legacy L3/L4 + lib/eval/baseline.ts layers. Whether the bottom-up layer reuses the existing baseline lifecycle / callTool harness or rebuilds them is ID-104’s call, made from a clean-sheet design, not assumed here.
  • ID-104’s eval-engine spec is a HARD UPSTREAM dependency, sequenced BEFORE ID-71’s born-evaluable waves (Wave 2 + Wave 3). ID-71 cannot ship B-INV-13/14/15/38/40 until the ID-104-owned contract + severity model + touchpoint registry exist to consume.
  • ID-71 MUST NOT implement eval-runner.ts / the severity model / recordAiCall() / the contract definition. Those are ID-104 deliverables, full stop.

The contract SHAPE ID-71 consumes (illustrative — ID-104 owns the canonical definition; fields below are the consumption surface ID-71’s born-evaluable guard reads):

// OWNED + canonically defined by ID-104 (bottom-up). Shown here only as the
// consumption surface ID-71's guard (M38) reads; ID-104 may refine.
interface AgentEvalContract {
touchpoint_id: string; // stable id: tool name | prompt name | skill | recipe slug
kind: 'tool' | 'prompt' | 'skill' | 'inline' | 'agent_recipe';
owner: string; // touchpoint registry: owner of record
suite_name: string; // the eval suite this touchpoint runs under
grounding_shape: // B-INV-35: exactly one of three
'structured_output' | 'forced_tool_strict' | 'citations' | 'n/a';
severity_on_fail: 'block' | 'warn' | 'info' | 'infra'; // ID-104 model
variance_band: number; // ID-104 model; tolerance before regression
graduation_metric?: string; // B-INV-15 / B-INV-19: in-house WS-5 auto-apply metric
}

Fallback (NOT intent): if ID-104’s spec lags ID-71’s born-evaluable guard subtask at dispatch time, ID-71 may ship the consumption-surface shape above as a last-resort placeholder interface in lib/eval/types.ts, clearly marked provisional, for ID-104 to reconcile. This is a schedule-slip fallback, NOT the design intent — the intent is ID-104-first.

OQ-3 — goose runtime/deploy mechanics + version-pin (co-location RATIFIED) {#OQ-3}

Section titled “OQ-3 — goose runtime/deploy mechanics + version-pin (co-location RATIFIED) {#OQ-3}”

RATIFIED 15/06/2026 (Liam): co-locate on the existing IONOS/Coolify box. The recommendation below stood; no design change. Mechanics unchanged: Recipes + cron + remote-MCP + goose run only (NOT goosed); pin-and-soak version policy; self-signed-TLS health-probe (#7685) handled in-spec.

Runtime mechanics (settled by HR-1 / §4.1): rely on the stable surfaces only — YAML Recipes (parameters / retry / sub-recipes) + the built-in cron scheduler (goose schedule add) + remote-MCP (SSE / Streamable-HTTP + auth headers, pointed at app/api/mcp/[transport]/route.ts) + goose run (one-shot headless). Do NOT depend on the goosed daemon (a bespoke ~103-endpoint REST surface backing the desktop app — not a stable embedding contract). Pin the image at a specific tag (ghcr.io/block/goose:<pinned>); the version-pin policy is pin-and-soak: bump only behind a re-run of the §MVP pilot recipe on staging-equivalent, never auto-track weekly releases (goose ships weekly; the moving surface is the §4.1 biggest risk). Configure the self-signed-TLS health-probe wrinkle (goose issue #7685) away on Coolify’s healthcheck.

Deploy posture (RATIFIED 15/06/2026 — co-locate on the existing IONOS/Coolify box) as a separate Coolify service (its own container, not sharing the cocoindex image). The box already runs the cocoindex pipeline via onprem-deploy.yml; a second small goose service avoids a new box’s provisioning/auth/cost. Residual risk to manage (recorded, not a blocker): the box is ~16 GB and onprem-deploy.yml already records Coolify burn-guard pressure from the ~5.3 GB cocoindex build — the co-located goose service competes for that headroom, so size the goose container conservatively and watch the burn guard during the soak. Either way the pilot is gated: goose is committed as the launch substrate ONLY after the §MVP pilot validates the §7 must-be-true assumptions (B-INV-21).

OQ-4 — bid→forms rename lockstep: ONE coordinated client-contract break {#OQ-4}

Section titled “OQ-4 — bid→forms rename lockstep: ONE coordinated client-contract break {#OQ-4}”

Ontology RULE the rename follows (BLOCKER fix — get this exactly right; PLAN must NOT inherit an inverted ontology). Two DISTINCT axes, cited:

  • application_type (CV 25) = the UMBRELLA / application axis. 6 core values: procurement, intelligence, sales_proposal, product_guide, competitor_research, training_onboarding (lib/mcp/tools/workspaces.ts:30-35; …/intended-architecture/04-workspace-types.md §3.2; platform-direction.md §v1-shape). procurement is an application umbrella value on THIS axis.
  • form_type (CV 26) = a DISTINCT sibling axis — the type of form a respondent fills; 8 core incl. bid, rfp, pqq, itt, tender (…/ontology/26-form-type.md; migration 20260520120828_t2_combined_pr_intel_shape_b_form_type_split.sql:627-635). form_types.applicable_application_types[] is the cross-link.
  • Therefore: procurement is the application umbrella; bid is a form_type WITHIN procurement (migration :257“bid survives as a form_type value within procurement”; :95“‘bid’ → ‘procurement’ (procurement umbrella rename)”). There is NO activity_type axis in the schema (grep: zero hits) — the umbrella axis is application_type. 04-workspace-types.md §11 lists the umbrella/form-type inversion as a RATIFIED-REJECTED anti-pattern.
  • Naming RULE the bottom-up rename enforces: application-axis names use application_types.key (e.g. procurement); form-axis names use form / form_type, never bare bid. bid survives ONLY as a form_types value, so no user-facing tool carries bare “bid” post-cutover — but a tool whose name is an APPLICATION-umbrella read (list_active_procurement, which genuinely queries .eq('application_types.key','procurement')lib/mcp/tools/procurement.ts:178) is already correctly named and is KEPT for that reason, NOT because procurement is a form-type.

Mechanics: one atomic break across six lanes, aligned with the prod DDL cutover wave. The DB layer is already renamed; the residual surface is MCP string literals + plugin + fixtures + evals + client guide. Because MCP tool/resource/prompt names are string literals (orientation), gitnexus_rename covers ONLY the graph-tracked code symbols (hook exports useBidActions/useBidTransitions, BidsPage); the string-literal surface is an ast-dataflow string-literal-uses + grep sweep across TS, Python, and SQL. The six lanes (must land in ONE PR / cutover wave so no client sees a half-renamed surface — B-INV-31):

  1. Code symbols (gitnexus_rename, high-confidence graph): useBidActionsuseFormActions, useBidTransitionsuseFormTransitions (hooks/procurement/use-procurement-actions.ts), BidsPageFormsPage (app/procurement/page.tsx), use-bid-data/use-bid-dialogs identifiers. Verify completeness with ast-dataflow callers post-rename (rename-sweep skill).
  2. MCP surface string literals (ast-dataflow string-literal-uses + grep): tool get_bid_questionget_form_question (tools/procurement.ts:334); resources bid_workspace + kb://bids/{id}kb://forms/{id} (resources.ts:148-149), ui://bid-dashboard/app.htmlui://forms-dashboard/app.html (resources.ts:662,670,680); prompts bid_briefingform_briefing (resources.ts:835), bid_pipeline_reviewform_pipeline_review (resources.ts:1029). KEEP list_active_procurement / get_procurement_detail — these are APPLICATION-umbrella reads (they query application_types.key='procurement', procurement.ts:178), already correctly named post-DB-rename per the ontology RULE above; they are NOT renamed. What retires is the generic bid form vocab (get_bid_questionget_form_question, the bare-bid MCP resources/prompts) — because bid is a form_type value, not a tool-name axis. No user-facing tool carries bare “bid” post-cutover.
  3. Plugin bundle (bun run build:plugin regenerates committed plugin-bundle.ts): rename commands bid-pipeline-review.mdform-pipeline-review.md, bid-status.mdform-status.md; plugin skill bid-writingcompleting-forms (the WS-9 named generalisation, B-INV-31).
  4. Queue + API path (PI-13/14 fold): job_type='bid_draft_all''form_draft_all' (lib/queue/handlers/procurement-draft-all.ts, lib/queue/dispatch.ts case, the s224_widen_job_type_check CHECK widened in a new migration), bid_id body field→form_id, path app/api/bids/[id]/…app/api/forms/[id]/… (add the new route to proxy.ts publicRoutes only if the old one was listed).
  5. Fixtures + evals (mcp-fixture-sync.test.ts drift-guard forces this in lockstep): scripts/mcp-eval/fixtures.ts:95-97,242-267 canonical tool/prompt/resource lists; the L4 checks in functional-correctness.ts; Python scripts/tests/test_cocoindex_server.py (grep — ast-dataflow does not cover Python).
  6. Inventory + client-doc refresh (M-DOCS): mcp-parser.ts / mcp-inventory-parser.test.ts count assertions update with the rename; the client-facing tool guide regenerates. Beyond the tool-guide regen, the broader client-doc refresh is a named ID-71 deliverable (M-DOCS): the Platform Overview + Claude Integration Guide are flagged “heavily outdated” (SYNTHESIS §1.8, tracker E.2) and their refresh is “a natural ID-71 deliverable” — refresh both to the rationalised outcome-grouped surface + the new forms vocabulary as part of this cutover wave. PI-13/14: strip stale plugin branding / connector refs — clean break, no live client connectors today (B-INV-41).

This is the one place ID-71 touches DDL: the job_type CHECK widen migration (see §Proposed changes M31/M41). Per supabase/CLAUDE.md: supabase migration new + foreground db push, SET search_path = public, extensions, explicit REVOKE … FROM anon on any new function (none expected here — CHECK-only).

OQ-5 — one-queue + facet reconcile: the facet IS the seam, it already exists {#OQ-5}

Section titled “OQ-5 — one-queue + facet reconcile: the facet IS the seam, it already exists {#OQ-5}”

Resolution: M30 ships a NEW faceted-queue MCP tool (whats_in_my_queue) — an additive GREENFIELD MCP surface OVER the existing lib/attention.ts substrate, NOT a thin wrapper. The unified attention substrate genuinely exists and already merges governance + quality + freshness — AttentionSourceData (attention.ts:~80-101) carries one set of counts, buildAttentionItems emits one AttentionItem[] list, and AttentionItem.type (attention.ts:~29-39) is the discriminator — but no MCP tool today consumes AttentionItem[] with a facet filter. Today AttentionItem.type drives the dashboard icon/colour, and formatGovernanceQueue is a SEPARATE MCP formatter on its own path. So M30 is genuinely new MCP read surface (the faceted-queue tool) built over a real, already-merged substrate — the build is the MCP consumption seam, not the merge (the merge is done).

The /review vs /api/governance/review split that CLAUDE.md documents as separate workflows stays at the route/page layer (content-quality review UI vs freshness/ownership governance UI) — only the MCP consumption surface collapses to one queue concept (B-INV-30).

Mechanics: a new whats_in_my_queue MCP tool takes facet?: 'content_quality' | 'governance' | 'all' (default all), reads the lib/attention.ts producers, and replaces the fragmented get_review_queue / get_governance_queue / get_assignments_for_user / get_dashboard_summary MCP reads (B-INV-30, B-INV-32 queue side). The cron freshness writers (app/api/cron/review-cadence/route.ts ReviewCandidate, app/api/cron/freshness-transitions/route.ts) and formatGovernanceQueue are unchanged producers; only the MCP read surface consolidates.

source_document_change facet-scope (explicit — pre-existing substrate gap): AttentionItem.type lists 'source_document_change' (attention.ts:~38) as a discriminator value, but AttentionSourceData carries NO count and there is NO producer function for it (no produceSourceDocumentChange*). M30 scopes source_document_change OUT of the v1 facet set — the v1 whats_in_my_queue facets are content_quality (quality_flag, stale/expired/unverified content) + governance (governance_review, review-overdue) + the existing assignment/coverage producers; source_document_change is NOT surfaced until a producer + count exist. Building that producer is explicitly NOT M30 scope (it is an ID-59 / source-document-lineage concern, cross-referenced, not built here). The v1 facet enum therefore omits it, so no facet value resolves to an empty/absent producer.

The plugin-skill question (governance-review vs content-governance, RESEARCH §2.8 [LIAM]) folds the same way: keep both skills as the expertise layer (B-INV-34) but they orchestrate the one faceted queue, not two.


Each change is keyed M<N> to the B-INV it satisfies (full table in §M). Changes are grouped by wave; the §M table is the orphan-check of record.

Wave 1 — Outcome-grouped surface consolidation (Option A; B-INV-25..34)

Section titled “Wave 1 — Outcome-grouped surface consolidation (Option A; B-INV-25..34)”
  • M27 find consolidation. Collapse search_knowledge_base / search_qa_library / search_content_chunks / find_similar_items into ONE find tool registered through registerTools (tools/index.ts ~48-75), parameterised type/scope (preserves q_a_pairs + scope_tag corpus semantics) + granularity (chunk vs item). Reuse findSimilarItemsImpl + the search formatters as internal branches; preserve the two-step list/preview→verbatim contract (M33). New entry declares an outputSchema (M37). (B-INV-27, B-INV-33.)
  • M28 ontology-grounding affordance. Promote get_entity_relationships + kb://entities to a first-class answering affordance of find (or a sibling ground entry), not a curation-side tool. (B-INV-28.)
  • M29 where-are-we-exposed. One entry consolidating get_expiring_content / get_freshness_report / get_coverage_gaps / get_certification_status / audit_content / get_quality_* into the five-layer presentation (data → quality → use-today → gaps → opportunities) with suggest_content_creation as the attached resolution affordance (“Draft content for X” / “Discuss options for Y”). (B-INV-4, B-INV-29.)
  • M30 whats_in_my_queue (one queue + facet). Per OQ-5: a NEW faceted-queue MCP tool — additive GREENFIELD read surface OVER the existing (already-merged) lib/attention.ts substrate, NOT a thin wrapper. facet?: 'content_quality' | 'governance' | 'all'; source_document_change is scoped OUT of the v1 facet set (no producer/count exists — pre-existing substrate gap, not built here). (B-INV-30.)
  • M-CREATE create_content_item write-to-canonical-store create-leg. Per OQ-1 (cutover SATISFIED): add a create-into-store leg on ID-59’s file-first write-back substrate — mint the source_document/reference FIRST (via reference_ingest, LIVE on prod), THEN materialise the content_item file-first so the {59.10} source-less GUARD (bl-266, write-back.ts:178-191) passes. NOT a direct-DB .insert, NOT the edit-path writeBackFileFirst; adjacent to the {59.19}/{59.22} promoteCorpusExtractions create-into-store pattern. Cross-Task-coupled to ID-59 (§X). (B-INV-6, B-INV-12.)
  • M31 completing-forms + bid→forms rename. Per OQ-4 six-lane break. (B-INV-31.)
  • M32 get/dedup/assign consolidation. One get (one-or-many param) for get_content_item/_items; one dedup (scope param) for find_duplicate_candidates/find_all_duplicates; one assign (one-or-many) for assign_content_owner/bulk_assign_owner. (B-INV-32.)
  • M33 two-step retrieval preserved on find + get. (B-INV-33.)
  • M34 layer placement. Prompts + /kb:* commands become thin orchestrators over the consolidated entries; 4 MCP Apps stay the visual-density + WS-8 resolution surface; plugin skills stay the expertise layer. No command re-implements tool ability. (B-INV-34.)
  • M22/M23/M24 verdict framing — surface-design record (this spec’s §M + the rename record) maps every keep/refine/retire/gap to an O/W number; no retire orphans a ranked outcome (each names its surviving entry). (B-INV-22, B-INV-23, B-INV-24.)
  • M25/M26 single-server. One human-facing server via registerTools; fleet additive; no second connector at launch; Option-B deferral trigger recorded (admin/consumption auth surfaces diverge enough to warrant a second connector). (B-INV-25, B-INV-26.)

Wave 2 — Grounding + eval standards rollout (B-INV-35..40)

Section titled “Wave 2 — Grounding + eval standards rollout (B-INV-35..40)”
  • M35 grounding shapes. Each AI touchpoint (classify.ts, draft.ts 3-pass, quality-check.ts, extract-questions.ts, summary/intelligence/guide passes) declares and uses exactly one of: output_config.format | forced-tool strict:true + additionalProperties:false | citations over search_result. Citations never combined with output_config.format; draft.ts 3-pass split stays forced. (B-INV-35.)
  • M36 no silent fallback. stop_reason:"refusal"/"max_tokens" handled explicitly (log + surface); replace draft.ts Pass-1 bare try/catch; no assistant prefills. (B-INV-36.)
  • M37 outputSchema forward-standard on new entries only; not retrofitted onto retiring tools. (B-INV-37.)
  • M38 forcing-function guard. Extend the mcp-parser.ts / mcp-fixture-sync.test.ts / mcp-inventory-parser.test.ts guard pattern (and the ID-104 recordAiCall() grep-guard, once it lands) so any tool/prompt/skill/inline change FAILS a guard test without a create-skill/update-skill invocation AND an eval/fixture update. The AgentEvalContract presence-check the guard enforces consumes ID-104’s bottom-up contract (OQ-2 — hard upstream dep, §X). (B-INV-38.)
  • M39 AI-invisibility. Consumption surfaces present enrichments as native; only admin Provenance exposes lineage. (B-INV-39.)
  • M40 eval-is-the-gate. Every grounded touchpoint carries an ID-104 eval in addition to its schema constraint. (B-INV-40.)

Wave 3 — Headless-completeness + fleet substrate (B-INV-1..21)

Section titled “Wave 3 — Headless-completeness + fleet substrate (B-INV-1..21)”
  • M1–M5 headless-complete set. The L4 layer enumerates exactly {O1 find/answer reads, O4 reorientation/briefing reads (widened beyond KH state — M3), O6 exposure five-layer reads (M4), W5.6 re-syndication (M5)}; each is driven MCP-only to a terminal result with zero human-in-UI step. (B-INV-1, B-INV-2, B-INV-3, B-INV-4, B-INV-5.)
  • M6/M7 propose-writes + auto-apply-off. Agents create propose-rows/drafts/resolutions into the queue (M30) with no publication gate; publishing routes to the human gate; per-workflow auto-apply switch exists and is verifiably OFF at launch. The write-to-canonical-store create affordance is the M-CREATE create-leg (OQ-1). (B-INV-6, B-INV-7.)
  • M8/M9 dual runtime. The headless-complete set is reachable identically from Claude’s own runtimes AND the goose runtime via the SAME remote-MCP surface (app/api/mcp/[transport]/route.ts); no runtime-privileged entry. (B-INV-8, B-INV-9.)
  • M10/M11/M12 connectivity. Incoming = remote MCP (exists, M10). Outgoing = one trigger-driven push channel (RSS/webhook/email) delivering a consumption output (M11) + write-back scoped to the three sanctioned destinations only (local-fs canonical store — writeBackFileFirst for edit-back + M-CREATE for create-into-store, live HubSpot↔Cowork connector, push) with a net-new source-system write-back refused at the surface (M12). (B-INV-10, B-INV-11, B-INV-12.)
  • M13/M14/M15 born-evaluable (consumes ID-104, bottom-up). Per OQ-2: every headless agent
    • new/refined touchpoint ships an ID-104-owned, bottom-up AgentEvalContract (ID-71 CONSUMES it, does not author the layer); evals authored locally (Raindrop Workshop) committed into KH’s KH-owned eval harness (not Raindrop-proprietary); the WS-5 graduation metric is computed in-house with a network assertion confirming zero client-data egress to Raindrop cloud. ID-104’s eval-engine spec is a HARD UPSTREAM dependency, sequenced before these waves (§X). (B-INV-13, B-INV-14, B-INV-15.)
  • M16/M17 scope boundary. W9.3 absent from ID-71’s enumeration, present as an onboarding cross-reference; launch scope = {O1/O4/O6 + W5.6} + one piloted recipe. (B-INV-16, B-INV-17.)
  • M18/M19/M21 pilot + graduation. ONE goose recipe (§MVP): cron → remote-MCP → Claude → one O4 briefing → deliver to one outbound channel + write one propose-row. It exercises HC-1/HC-2/HC-4; goose commitment is pilot-gated. Fleet grows additively, per-Task, metric-gated. (B-INV-18, B-INV-19, B-INV-21.)
  • M20 net-new capabilities NOT delivered under ID-71 (W5.2/W5.3/W2.3/W3.1-3.3/W2.2); each has a reserved concept + own-Task pointer (RESEARCH §4 task-split map). WS-10 W5.2-home assessment + WS-12 proposal-writer exemplar carried forward to the O5/O2 own-Tasks (§X intent #4; PRODUCT B-INV-20). (B-INV-20.)

Wave 1b — Client-doc refresh (rename cutover wave)

Section titled “Wave 1b — Client-doc refresh (rename cutover wave)”
  • M-DOCS client-doc refresh. Beyond the OQ-4-lane-6 tool-guide regen, refresh the Platform Overview + Claude Integration Guide (SYNTHESIS §1.8 — “heavily outdated”, refresh is “a natural ID-71 deliverable”) to the rationalised outcome-grouped surface + the forms vocabulary. Lands in the OQ-4 cutover wave; coordinated with the docs-site docubot lane (§X intent #5). Binds to B-INV-41 (the surface-design / branding-hygiene invariant the rename lockstep already carries). (B-INV-41.)

Wave 3b — Deferred-with-anchor (no launch change)

Section titled “Wave 3b — Deferred-with-anchor (no launch change)”
  • M-DEFER-O8 (no launch code change — recorded anchor). O8 W8.4 argument-quality checking is a WANTED affordance recorded DEFERRED-WITH-ANCHOR (PRODUCT B-INV-43, §G): rank-8, below the launch set; it graduates as an O8 affordance in a later group (own-Task / guide-tooling Task), born against an ID-104 eval when it lands. ID-71 builds nothing; the numbered anchor preserves it. (B-INV-43.)

Wave 0 — Carried PI-items (B-INV-41, B-INV-42)

Section titled “Wave 0 — Carried PI-items (B-INV-41, B-INV-42)”
  • M41 PI-13/14 fold into the OQ-4 rename lockstep (clean break; no live connectors). (B-INV-41.)
  • M42 PI-15 retire. Carry PI-15 (per-tenant taxonomy-sync, BROKEN since {68.23}) as RETIRED in the surface-design record with the one-line no-live-consumer confirmation (expected: none); kb://taxonomy (absorbs bl-52) named as the replacement. No code path re-enabled. (B-INV-42.)

MVP pilot (riskiest-assumption-first; B-INV-18/21) {#MVP}

Section titled “MVP pilot (riskiest-assumption-first; B-INV-18/21) {#MVP}”

ONE goose Recipe on Coolify/IONOS (co-located, RATIFIED): cron trigger → remote-MCP into KH (Claude) → generate one O4 briefing → (a) deliver to one outbound channel + (b) write one propose-row into KH’s queue (M30). Instrumented with Raindrop Workshop locally; the failure-authored eval committed into the ID-104-owned harness (bottom-up; ID-104 is a hard upstream dep — §X). Proves HC-1 (briefing read) / HC-2 (propose-row) / HC-4 (push + queue write) and the §7 must-be-true assumptions in one slice. Gated: goose is the launch substrate ONLY on pilot success.


Each B-INV maps to a concrete check. The MCP eval L4 functional-correctness layer (scripts/mcp-eval/functional-correctness.ts, bun run test:mcp-eval:fc) is the primary verifier; bun run test is the regression gate (never bun test).

  • B-INV-1/2/5 (completable set): an L4 suite enumerates exactly {O1/O4/O6 reads + W5.6} and drives each MCP-only to a terminal result; the Checker confirms the enumeration verbatim and that no member is UI-only.
  • B-INV-3/4 (O4 widen / O6 five-layer): L4 fixtures assert a non-KH-state dimension on the O4 read and the five-layer ordering + ≥1 resolution affordance on the O6 read.
  • B-INV-6/7 (propose / auto-apply): L4 confirms an agent creates a propose-row with no human step AND is refused at publish (routed to the human gate); every headless write path defaults propose-only; the per-workflow auto-apply switch is verifiably off.
  • B-INV-8/9/10 (dual runtime / incoming): the goose-consumed inventory equals the Claude-runtime inventory for the headless set; the remote-MCP-with-auth path is an evaluated connection.
  • B-INV-11/12 (outgoing / write-back): an eval asserts one push delivery end-to-end; an L4 check confirms a net-new source-system write-back is refused at the surface.
  • B-INV-13/14/15 (born-evaluable): the extended guard (M38) rejects a new/refined touchpoint lacking an AgentEvalContract; a headless agent’s eval runs under KH CI; a network assertion confirms zero client-data egress to Raindrop cloud.
  • B-INV-27/29/30/31/32/33 (surface shape): L4 + mcp-fixture-sync.test.ts + mcp-inventory-parser.test.ts confirm one parameterised entry per outcome, preserved semantics (q&a scope, chunk granularity, two-step retrieval), one faceted queue, no user-facing bid entry post-cutover, each single+batch pair consolidated.
  • B-INV-34/35/36/37/38/39/40 (layer + grounding + eval): unit tests assert each touchpoint declares one grounding shape and no citations×structured-output combination; refusal/max-tokens surfaced explicitly; new entries carry outputSchema; the guard test fails a tooling change without skill+eval update; consumption surfaces present enrichments as native.
  • B-INV-18/21 (pilot): the §MVP recipe exercises an O4 read + propose-row + push + queue write; goose adoption is pilot-gated.
  • B-INV-22/23/24/25/26/41/42 (framing / scope / carried): Checker confirms against this spec’s §M record (verdicts anchored to O/W numbers, no orphaned outcome, single server + deferral trigger, PI-13/14 folded, PI-15 retired with no-consumer confirmation).
  • B-INV-16/17/19/20 (scope boundary): Checker confirms W9.3 is cross-reference-only, launch scope unchanged, fleet additions Task+metric-gated, no net-new capability built.

  • M-CREATE source-less GUARD (bl-266). The {59.10} guard BLOCKS file writes for a source-less content_item; a naive create-verb that materialises the item before minting its source binding trips the guard. Mitigation: M-CREATE mints the source_document / reference (via reference_ingest, LIVE on prod) FIRST, then the file leg — the guard passes. Cross-Task-coupled to ID-59 (§X), which owns the substrate.
  • goose moving surface (§4.1 biggest risk). Weekly releases; goosed instability. Mitigation: pin-and-soak (OQ-3); stable surfaces only; pilot-gated commitment.
  • Coolify burn headroom. Co-located goose (RATIFIED) competes with the ~5.3 GB cocoindex build on the ~16 GB box. Mitigation: size the goose container conservatively; watch the burn guard during the soak (OQ-3).
  • Rename half-break. A partial bid→forms rename shows clients a mixed surface. Mitigation: OQ-4 one-PR atomic break; mcp-fixture-sync.test.ts drift-guard forces fixtures in lockstep; proxy.ts publicRoutes re-check for the new API path.
  • ID-104 hard-upstream slip. The bottom-up eval-engine (contract + severity model + registry) is an ID-104 deliverable sequenced BEFORE ID-71 Wave 2/3. If it slips, the born-evaluable waves block. Mitigation: sequence ID-104 spec first (§X); the lib/eval/types.ts consumption-shape placeholder is a last-resort schedule fallback only (NOT intent — OQ-2). ID-71 does NOT implement the engine.
  • Raindrop egress. Mitigation: B-INV-15 network assertion; Workshop-local only, empty writeKey.

These are Orchestrator-applied ledger/coordination notes (sibling-only Subtask dep constraint honoured — these are cross-Task, so they stay at Task level, NOT bent into Subtask deps):

  1. ID-59 (file-first write-back substrate, in_progress) → OQ-1 / M-CREATE. The create_content_item write-to-canonical-store create-leg is built on ID-59’s substrate (the canonical write-back home). reference_ingest is LIVE on prod (verified 15/06/2026), so the cutover gate is satisfied; M-CREATE mints the source binding then materialises the item file-first, satisfying the {59.10} source-less guard (bl-266). Coordinate sequencing with the in-progress ID-59. This REPLACES the prior “ID-110 cutover → onboarding” intent — the create verb is v1.1 canonical-pipeline scope on ID-59, NOT onboarding-routed.
  2. ID-104 (eval engine, spec_needed) → OQ-2 / B-INV-13/14/15/38/40 — HARD UPSTREAM. The per-agent AgentEvalContract + severity/variance model + touchpoint registry + recordAiCall() are designed BOTTOM-UP and OWNED by ID-104; ID-71 CONSUMES them only and MUST NOT implement them. ID-104’s eval-engine spec is sequenced BEFORE ID-71’s born-evaluable Wave 2/3 (hard dependency, not advisory). The reuse-vs-rebuild decision is ID-104’s to make from a clean-sheet design. A lib/eval/types.ts consumption-shape placeholder is a last-resort schedule fallback only if ID-104 lags — not the intent. ID-104 deliverable requirements ID-71 surfaces (must be answered, not deferred-by-omission):
    • (2a) Name the eval-result / observability UI surface explicitly. ID-71 leverages real third-party + in-repo tooling whose viewers must be named, not left implicit: the Raindrop Workshop local viewer (localhost:5899, OTel-fed — for agents/recipes) + create-skill’s eval-viewer/viewer.html (in-repo today, .claude/skills/create-skill/eval-viewer/viewer.html — for skills) + a KH-owned ID-104 results surface inheriting the deferred phase-4 /admin/refinement stub concept (specs/ai-eval/ai-eval-phase4-hitl-refinement-spec.md AC#12, ~:1143-1151 — a stub listing registered touchpoints + registry version + per-touchpoint signal/pattern/ proposal/rollback endpoints). Hosted raindrop.ai stays EXCLUDED (HR-5 client-data blocker, B-INV-15). This is an explicit ID-104 deliverable requirement.
    • (2b) Inherit-or-explicitly-scope the phase-4 HITL refinement layer. The historic phase-4 HITL refinement loop (signal aggregation → pattern detection → Claude-proposed prompt diffs → A/B → human approve → rollback registry, across all touchpoints; ai-eval-phase4-hitl-refinement-spec.md §1.2) narrowed in ID-71 to just propose + publication-gate + the B-INV-38 forcing-function guard. ID-104 MUST either inherit that full refinement layer or explicitly scope it — no silent orphan.
    • (2c) The L1/L3/L4 reuse-vs-rebuild call is DELIBERATE, not by omission. The shipped CI-wired L1/L3/L4 suite (42+17+37 checks, live in ci.yml; specs/ai-eval/mcp-evaluation-spec.md §1) must be an EXPLICIT ID-104 reuse-or-rebuild decision (OQ-2 defers the call to ID-104’s bottom-up design — this note makes the deliberateness a requirement, so the existing suite is not silently dropped or silently assumed-reused).
  3. Onboarding Task (O9 / W9.3) → B-INV-16. W9.3 headlessness is owned by the onboarding Task; ID-71 cross-references only. (Note: the create verb is NO LONGER onboarding-routed — it moved to ID-59 per intent #1; this retires PRODUCT OQ-1’s stated TECH-GATE defer-to- onboarding fallback. PLAN states the ID-59 coupling explicitly — §PLAN-readiness.)
  4. Sales-proposal own-Task(s) → B-INV-20 carry-forward (WS-10 / WS-12). Two RESEARCH inputs are carried forward to the O5/O2 own-Tasks (NOT built under ID-71): (a) the WS-10 W5.2-home assessment — W5.2 (sales-trigger→outreach) opens as its OWN Task in the O5 group, NOT inside the unratified sales-proposal-workspaces Phase-3 drafts (different outcome + different data home: the live HubSpot↔Cowork connector); the sales-proposal Phase-3 build is a separate future Task whose four arm-a/arm-b substrate drafts still need Liam ratification + canonical-pipeline reconciliation. (b) the WS-12 proposal-writer exemplar — carried as a named skill-reuse input to the O2 W2.2 proposal-assembly thread (not W5.2). Orchestrator backlogs both. (PRODUCT B-INV-20 records the same.)
  5. M-DOCS client-doc refresh (ID-71-owned deliverable; coordination note for docs-site lane). The Platform Overview + Claude Integration Guide refresh (OQ-4 lane 6, M-DOCS) is an ID-71 deliverable, but the docs live in the docs-site repo with its own docubot lane — coordinate the refresh PR with that lane so it lands in the cutover wave, not after.

Both prior ratify gates are now CLOSED (15/06/2026):

  1. OQ-2 — eval-contract approach: RESOLVED (Liam steer). Bottom-up, ID-104-owned AgentEvalContract; ID-71 consumes only; ID-104 is a hard upstream dep. The reuse-vs-rebuild decision is deferred to ID-104’s bottom-up design (NOT pre-committed to reuse).
  2. OQ-3 — goose deploy posture: RATIFIED. Co-locate on the existing IONOS/Coolify box.

No open Liam-ratification items remain in this spec.

PLAN-readiness / decomposition guidance {#PLAN-readiness}

Section titled “PLAN-readiness / decomposition guidance {#PLAN-readiness}”

Standing directive for {71.4} PLAN (inherit explicitly): there is NO 25-subtask ceiling. Decomposition is sized for QUALITY, not to a count budget. Carving standalone tasks/subtasks is SANCTIONED wherever it yields a higher-quality result. The PLAN pass MUST decompose for quality — including splitting the bottom-up agent-eval-layer design out (it is ID-104-owned and a hard upstream dependency; see OQ-2 / §X) and carving the M-CREATE write-to-store create-leg as its own slice coupled to ID-59 if that is the higher-quality path. Chain order to respect: ID-104 eval-engine (upstream) → ID-71 Wave 1 surface + rename → Wave 2 grounding/eval-guard → Wave 3 headless/fleet; M-CREATE sequences with ID-59.

PLAN must inherit (from the two fix-passes):

  • The corrected ontology RULE (OQ-4). application_type (umbrella) and form_type (sibling axis) are DISTINCT; procurement is the umbrella, bid is a form_type within it; list_active_procurement is an application read and is KEPT/NOT-renamed. PLAN must NOT inherit the inverted “procurement is a form-type” framing (a ratified-rejected anti-pattern).
  • OQ-1 fallback retired. The create-verb moved onboarding → ID-59; PRODUCT OQ-1’s stated defer-to-onboarding TECH-GATE fallback is RETIRED. PLAN states the ID-59 coupling explicitly (M-CREATE sequences with the in-progress ID-59), not the onboarding route.
  • M-DOCS (client-doc refresh) lands in the OQ-4 cutover wave, coordinated with the docs-site docubot lane (§X#5).
  • B-INV-43 / M-DEFER-O8 is recorded deferred-with-anchor — PLAN creates NO launch slice for O8 W8.4; it carries the anchor forward to a later O8 group / own-Task.
  • WS-10 / WS-12 carry-forward (§X#4) are inputs to the O5/O2 own-Tasks, NOT ID-71 slices.

B-INV → Proposed-change mapping (orphan-check) {#M}

Section titled “B-INV → Proposed-change mapping (orphan-check) {#M}”

1:1 mapping; every B-INV has a change (or an explicit “no code change — satisfied by X”).

B-INVProposed changeNotes
B-INV-1M1L4 enumerates {O1/O4/O6 reads + W5.6} only
B-INV-2M2each driven MCP-only to terminal
B-INV-3M3O4 widened beyond KH state
B-INV-4M4 / M29O6 five-layer + resolution affordance
B-INV-5M5W5.6 re-syndication MCP-only
B-INV-6M6 / M30 / M-CREATEpropose-rows to queue; publish human-gated; create-into-store via M-CREATE (OQ-1)
B-INV-7M7per-workflow auto-apply switch off at launch
B-INV-8M8identical surface both runtimes
B-INV-9M9inventories equal
B-INV-10M10no code change — remote MCP exists (app/api/mcp/[transport]/route.ts); evaluated as a connection
B-INV-11M11one trigger-driven push channel
B-INV-12M12 / M-CREATEsanctioned destinations; net-new refused; M-CREATE = create-into-store leg, writeBackFileFirst = edit-back
B-INV-13M13 / M38consumes ID-104-owned bottom-up AgentEvalContract (hard upstream dep)
B-INV-14M14evals KH-owned (not Raindrop-proprietary); harness is ID-104-owned bottom-up
B-INV-15M15in-house metric + egress assertion; metric computed on ID-104 infra
B-INV-16M16no code change — W9.3 cross-reference only
B-INV-17M17scope enumeration matches
B-INV-18M18 / MVPone piloted recipe
B-INV-19M19fleet additive, Task+metric-gated
B-INV-20M20no code change — net-new capabilities not built; reserved concepts + WS-10/WS-12 carry-forward (§X#4)
B-INV-21M21 / MVPpilot proves HC-1/2/4; pilot-gated
B-INV-22M22verdicts anchored to O/W numbers (this §M)
B-INV-23M23keep-concept = outcome retained
B-INV-24M24no retire orphans a ranked outcome
B-INV-25M25one human-facing server; fleet additive
B-INV-26M26no code change — deferral trigger recorded (OQ-5/§Proposed M25/26)
B-INV-27M27one find; trio semantics as params
B-INV-28M28ontology-grounding affordance
B-INV-29M29one where-are-we-exposed five-layer
B-INV-30M30 / OQ-5one queue + facet (lib/attention.ts)
B-INV-31M31 / OQ-4completing-forms + bid→forms rename
B-INV-32M32get/dedup/assign one-or-many consolidation
B-INV-33M33two-step retrieval preserved
B-INV-34M34layer placement holds
B-INV-35M35one grounding shape per touchpoint
B-INV-36M36no silent fallback
B-INV-37M37outputSchema forward-standard
B-INV-38M38forcing-function guard
B-INV-39M39AI-invisibility
B-INV-40M40eval is the gate, not the schema
B-INV-41M41 / M-DOCS / OQ-4PI-13/14 fold into rename; M-DOCS = Platform Overview + Claude Integration Guide refresh
B-INV-42M42PI-15 retired; kb://taxonomy replacement
B-INV-43M-DEFER-O8no launch code change — deferred-with-anchor (O8 W8.4 argument-quality; own-Task/later-group; born-evaluable when it lands)

Coverage: all 43 invariants mapped; no orphan invariants, no orphan changes. Five “no code change / no launch change” entries (B-INV-10 remote-MCP exists, B-INV-16 W9.3 cross-ref, B-INV-20 net-new not built, B-INV-26 deferral-trigger-recorded, B-INV-43 deferred-with-anchor) are explicit satisfied-by-X records, not gaps. Net-new changes added across the two fix-passes: M-CREATE (OQ-1 write-to-canonical-store create-leg — binds B-INV-6 + B-INV-12, no new invariant) and M-DOCS (client-doc refresh — binds B-INV-41). B-INV-43 is the one NEW invariant (PRODUCT §G, 42→43) and maps to the recorded anchor M-DEFER-O8 (no launch code). The 1:1 B-INV↔change coverage is preserved across the amended PRODUCT + TECH with no orphan invariant and no orphan change.


End of {71.3} TECH (second fix-pass applied 15/06/2026). Five TECH OQs resolved: OQ-1 write-to-canonical-store (cutover SATISFIED — reference_ingest LIVE on prod; M-CREATE create-leg on the ID-59 substrate); OQ-2 bottom-up, ID-104-owned AgentEvalContract (hard upstream dep — ID-71 consumes only; reuse-vs-rebuild deferred to ID-104; ID-104 must also name the eval/observability UI surface + inherit-or-scope phase-4 HITL + make the L1/L3/L4 call deliberate — §X#2a-c); OQ-3 co-locate RATIFIED + stable-surfaces + pin-and-soak; OQ-4 one-PR bid→forms rename with the corrected ontology RULE (application_type umbrella vs form_type sibling axis; procurement is the umbrella, bid is a form_type within it — list_active_procurement KEPT as an application read, NOT renamed); OQ-5 facet as a NEW greenfield MCP tool over the existing lib/attention.ts substrate (source_document_change scoped out of v1). 43 B-INV + M-CREATE + M-DOCS → Proposed-change 1:1 mapping in §M (Five explicit “no code change” records; B-INV-43 = new deferred-with-anchor for O8 W8.4). Cross-Task coordination intents (§X: ID-59 for create-verb; ID-104 hard-upstream for eval + 3 named deliverable requirements; onboarding intent REMOVED from the create-verb path; WS-10/WS-12 carry-forward to sales-proposal Task; M-DOCS docs-site coordination). No open Liam-ratification items. NO 25-subtask ceiling — {71.4} PLAN decomposes for quality (§PLAN-readiness). Feeds {71.4} PLAN decomposition.