Wave 5 addendum appended below — original audit covered FIN Central vault and DB-side projections only. Wave 5 closes the gap on
nathel-intranettenant vault, Central↔Tenant taproot sync, tenant vault webhook, tenant projector, and end-to-end cross-taproot propagation. All Wave 5 verdicts: FAIL or DEGRADED. The tenant content layer has no producer and no transport. See “Wave 5 Addendum” section at the end of this doc.
FIN Projection System Audit — 2026-05-17
Scope: Both JSONB and MD format projections, across both FIN Central and Nathel tenants, including the vault → DB ingestion pipeline, the agent runtime’s tool access, the human UI’s drill-through experience, the closed-loop refresh + self-improvement mechanism, and the supporting registers/indexes.
Method: 15 work streams in 4 parallel waves, dispatched as general-purpose subagents with self-contained briefs. Each WS produced an evidence-backed report under .audit-2026-05-17/waveN/ws-NN-*.md. This document consolidates findings and the remediation backlog.
Master HEAD at audit time: dc2e36a2 (after PR #793 + #794 + #798 squash-merges).
TL;DR — Executive Summary
The “agentic ERP with closed-loop self-improving MD projection system that fuses universal + tenant intelligence” is architecturally sound but operationally cold. Schema is in place, RLS hardened, dispatch loop runs, ~544 tools registered, vault has 487 well-formed entity profiles. But the runtime never actually executes end-to-end:
- 6.4% of the expected 4D projection matrix is operational (3 cells of 47 non-N/A; per WS-13).
- The MD renderer worker (
fin-central-renderer) is not deployed — entry-point file missing, Hyperdrive ID is placeholder; the 331 entity_doc rows are one-shot hand-seeds from 2026-05-17 02:02-03:34 UTC (per WS-03, WS-05). - The agent never uses the MD tools — 0 real-agent activations in 30 days; system prompt steers to legacy
get_commodity_profile(per WS-08). - The UI strips what little exists — drill buttons are dead clicks (
EntityRoute.tsx:282calls CardRenderer with NOonActionprop); linked_entities never make it into tool envelopes (per WS-14). - fin-central staging is 21 migrations behind nathel — federation has nowhere to land (per WS-01, WS-12).
- The self-improvement signal substrate IS in place — 19,198
system.scenario_eval_resultevents + 45knowledge.domain_stale+ 100%-populatedknowledge.confidence/knowledge.version— but zero consumers wire them back into projections (per WS-15).
The audit’s framing: This is a deployment + back-fill + system-prompt-rewrite + ~5 small wiring patches problem, not a redesign. PR #798 shipped components; integration of those components into a working pipeline did not happen. The fixes are bounded.
Health summary table
| WS | Domain | Verdict | Critical finding |
|---|---|---|---|
| WS-01 | DB inventory | DEGRADED | nathel 18/20 projections present, 10+ empty; fin-central 5/20 (21 mig behind); mig 053+054 missing |
| WS-02 | Vault MD inventory | PASS | 547 .md / 98.9% frontmatter / 487 entity_profiles; 50/52 wikilinks broken (type-prefix resolver missing) |
| WS-03 | CF assets | DEGRADED | fin-central-renderer Worker NOT deployed (placeholder Hyperdrive ID); 5 parity crons broken on pnpm v10/9.15.4; phantom out-of-band staging deploy |
| WS-04 | JSONB freshness | DEGRADED | 7 fresh / 11 empty / 3 missing updater; doc_backlink dispatch wiring missing; intel_inbox rejects 100% of 1,400+/day events; 360s are attribute-key-mismatch stubs |
| WS-05 | MD completeness | FAIL | doc_backlink=0 rows; 4/5 entity domains 0 rows; body_md empty across all 331 rows; 70% DB commodities have no vault MD source; fin-central missing entity_doc table entirely |
| WS-06 | Universal+overlay | DEGRADED | 0 tenant overlay rows; composition is override-not-merge; nathel HAS 708 price_observation + 33 vendor_quote events in 30d but no consumer materializes them |
| WS-07 | Tool access | DEGRADED | 8 JSONB projections have NO MCP reader; pulse_admin reachable only via Hono; 5 brief tools fail INVALID_EVENT_PAYLOAD; 5 MD tools in SPRINT_TOOLS but not in any role catalog |
| WS-08 | Agent usage | FAIL | 0 real-agent activations of MD tools in 30 days; system prompt teaches legacy get_commodity_profile as authoritative; jargon-heavy descriptions |
| WS-09 | KF content | DEGRADED | 386 of 487 vault profiles never ingested (variety+vendor); vault STALER than DB on commodities; wikilink resolver bug (1-line fix); 8 READMEs collide on doc_id |
| WS-10 | Drill-through | DEGRADED | 1 PASS + 2 PARTIAL + 2 FAIL on 5 paths; walk_provenance never reaches events; 0 vendor actors break vendor drill; graph data EXISTS (2,620 links) but adapter drops it |
| WS-11 | Closed-loop | FAIL | 16/19 dispatcher-wired; renderer cron not in wrangler triggers; no staleness listener; 4 feedback event types defined but 0 events; watchdog has 0 runs ever |
| WS-12 | Registers | DEGRADED | approved-projection-tables.json has unresolved git merge conflict (lines 167/196/207); schema bifurcation on slots 050/053/054; fin-central 21 mig behind; 4 phantom SPRINT_TOOLS |
| WS-13 | Parity matrix | DEGRADED | 6.4% operational (3 of 47 non-N/A cells); central column uniformly missing; MD shared/commodity-only; federation publisher mature but intentionally frozen |
| WS-14 | Human UI | DEGRADED | 4/5 routes load; drill buttons are dead clicks (CardRenderer no onAction); explore_vendor returns vendor_not_found for vendors visible via other tools; no tool envelope carries linked_entities |
| WS-15 | Perf + self-improvement | DEGRADED | 8/14 tools meet <2s p95; median projection age 12h; 19,198 eval events archived without consumer; knowledge.confidence+.version 100% populated but never updated from feedback |
Convergent themes
Theme 1 — “Shipped schema + tools, not integration”
PR #798 (137/137 task ship) landed schema (mig 051..068), tools (14 new SPRINT_TOOLS), and dispatcher wiring (15 updaters). Wave 1+2 confirm those components exist on master. But three integration steps never happened:
- Deployment —
fin-central-rendererWorker config exists atpackages/mcp-server/wrangler.fin-central.jsoncbut references a non-existent entry-point file + a placeholder Hyperdrive ID. ZEROrenderer.*orentity_doc.*events ever in events table (WS-03). - Cron registration —
entity-doc-renderer-worker.tsexportsRECOMMENDED_CRON_SCHEDULESbutwrangler.jsonctriggers.cronsdoesn’t include them, andpackages/mcp-server/src/index.ts:3480-3760scheduled()handler has norunRendererLoopIterationcall (WS-11). - Workflow auth — the 5 parity cron workflows (PR #794) are present but broken on
pnpm/action-setup@v4 version: 10vs repopackageManager: pnpm@9.15.4(WS-03).
Theme 2 — “Producer dead, composition shallow, consumer ignoring”
Even granting the substrate works, the three layers of the pipeline each have an independent failure:
| Layer | Failure | Concrete evidence |
|---|---|---|
| Producer | Renderer worker dead; body_md empty across all 331 rows; commodity-only domain coverage | WS-03, WS-05 |
| Composition | 0 tenant overlay rows; SQL is LIMIT 1 tenant-prefer (override, not merge); nathel signal substrate (708+33/30d) never materialized into knowledge | WS-06 |
| Consumer | 0 real-agent MD-tool activations in 30 days; system prompt at reasoning.ts:232-305 + exploration-context.ts:707 steers to get_commodity_profile as authoritative; MD tool descriptions are jargon-heavy (“Plane 3 / TZ-5 §5.1”) | WS-08 |
Theme 3 — “Self-improvement substrate exists, no closer”
The richest discovery of the audit (WS-15) — the system already produces the signals it would need to self-improve:
- 19,198
system.scenario_eval_resultevents on nathel (last 30d) with score/latency/category payloads - 45
knowledge.domain_staleevents, 81knowledge.freshness_check_completedevents knowledge.confidence100% populated across 14,885 rowsknowledge.version100% populated, max=330
None of these have a consumer that closes the loop into entity_doc re-renders or trust_score updates. ~30 LOC of glue per loop, not a redesign.
Theme 4 — “Graph data exists, transport layer drops it”
The drill-through experience is broken NOT because the graph is missing but because two adapters drop it:
get_entity_profilereturns the profile withlinked_entitiesarrays present inknowledge.content(2,620 cross-entity links across 330 commodities). ButEntityRoute.tsx:124-249 commodityProfileToEnvelopehand-extracts onlys01_identity— drops 28 of 29 KF sections AND the linked_entities array (WS-10, WS-14).- Even if linked_entities reached the renderer,
EntityRoute.tsx:282calls<CardRenderer>with NOonActionprop, so every drill button across every entity URL is a dead click (WS-14).
doc_backlink_projection has 0 rows but the data to reconstruct it lives in knowledge.content.linked_entities — a one-time back-fill query could populate it (WS-09, WS-10).
Theme 5 — “Vault is staler than DB”
Counter-intuitive but well-evidenced (WS-09):
- For the 98 overlapping commodities, DB content is more comprehensive than vault (apricot vault = 4-section stub, DB = 3142-attribute profile; apple vault = 3735 attrs, DB = 3647).
- 233 of 331 DB commodity rows have NO vault MD source (bulk-render scripts wrote richer content than the vault holds).
- 386 of 487 vault profiles (varieties + vendors) NEVER reached DB at all.
This inverts the assumption that vault = source of truth. Reality: vault is one input, the bulk-render machinery is another, and the DB has accumulated content from both — but without source_row_version provenance (95.5% of rows have 'unknown', per WS-05), no one knows which is which.
Theme 6 — “fin-central is uninhabited”
WS-01, WS-12, WS-13 all converge:
- fin-central staging Neon: 0 events, 1 knowledge row, 3 actors, 5/20 projection tables, 21 migrations behind (last applied
047_fts_hybrid_search). - entity_doc_projection table doesn’t even exist on fin-central.
- Federation publisher code is mature (EIF-RM-026/027 — k≥5 anonymization, HMAC-hashed vendor IDs, customer fields dropped, gated behind
FEDERATION_PUBLISH_ENABLED='true') but ZEROfederation.*events ever fired — and per memoryfeedback_federation_publisher_frozen.mdthis freeze is intentional (single-tenant reality).
So fin-central is structurally an empty branch waiting for the day federation activates. The audit’s federation-readiness verdict is “intentionally not ready” — not a bug.
Prioritized remediation backlog
Listed in recommended fix order. Each item names file path(s) where applicable.
P0 — System-correctness blockers (15 items, mostly small)
| # | Title | Location | Estimated effort |
|---|---|---|---|
| P0-01 | Resolve git merge conflict in audit/card-window-system/approved-projection-tables.json (lines 167, 196, 207) — runtime parses this; invalid JSON breaks projection registry | audit/card-window-system/approved-projection-tables.json | 5 min |
| P0-02 | Fix pnpm/action-setup@v4 version: 10 conflict in 5 parity cron workflows — either drop version: 10 or bump root packageManager to 10.x | .github/workflows/parity-*.yml (5 files) | 10 min |
| P0-03 | Deploy fin-central-renderer Worker: write the entry-point file + replace placeholder Hyperdrive ID + add to deploy pipeline | packages/mcp-server/src/workers/fin-central-renderer-worker.ts (new), wrangler.fin-central.jsonc | 2-3 hr |
| P0-04 | Wire runRendererLoopIteration into Cloudflare scheduled() handler + add cron to wrangler.jsonc triggers.crons | packages/mcp-server/src/index.ts:3480-3760 + packages/mcp-server/wrangler.jsonc | 30 min |
| P0-05 | Write event-handlers/projection-doc-backlink.ts + register in dispatchProjectionUpdaters; back-fill from knowledge.content.linked_entities (2,620 existing edges) | packages/mcp-server/src/event-handlers/projection-doc-backlink.ts (new) + event-handlers/index.ts:113-131 | 2-3 hr |
| P0-06 | Write entity_doc staleness listener — consume knowledge.row_updated / knowledge.domain_stale events and flip entity_doc_projection.status='stale' (~30 LOC per WS-15) | event-handlers/projection-entity-doc-staleness.ts (new) | 1 hr |
| P0-07 | Write system.scenario_eval_result consumer — 19,198 events being archived; consume into entity_doc_projection.trust_score + knowledge.confidence updates | event-handlers/projection-eval-feedback.ts (new) | 2 hr |
| P0-08 | Fix attributeEventToInbox() upstream — propagate source_email_inbox/inbox/to_address into event data from EIF S0.5 intake classifier + S4 extractor consumers | packages/mcp-server/src/intake/intake-classifier.ts, packages/mcp-server/src/intake/extractor-consumer.ts | 2 hr |
| P0-09 | Fix attribute-key mismatch in commodity_360_projection + vendor_360_projection recompute — events use data.commodity/data.vendor, projection queries data.commodity_key/data.vendor_key | packages/mcp-server/src/event-handlers/projection-commodity-360.ts, projection-vendor-360.ts | 30 min |
| P0-10 | Fix extractWikilinkTarget to output commodity_profile_X (matching DB doc_id shape) instead of commodity_X — 1-line fix | packages/mcp-server/src/ingestion/upsert-doc.ts:342-356 | 5 min |
| P0-11 | Wire EntityRoute to pass onAction prop to <CardRenderer> — currently all drill buttons across every entity URL are dead clicks | packages/workspace/src/routes/EntityRoute.tsx:282 | 30 min |
| P0-12 | Add linked_entities field to tool envelope schema (CardSpec) AND ensure get_entity_profile + explore_commodity etc. emit it | packages/shared/src/schemas/cardspec.ts, packages/mcp-server/src/tools/get-entity-profile.ts, explore-commodity.ts | 2 hr |
| P0-13 | Fix explore_vendor — returns vendor_not_found for vendor IDs that lookup_vendor + explore_commodity resolve (vendor 000876 LA MINITA FRESH LLC is a confirmed example); 0 vendor actors on nathel means vendor lookup must use party + relationship tables, not actors WHERE role='vendor' | packages/mcp-server/src/tools/explore-vendor.ts, lookup-vendor.ts | 2-3 hr |
| P0-14 | Sync fin-central staging — apply migrations 048..068 to fin-central; create entity_doc_projection + doc_backlink_projection tables; backfill shared-tenant rows | db/migrations/048-068 + apply via scripts/apply-staging-migrations.ts to project cool-scene-51259109 branch br-super-sky-apy08f2d | 1 hr |
| P0-15 | Resolve schema bifurcation on slots 050, 053, 054 — nathel has duplicate-slot applied migrations (veteran_panelist_role + inventory_current_projection at 050; materialized_views + customer_360_projection at 055); parity-integration is missing slot 050 entirely. Re-number all conflicting migs to new slots and rebase. | db/migrations/*.sql | 2-3 hr |
P1 — Quality/agent-effectiveness blockers (12 items)
| # | Title | Location | Estimated effort |
|---|---|---|---|
| P1-01 | Rewrite system prompt to teach MD tools — replace “cross-reference get_commodity_profile for authoritative data” with search_entity_profiles + get_entity_profile + walk_provenance chain; de-jargon descriptions | packages/agent/src/personas/reasoning.ts:232-305, packages/agent/src/exploration-context.ts:707, tool descriptions in packages/mcp-server/src/tools/get-entity-profile.ts etc. | 4 hr |
| P1-02 | Add 5 MD-projection tools (get_entity_profile, search_entity_profiles, walk_provenance, get_doc_graph, get_knowledge_health) to role catalogs — currently only admin gets *, SMS/voice agents can’t pick | packages/mcp-server/src/tools/role-tool-catalog.ts | 1 hr |
| P1-03 | Add get_knowledge_health to SPRINT_TOOLS (in toolRegistry but missing from allow-list → fails authz with TOOL_UNKNOWN) | packages/agent/src/permissions.ts | 5 min |
| P1-04 | Fix 5 generate_*_brief tools failing INVALID_EVENT_PAYLOAD (brief.generated schema drift — requires shift_type + posted_to_slack) | packages/mcp-server/src/tools/generate-*-brief.ts (5 files) | 1 hr |
| P1-05 | Wire 8 JSONB projections to MCP readers (buyer_team_dashboard, commodity_360, customer_360, daily_company_pulse, location_intel, sales_team_dashboard, supply_chain_position, vendor_360) — currently explore_* tools read raw events instead of these projections, which is why the 360s remain stubs (no read pressure to validate the projection is correct) | packages/mcp-server/src/tools/get-*-360.ts (new) + update explore_*.ts to prefer projection | 3-4 hr |
| P1-06 | Add MCP reader for pulse_admin_active_holds (currently reachable only via Hono /api/pulse route) | packages/mcp-server/src/tools/get-pulse-admin.ts (new) | 1 hr |
| P1-07 | Fix explore_commodity p50=7s (3.5x over CLAUDE.md ≤2s target) — bypasses commodity_360_projection and does wide KB scan; route through projection once P1-05 lands | packages/mcp-server/src/tools/explore-commodity.ts | 2 hr |
| P1-08 | Fix walk_provenance to query doc_backlink_projection (currently only get_doc_graph does); also fix fact_citations to use event_id instead of abstract commodity:<slug> keys so provenance reaches events | packages/mcp-server/src/tools/walk-provenance.ts + citation emitters in packages/mcp-server/src/ingestion/extract-fact-citations.ts | 3 hr |
| P1-09 | Implement renderer routes for vendor / customer / location / lot domains (currently routeRenderer() throws RendererNotImplementedError for all 4) — Each domain has substantial vault content (142 vendors + …) that’s stranded | packages/mcp-server/src/ingestion/renderers/route-renderer.ts + per-domain renderer files | 1-2 days |
| P1-10 | Resolve 24 silent render failures (19 s01_identity TypeError on acai-berry section sub-renders, 5 embedding_model NOT NULL violations on L-named commodities); raise retry cap from 1 to 3; add ops alerting on entity_doc_projection.status='failed' | packages/mcp-server/src/ingestion/renderers/entity-doc-renderer-worker.ts, packages/mcp-server/src/ingestion/upsert-doc.ts | 2 hr |
| P1-11 | Fix renderer to populate source_row_version correctly (95.5% of 331 rows have 'unknown' → drift detection blind even after P0-06 lands) | packages/mcp-server/src/ingestion/renderers/entity-doc-renderer-worker.ts | 30 min |
| P1-12 | Add post-deploy /health version-match gate to eval-pipeline.yml deploy-staging job — catches out-of-band wrangler deploys (today’s qc-receiving-cte-20260517T144520Z phantom deploy clobbered CI’s dc2e36a) | .github/workflows/eval-pipeline.yml | 30 min |
P2 — Hygiene + smaller (8 items)
| # | Title | Location | Estimated effort |
|---|---|---|---|
| P2-01 | Add frontmatter to 3 vault audits (HISTORICAL_DATA_BACKFILL_2026-05-16, ROLE_DRILLTHROUGH_DESIGN_2026-05-16, PROJECTION_PARITY_AUDIT_2026-05-16) — won’t index without it | vault docs/audits/*.md | 15 min |
| P2-02 | Build wikilink resolver for vault’s type-prefix shorthand ([[commodity:X]], [[vendor:X]], [[canon/people/X]]) — 50 of 52 wikilinks currently broken | vault _scripts/link-resolver.ts (new) or via Obsidian-style alias map | 4 hr |
| P2-03 | Fix 8 README.md files sharing doc_id: readme (collide on upsert, silently destroy 7 of 8) | vault docs/**/README.md (8 files) | 20 min |
| P2-04 | Fix FIN_PLATFORM_DOC_INDEX.md — ~12 broken absolute links to fin-agentic paths (e.g., /AGENTS.md, /db/schema.sql); either convert to GitHub URLs or remove | vault FIN_PLATFORM_DOC_INDEX.md | 30 min |
| P2-05 | Tag vault docs with data_plane other than universal_intelligence where appropriate (currently monovalent → federation gate has no signal); especially flag avocado.md which leaks Nathel-specific operational pattern at line 124 into universal plane | vault docs/**/*.md author discipline | ongoing |
| P2-06 | Fix variety/vendor naming mismatch — commodities link to display slugs (crimson_sweet) but variety files stored as anonymous codes (cmd_00057.md); causes 98.2% orphan rate in linked_entities graph | vault rename OR build alias map | 2 hr |
| P2-07 | Author the 9 missing W1/W2 PROJECTION_INDEX types — kb_health_summary, tenant_readiness, region_intelligence, season_window, agent_enrichment_metrics, cohort_status, etc. have 0 vault docs despite being in the index | vault | author backlog |
| P2-08 | Add normalizer for docId shape inconsistency — commodity_profile_blackberry (underscored, internal) vs commodity_profile/blackberry/v1 (slashed/versioned, external API) | packages/shared/src/utils/doc-id-normalize.ts (new) | 1 hr |
Frozen / intentional (do not fix)
| # | Item | Why |
|---|---|---|
| F-01 | Federation publisher (EIF Slice 7 — anonymization verifier + publisher + cron + 6 acceptance tests) gated behind FEDERATION_PUBLISH_ENABLED='true' | Single-tenant reality per feedback_federation_publisher_frozen.md; system resources + rights not aligned to activate; code preserved as future-proof option for training-data scenario |
| F-02 | fin-central tenant empty | Intentional — no production tenant relies on it yet; sync to current schema only when first federation peer onboards |
| F-03 | OpenClaw integration tests | User has confirmed (this session) OpenClaw is not in scope for current phase; eval-smoke-staging timeout on openclaw/staging-http.test.ts is acceptable |
Recommended fix sequencing
Three tiers of work, each independently shippable:
Sequence A — “Turn on the pipeline” (~1 day)
Goal: Get the renderer running, the dispatcher firing, the consumers consuming. Most impact per LOC.
- P0-01 (registry JSON merge conflict) — 5 min
- P0-02 (cron pnpm fix) — 10 min
- P0-04 (cron+scheduled wiring) — 30 min
- P0-05 (doc_backlink updater + back-fill) — 2-3 hr
- P0-06 (staleness listener) — 1 hr
- P0-09 (360 attribute-key fix) — 30 min
- P0-10 (wikilink resolver fix) — 5 min
- P1-11 (source_row_version populate) — 30 min
- P0-03 (deploy renderer worker) — 2-3 hr — DEFERRED if Sequence A only running on existing renderer cron via P0-04
End state: Projections refresh on event; backlinks populated; 360s carry real data; renderer cron actually fires.
Sequence B — “Surface to agent” (~1 day)
Goal: Make the agent actually use the MD layer and the user able to drill through.
- P0-11 (EntityRoute onAction wiring) — 30 min
- P0-12 (linked_entities in tool envelope) — 2 hr
- P0-13 (explore_vendor + vendor-actor gap) — 2-3 hr
- P1-01 (system prompt rewrite — teach MD tools) — 4 hr
- P1-02 (5 MD tools into role catalogs) — 1 hr
- P1-08 (walk_provenance reads doc_backlink + cite event_ids) — 3 hr
- P1-07 (explore_commodity speed via 360) — 2 hr
End state: Drill buttons work, agent prefers MD tools, drill-through traverses actual graph, provenance reaches events.
Sequence C — “Close the self-improvement loop” (~0.5 day)
Goal: The 19,198 archived eval signals start updating content quality.
- P0-07 (eval-feedback consumer → trust_score + re-render trigger) — 2 hr
- P0-08 (inbox-lineage propagation upstream) — 2 hr
- P1-10 (24 silent renders + retry cap + alerting) — 2 hr
- P1-12 (post-deploy version-match gate) — 30 min
End state: Low-scoring profiles auto-flagged for re-render; intel inbox starts populating digest; render failures visible; phantom deploys caught.
Sequence D — “Vault hygiene + back-fill” (~0.5 day)
Goal: Bring vault back to parity with DB.
- P0-14 (sync fin-central staging) — 1 hr
- P0-15 (resolve mig slot collisions) — 2-3 hr
- P1-09 (vendor/customer/location/lot renderers) — 1-2 days (largest item — split into Sequence D2)
- P2-01..P2-08 vault hygiene — ~6 hr total
Sub-report index
Detailed evidence for each work stream:
| WS | Report |
|---|---|
| WS-01 | .audit-2026-05-17/wave1/ws-01-db-inventory.md |
| WS-02 | .audit-2026-05-17/wave1/ws-02-vault-inventory.md |
| WS-03 | .audit-2026-05-17/wave1/ws-03-cf-inventory.md |
| WS-04 | .audit-2026-05-17/wave2/ws-04-jsonb-freshness.md |
| WS-05 | .audit-2026-05-17/wave2/ws-05-md-completeness.md |
| WS-06 | .audit-2026-05-17/wave2/ws-06-overlay-composition.md |
| WS-07 | .audit-2026-05-17/wave2/ws-07-tool-access.md |
| WS-08 | .audit-2026-05-17/wave2/ws-08-agent-usage.md |
| WS-09 | .audit-2026-05-17/wave3/ws-09-kf-content.md |
| WS-10 | .audit-2026-05-17/wave3/ws-10-drill-through.md |
| WS-11 | .audit-2026-05-17/wave3/ws-11-closed-loop.md |
| WS-12 | .audit-2026-05-17/wave3/ws-12-registers.md |
| WS-13 | .audit-2026-05-17/wave3/ws-13-parity-matrix.md |
| WS-14 | .audit-2026-05-17/wave4/ws-14-ui-projections.md |
| WS-15 | .audit-2026-05-17/wave4/ws-15-perf-self-improvement.md |
Methodology notes
- Subagent contract: Each WS dispatched as
general-purposebackground agent with self-contained brief + structured===WS-NN-RESULT===return block per CLAUDE.md §Subagent Orchestration Rules. - Subagent state: Returns
VERIFYINGper §1; this audit doc’s orchestrator (Claude) is responsible for promoting to verified findings — done above. - Read-only: All 15 work streams were read-only audits. No DDL, no DML, no deploys, no code edits.
- Connection info pre-resolved: Each subagent received Neon project + branch IDs, vault path (
/tmp/fin-central-intranetHEAD74cf796), staging Worker URL + bypass header. - Stale-worktree caveat: This worktree (
heuristic-kepler-4f9104) is stale relative toparity-integration(per WS-12: duplicate 048 migrations + 049-062 gap). One WS (WS-15) appears to have searched this worktree for cron workflows — they exist on master via PR #794. The eval workflow that IS running on master iseval-baseline.yml.
Open questions for follow-up
- Federation activation criteria — what triggers
FEDERATION_PUBLISH_ENABLED=true? Memory says “frozen, single-tenant reality; rights + resources not aligned”. Need explicit unfreeze conditions. - Renderer ownership — should the renderer worker run on Cloudflare (per current design) or migrate to a long-running Node process? Cloudflare cron has limits.
- Vault vs DB authoring contract — given DB content is richer than vault for commodities, is the vault meant to be authoritative? Or is it a publishing/snapshot target? This needs a written decision before Sequence D2.
- Hot-path tools NOT going through projections — the WS-08 telemetry shows
get_events,query_knowledge,get_load_context_package,list_work_threadsare the production hot path, but the audit focused on projection-read tools. There may be a parallel parity audit needed for the hot-path tools (do they have JSONB projections behind them?).
Wave 5 Addendum — Tenant Intranet + Cross-Taproot Propagation (2026-05-17, 16:00 UTC)
Trigger: the original 15-WS audit covered FIN Central vault (fin-central-intranet) and the DB-side projections, but missed (a) the separate nathel-intranet tenant vault, (b) the Central → Tenant taproot sync mechanism, (c) the tenant vault webhook ingest, (d) the tenant projector write-back, and (e) end-to-end cross-taproot propagation. Wave 5 ran 5 additional parallel subagents to close that gap.
Wave 5 health summary
| WS | Domain | Verdict | Critical finding |
|---|---|---|---|
| WS-16 | nathel-intranet content | DEGRADED | Pure skeleton: 14 .md files all scaffolding READMEs, 0 content/projected/mirrored docs; vault-webhook is .placeholder; 0 of 16 advertised projections have produced output |
| WS-17 | Central → Tenant taproot sync | FAIL | Wave 1 (poll) + Wave 4 (push) BOTH not implemented; no code skeleton, no wrangler config, no GH workflow; 0 sync events ever; design exists ONLY in _taproot_mirror/README.md — under-spec’d |
| WS-18 | Tenant vault webhook → Neon | FAIL | 3 stacked show-stoppers: route unmounted in index.ts, handler skeleton (no upsertDoc call), no GH webhook configured on nathel-intranet; nathel knowledge.domain='doc' = 0 rows (vs 5,272 total across 27 other domains); 0 vault.* events all-time |
| WS-19 | Tenant projector → vault | FAIL | Vapor: zero octokit usage anywhere; no tenant Cloudflare Worker; 13 promised renderers have NO source files; one renderer comment misleading (says it writes MD but actually writes to DB); naming collision with DB CQRS projection layer masks absence |
| WS-20 | Cross-taproot E2E (3 commodities × 5 stages) | DEGRADED | 0/3 chains operational; breakpoint at Stage 2 (fin-central Neon empty) AND Stage 3 (taproot mirror dormant); agent reachability passes 6/6 but returns BYTE-IDENTICAL content per commodity; cucumber has 314 nathel price observations / 30d but data.tenant overlay returns null — federation plumbed in schema but unwired in assembly |
The 3-rung stack — none implemented
The “agentic ERP with federated tenant + central intelligence” requires three stacked layers. All three are independently broken:
| Rung | Layer | Wave 5 verdict |
|---|---|---|
| 1 | Central MD renderer (fin-central-renderer writes to fin-central Neon + supposedly to vault) | WS-03 (original): config exists with placeholder Hyperdrive ID; never deployed |
| 2 | Central → Tenant sync transport (_taproot_mirror/ populated from fin-central-intranet) | WS-17: does not exist (no code, no spec beyond a one-page README) |
| 3 | Tenant overlay renderer (writes tenant-specific MD into nathel-intranet from tenant events) | WS-19: vapor (zero code; 13 promised renderers have no source files) |
Fixing rung 1 alone doesn’t unblock the chain — rungs 2 and 3 are also gone.
The critical new finding (WS-20 bonus probe)
Cucumber has 314 tenant-specific
signal.price_observationevents on nathel in last 30 days.get_entity_profile(commodity:cucumber, tenantId:nathel)returnsdata.tenant = null.
The federation overlay is plumbed in the response schema but unwired in the assembly logic. The substrate exists (314 events). The contract supports it (data.tenant field). The code that fills the field doesn’t query the substrate.
This is a single-fix gap — wire the assembly to query nathel signals and populate data.tenant. Same pattern as the system-prompt rewrite (WS-08): tools exist, agent doesn’t pick them; data exists, surface doesn’t render them.
Naming collision — architectural blind spot
WS-19 surfaced a structural confusion:
| Term | What it means | State |
|---|---|---|
”Projection” in PROJECTION_CATALOG_2026-05-16.md | 17 DB tables, event-driven CQRS read models | Live (16 wired updaters per WS-11) |
“Projection” in nathel-intranet/PROJECTION_INDEX.md | 13 MD documents to be deposited in the tenant vault by an agent-projector | None implemented; renderers don’t exist |
The two unrelated systems share the word “projection”. The catalog never references the vault-deposit projector. The naming collision masks the absence — verbal confidence in the DB CQRS layer implies that the vault projector is also working, when it’s vapor.
Remediation suggestion: rename the vault-deposit layer to “tenant vault renderer outputs” or “tenant projector deposits” to distinguish from CQRS projections. Then the absence becomes obvious in any future audit.
Updated remediation backlog (Wave 5 additions)
P0 additions
| # | Title | Location | Estimated effort |
|---|---|---|---|
| P0-W5-01 | Mount vaultWebhookRoutes in packages/mcp-server/src/index.ts — currently POST /webhooks/github-vault returns 404 even though handler code exists | packages/mcp-server/src/index.ts | 15 min |
| P0-W5-02 | Complete the tenant vault webhook handler — currently a skeleton that doesn’t invoke upsertDoc; file’s docstring marks it “next session”. Wire fetch-from-GitHub + parseDocMarkdown + upsertDoc (which is fully implemented per §11.5.3) | packages/mcp-server/src/routes/github-vault-webhook.ts | 2-3 hr |
| P0-W5-03 | Configure GitHub webhook on fin-platform/nathel-intranet to POST to the staging Worker /webhooks/github-vault endpoint (once P0-W5-01 lands) | gh api repos/fin-platform/nathel-intranet/hooks POST | 15 min |
| P0-W5-04 | Wire get_entity_profile assembly to populate data.tenant overlay from tenant-side signal events (cucumber has 314 ready signals; cherry/grape have similar) | packages/mcp-server/src/tools/get-entity-profile.ts | 2-3 hr |
| P0-W5-05 | Build Central → Tenant taproot sync — choose one of: (Wave 1) Cloudflare Worker poll on 5-min cron via GitHub Commits API, OR (Wave 4) repository_dispatch from fin-central-intranet on merge | New file packages/mcp-server/src/workers/taproot-sync-worker.ts + wrangler config | 1-2 days |
| P0-W5-06 | Implement at least 1 of the 13 promised tenant projectors (start with daily_arrivals since RP581 ingest is live) — establishes the pattern; remaining 12 follow same skeleton | packages/mcp-server/src/workers/tenant-projector/daily-arrivals.ts (new) + wrangler config + octokit dep | 1-2 days |
P1 additions
| # | Title | Location | Estimated effort |
|---|---|---|---|
| P1-W5-01 | Fix misleading comment at qc-inspection-profile-renderer.ts:13 (says writes to nathel-intranet vault but actually writes to DB) | packages/mcp-server/src/ingestion/renderers/qc-inspection-profile-renderer.ts:13 | 5 min |
| P1-W5-02 | Add FIN_VAULT_TENANT_MAP entry for nathel in wrangler.fin-central.jsonc (or create peer wrangler.tenant.jsonc) so the renderer worker would even know to address nathel content | packages/mcp-server/wrangler.*.jsonc | 30 min |
| P1-W5-03 | Document the auth bypass tenant routing footgun — body tenantId arg returns UNAUTHORIZED while X-Fin-Tenant header silently re-binds. Harmonize the contract | packages/mcp-server/src/middleware/auth.ts + docs/engineering/AUTH_CONTRACT.md | 1 hr |
| P1-W5-04 | Rename “PROJECTION_INDEX” in tenant vault to “VAULT_RENDERER_OUTPUTS” or similar to disambiguate from DB CQRS PROJECTION_CATALOG | nathel-intranet/PROJECTION_INDEX.md (rename) + fin-central-intranet mirror | 30 min |
| P1-W5-05 | Engineering Spec §11.5 currently covers alert routing, not taproot. Add a new §11.7 (or similar) covering the 3-rung architecture: renderer + sync transport + tenant overlay renderer. Without it the design lives only in _taproot_mirror/README.md | docs/engineering/ENGINEERING_SPEC.md (or wherever the canonical spec lives) | 2-3 hr |
Frozen-intentional additions
| # | Item | Why |
|---|---|---|
| F-W5-01 | Tenant authored content (decisions, runbooks, SOPs) | nathel-intranet README itself says “(empty at bootstrap 2026-05-15 — content lands during Phase 2+)“. Phase 2+ author work, not a runtime gap |
| F-W5-02 | Aggregate intelligence (across-tenant rollups) | fin-central-intranet’s aggregate_intelligence/ is frozen per single-tenant reality (feedback_federation_publisher_frozen.md) |
Updated recommended sequencing
Wave 5 work fits into the existing 4-sequence model from the original audit, but adds a new tier-2 priority because the tenant pipeline being completely cold means none of the agent-tool work (Sequence B) can deliver tenant-overlay value without the producer being built.
Sequence E — “Wake the tenant pipeline” (~3-4 days, new)
Goal: turn on tenant-side ingestion + at least one projector + the taproot sync. Establishes the pattern so the remaining 12 projectors are mechanical.
- P0-W5-01 (mount route) — 15 min
- P0-W5-02 (complete handler) — 2-3 hr
- P0-W5-03 (configure GH webhook) — 15 min
- P0-W5-04 (wire data.tenant overlay) — 2-3 hr (this alone gives meaningful tenant-overlay value immediately, even before the sync transport lands)
- P0-W5-05 (taproot sync, pick Wave 1 OR Wave 4) — 1-2 days
- P0-W5-06 (1st tenant projector: daily_arrivals) — 1-2 days
End state: Tenant vault accepts authored content + Central taproot mirrors into tenant + at least daily_arrivals projector deposits agent-generated MD back into tenant vault. The 3-rung stack is live for 1 deliverable; remaining 12 projectors can follow the same pattern.
Original 4 sequences still apply
A (pipeline), B (agent surface), C (self-improvement loop), D (vault back-fill) — unchanged. Sequence E should precede or run parallel to B, since the agent-surface work has no tenant content to surface without it.
Wave 5 sub-report index
| WS | Report |
|---|---|
| WS-16 | .audit-2026-05-17/wave5/ws-16-nathel-intranet.md |
| WS-17 | .audit-2026-05-17/wave5/ws-17-taproot-sync.md |
| WS-18 | .audit-2026-05-17/wave5/ws-18-tenant-webhook.md |
| WS-19 | .audit-2026-05-17/wave5/ws-19-tenant-projector.md |
| WS-20 | .audit-2026-05-17/wave5/ws-20-cross-taproot.md |
Final consolidated health (20 work streams)
| Status | Count | WSs |
|---|---|---|
| PASS | 1 | WS-02 |
| DEGRADED | 13 | WS-01, WS-03, WS-04, WS-06, WS-07, WS-09, WS-10, WS-12, WS-13, WS-14, WS-15, WS-16, WS-20 |
| FAIL | 6 | WS-05, WS-08, WS-11, WS-17, WS-18, WS-19 |
Final remediation backlog count:
- P0: 21 items (15 original + 6 Wave 5)
- P1: 17 items (12 original + 5 Wave 5)
- P2: 8 items (unchanged)
- Frozen-intentional: 5 (3 original + 2 Wave 5)
Total estimated focused work to fully operational: ~7-8 days across 5 parallel sequences (A pipeline, B agent surface, C self-improvement loop, D vault back-fill, E tenant pipeline NEW). Still all bounded fixes; no redesign needed.