Design — Remediating Zero Bespoke Tables Violation (GAP-01)
Related Gap: GAP-01 — kazi and Materra tables not mapped to canonical primitives AGENTS.md Mandate: Mandate 1 — Zero bespoke tables: "NEVER create module-specific tables (e.g. materra_, kazi_). All domain entities MUST be mapped directly into the existing canonical base infrastructure: claim, truth_node/truth_edge, resource/resource_edge, integration_entity." Status: DESIGN — design document for remediation
1. Problem Statement
KaziSasa vertical (V8 in the commercial catalog) and potentially Materra have created module-specific tables (kazi_, materra_) that violate the zero-bespoke-tables mandate. These fork the data model, making federation, audit, and cross-vertical collaboration point-to-point wiring that cannot be governed or audited as one platform.
The apps/agent-bff/src/kazisasa-bff-routes.js is a "thin compat shim" with no data-plane logic, routing through ERPNext REST via Camel. The full kazi-app and kazi-composable-template-routes implementation was not verified in this read.
2. Remediation Strategy
2.1 Map All KaziSasa Entities to Canonical Primitives
Every KaziSasa endpoint must map to one of the canonical kinds:
| KaziSasa Endpoint | Canonical Kind | Mapping Pattern |
|---|---|---|
/v1/kazisasa/health | integration_entity + health check | Map to integration_entity with entity_type: "kazi_health" |
/v1/kazisasa/templates/* | claim + truth_node | Claim type cns://claim/type/kazi/template with traits |
/v1/kazisasa/scenarios/* | claim + truth_edge | Scenario as claim with edge to resource |
/v1/kazisasa/requisitions/* | claim | Tender/lifecycle claim with traits [assignable, time_bound, completable, blockable] |
/v1/kazisasa/requisitions/:id/solve | claim + integration_entity | Solver result dual-written to integration_entity |
/v1/kazisasa/requisitions/:id/insurance-bind | claim | Micro-insurance binder in canonical claim |
/v1/kazisasa/requisitions/:id/equipment-reserve | claim | Tool depot reservation in canonical claim |
/v1/kazisasa/intel/* | claim + truth_node | Prevailing wage benchmarks as claims |
/v1/kazisasa/vault/* | claim + integration_entity | Zero-knowledge vault claims |
/v1/kazisasa/vault/credentials/verify | claim + truth_edge | BBS+ selective disclosure as graph edge |
/v1/kazisasa/vault/leases/* | claim + resource | Ephemeral blind data leases |
2.2 Delete or Rename Any kazi_ / materra_ Tables
Audit the SurrealDB schema for any tables named:
kazi_*materra_*- Any table not in the canonical list:
claim,truth_node,truth_edge,resource,resource_edge,integration_entity,resource_graph_node,resource_graph_edge,universal_mirror,sync_audit,bridge_config,event_queue,interface_catalog,type_catalog,scope,delegation,scope_hierarchy,type_registry,execution_path_spec,execution_session,step_receipt,lifecycle_event,compliance_gate,compliance_stage,compliance_eval,admin_region,region_cover,targeted_event,boundary_rule,boundary_event,app_definition,permission_vocab,role,role_assignment,permission_grant,secret_registry,rotation_log,quorum,quorum_sig,integration_registry,durable_outbox
Any tables not in the canonical list must be:
- Dropped if they can be expressed as canonical kinds, OR
- Migrated to the canonical kind with a data migration script, OR
- Kept only if absolutely unmappable — but this requires stopping and asking the user per mandate 1
2.3 Update All KaziSasa Routes to Use Canonical Planes
Update apps/agent-bff/src/kazisasa-bff-routes.js (and any successor files) to:
- Claim creation: Use
fn::claim_processing::create_claimor equivalent canonical function, NOT create akazi_*table - Claim transitions: Use the guarded transition operations on the claims plane, NOT vertical-specific logic
- Outbox enqueue: Use the canonical outbox envelope, NOT a vertical-specific outbox
- Proof recording: Use
fn::lifecycle::start_poce_session,fn::lifecycle::record_poce_step,fn::lifecycle::finalize_poce_session— these are horizontal functions, not vertical - Integration entity dual-write: Use
fn::integration::upsert_entityto write tointegration_entityalongside any legacy writes (best-effort, never blocking)
2.4 Ensure Scenario Packs Are Declarative, Not Hardcoded
Kazi scenario packs (journey definitions, panel configurations, SLA windows, quorum bands) must be declared in the NATS KV buckets SOVEREIGN_SCENARIOS or SOVEREIGN_LOOKUPS, NOT hardcoded in JavaScript. The verify-kazi-headless-api.mjs script should validate that all scenario packs are loaded from KV at runtime.
Pattern:
- Pack definitions in
datasets/scenarios/kazi/*.yamlor similar - Seeded via
scripts/seed-nats-kv-declarative.mjsintoSOVEREIGN_SCENARIOS - Read at runtime from NATS KV, NOT from
fs.readFilepaths
2.5 Add Validation and Guardrails
- Schema validation: Add SurrealDB schema checks that reject any
CREATE TABLEwith name matchingkazi_ormaterra_patterns (except the canonical tables) - Route guard: Add a middleware check in the BFF that rejects any route handler attempting to write to a non-canonical table
- CI check: Add a script that scans the codebase for
kazi_ormaterra_table references and fails the build if found (unless mapped to canonical kind in documentation)
3. Migration Script Design
Create a migration script scripts/migrate-kazi-to-canonical.surql that:
- Reads all existing
kazi_*rows - For each row, determines the canonical mapping:
kazi_health→integration_entitywithentity_type = "kazi_health"and appropriatenormalized_datakazi_claim_→claimwithclaim_type = "cns://claim/type/kazi/..."and traits mapped fromkazi_traitskazi_truth_*→truth_node+truth_edgewith appropriate CNS names
- Creates canonical rows
- Marks legacy rows as migrated (adds
migrated_to_canonical: truefield, or moves to amigrations_logtable) - Outputs a report of what was migrated, what was unmappable (requires user stoppage)
Important: The migration must be best-effort. If any kazi_* row cannot be mapped to a canonical kind, the script must STOP and ask the user, not silently skip or invent data.
4. Interface Contract
All KaziSasa external interfaces must conform to the canonical contract:
Claim creation request:
{
"claim_type": "cns://claim/type/kazi/...",
"traits": ["assignable", "time_bound", "completable", "blockable"], // or relevant subset
"payload": { /* claim-specific data */ },
"link_to_graph": true // always true — must also create graph link
}
Claim transition request:
{
"transition_from": "draft", // or relevant state
"transition_to": "submitted",
"actor": "user_id_or_service_id",
"guards": "declarative_guard_check", // from scenarios plane
"proof": "poce_session_ref" // optional but recommended
}
Integration entity dual-write:
{
"service": "kaza-api",
"external_id": "crew_12345",
"entity_type": "crew_member",
"sovereign_id": "crew_12345", // or a sovereign-generated UUID
"sovereign_type": "claim",
"tenant_slug": "kazi",
"normalized_data": { /* canonical subset */ },
"raw_data": { /* original data */ },
"metadata": { "migrated_from": "kazi_crew_members", "migration_timestamp": "..." }
}
5. Validation Checklist
After remediation, verify:
- [ ] No
kazi_ormaterra_tables exist in SurrealDB (or have been migrated) - [ ] All KaziSasa routes map to canonical
claim,truth_node,truth_edge,resource, orintegration_entity - [ ] All scenario packs are loaded from NATS KV at runtime, not from disk
- [ ] The verify-kazi-headless-api.mjs script passes all 9 gates
- [ ] No route handler creates a non-canonical table or writes to a non-canonical field
- [ ] The codebase scan for
kazi_/materra_table references fails (as it should — all are mapped canonically) - [ ] Dual-write to
integration_entityis best-effert (LET $_ = ...) but never blocks the legacy write
6. Dependencies
This design depends on:
- AGENTS.md mandates 1, 2, 4 (zero bespoke, declarative-first, no runtime FS reads)
- Existing canonical functions:
fn::claim_processing::create_claim,fn::integration::upsert_entity,fn::lifecycle::* - NATS KV buckets
SOVEREIGN_SCENARIOSandSOVEREIGN_LOOKUPSproperly seeded - SurrealDB schema already deployed with canonical tables
- The verify-kazi-headless-api.mjs script and its imported helpers