Consolidated gaps register
| # | Gap | Owning subsystem | Severity | Honest status |
|---|---|---|---|---|
| 1 | No template→fork re-sync / live inheritance — fleet-wide config change does not reach already-forked tenants; contradicts "author once" thesis | provisioning-planes (also authoring-surfaces, surface-widgets, reports-analytics) | High | By design today (copy-on-create); biggest unaddressed lifecycle edge |
| 2 | Per-app read-path flip SHIPPED + decommissioned (#424/#431, flags retired). Residual: engine resolving under a consumer app-id is gated on one upstream change (resolver.py:67); BFF compilation stays by design (ADR-0001, Pattern A) | authz-ums | Low | Not platform debt — upstream + irreducible-by-design |
| 3 | tenant_scoped default divergence — sheet grammar requires it explicit, but engine repo default is False (repo.py:208,245) vs docs' true | ontology-core (also sheets-import re identity.features) | High | Confirmed divergence; API re-default not traced this pass |
| 4 | Live is_service / is_superuser enforcement bypass still present; control plane partly convention-derived (no stored root_org_id) | authz-ums (also provisioning-planes) | High | Permanent cold-start seam vs must-close — unresolved |
| 5 | Creator-baked-ACL footgun — a view authored by an over-privileged principal leaks to all readers; X-Tenant-Id never narrows a view | ontology-core | Med-High | Accepted contract today; no per-reader re-scope or lint |
| 6 | source_kind:view public surfaces bypass both Gate A and Gate B — trust concentrated on one operator-authored public_fields row | reports-analytics | High | No second guard (e.g. output-column allowlist at import) |
| 7 | Fail-open boot importer not in CI; manifest guards advisory-only — fatal sheets silently produce "nav blank / citizen 404" | sheets-import | High | Gates exist but rot; no check_sheet --applied in main CI |
| 8 | No fleet-wide view-collision guard — divergent duplicate view silently keeps whichever applied first | sheets-import | Med | Type- and route-collision guards exist; view does not |
| 9 | Shared Martha — no per-tenant isolation; dormant per-tenant secret matching + #441 Vault | workflows-martha | Med-High | Permanent-vs-revive decision pending; dead-code risk |
| 10 | Best-effort CloudEvents offload, no outbox/retry — swallowed emit_cloud_event silently drops a submitted create while ledger row persists | workflows-martha | High | No reconciliation path |
| 11 | admin_entity_config.actions JSONB dead but compile.py header still claims it live (doc-debt); two sibling-fetch paths diverge — compile._fetch_siblings caps at 500 → silent row loss >500 | action-engine | Med | Latent correctness bug + stale doc |
| 12 | UMS apply gate fails OPEN without UMS (dev) — _check_action_apply_grant | action-engine | Med | Acceptable in dev; must flip fail-closed before prod-on-every-target |
| 13 | /actions/submit is a placeholder — citizen submission actually flows through /public/submit; schema advertises an unimplemented affordance | action-engine | Low-Med | Remove or implement; affects whether citizen submit unifies onto execute_action |
| 14 | action_side_effect.event FK dead at runtime; string is load-bearing; event lookup unscoped despite tenant_scoped:true; SMS selectable but no workflow adapter; in-app tray is BFF-Postgres not ontology | notifications | Med | Slice 4f unfinished; SMS is a selectable no-op |
| 15 | Admin uses converged renderer for charts only — KPI cards + Metabase embeds bespoke; kpi/stat-grid has no wired public read path; Block.children grid reserved; no single enumerable widget catalog (3 overlapping lists) | surface-widgets | Med | "One renderer drives admin" is charts-only today |
| 16 | Fork copying __dashboard__ admin_entity_config + portal_page rows inferred from doctrine, not re-confirmed at file:line this pass | surface-widgets | Med | Needs explicit verification |
| 17 | m10 PRD diverged, unmarked as superseded by the shipped Block[]+WidgetKind+page_template model | surface-widgets | Low | Should be formally marked superseded in this freeze |
| 18 | act-as authz predicate exercised only in live tests, not CI units — the single cross-tenant control-plane gate | authoring-surfaces | Med-High | Move into CI unit coverage |
| 19 | Branding/campaigns are runtime-tenant editors (no act-as) despite living under /settings — operators may assume act-as uniformity | authoring-surfaces | Low-Med | Reclassify or document explicitly |
| 20 | Per-app citizen ACL flip mid-migration (#409/#428/#433) — write path not yet flipped to match read-path namespace; dual-namespace fallback not retired | reports-analytics (also authz-ums) | Med | Cutover gate undefined |
| 21 | Reports have no delivery ledger; Metabase provisioned out-of-band (hand-wired dashboard_id) | reports-analytics | Low-Med | Fork-time/import-time reconciliation unbuilt |
| 22 | Saga compensation never raises and isn't retried — dangling cross-service state (Vault/Martha/KC) has no standing reconciliation pass | provisioning-planes | Med | Ownership undefined |
| 23 | identity.features:{map,calendar} in sheet vs #386 "map/calendar are DERIVED not stored, no tenant_feature entity" | sheets-import | Med | Real divergence to reconcile |
| 24 | Stale docstrings actively mislead — control_plane vs forkable, system vs binding plane, authoring-architecture.md's "BFF-Postgres" claim | provisioning-planes (cross-cutting) | Low | Correct as part of this spec freeze |
| 25 | action_placement.target_config pins a template occurrence_type UUID — fork-fidelity gap for UUID-bearing config blobs | workflows-martha (also provisioning-planes) | Med | Fork must rewire target_config FKs; generalises |
| 26 | Portal/form_definition tenancy scoped out (#75/#79) — citizen-form definitions not formally covered by the plane model | provisioning-planes | Low-Med | Known second-class config class |