Backend Implementation Backlog — Sovereign OS / Jamii Console
Date: 9 Nov 2026
Branch: program-backlog
Status: Draft for implementation
Scope: Backend APIs and modules only. Frontend/UI work is tracked separately.
How to read this backlog
Each item has:
- ID — unique reference (BL-001, BL-002, …)
- Priority — P0 (blocking), P1 (needed for MVP), P2 (nice to have)
- Status — TODO / IN PROGRESS / DONE / BLOCKED
- Files — exact file paths to create or modify
- Acceptance criteria — what "done" looks like; all must pass
node --testandrun-gtm-live-observe.mjs --require-live - Dependencies — must be completed before this item
Mandatory constraints (from AGENTS.md — Sovereign OS / Cosmic Circuit and D-15):
- No bespoke tables. Everything maps into
claim,truth_node/edge,resource/edge,integration_entity. - Declarative-first: lookups, thresholds, packs, maps live in NATS KV (
SOVEREIGN_LOOKUPS/SOVEREIGN_SCENARIOS). Runtime never reads disk. - Fail-closed: infrastructure errors → 502/503/422/throw. Never synthetic
"OPTIMAL"/"APPROVED". - Soft≠LIVE honesty:
live_gate_met/invent_refuse_liveare real probe results. - All modules must pass
node --testandrun-gtm-live-observe.mjs --require-livebefore merge.
Phase 1 — Core Claim & Property APIs (Blocking)
BL-001 — GET /v1/communities/:orgId/properties — List properties for an org
| Field | Value |
|---|---|
| ID | BL-001 |
| Priority | P0 |
| Status | DONE |
| Files | apps/agent-bff/src/property-playbook.js (listProperties()), apps/agent-bff/src/building-reflex-routes.js (route mount) |
| Depends on | None |
What to build:
listProperties(input)inproperty-playbook.js: queriesclaimtable whereclaim_type = "property"andsubject_id = orgId, returnsclaim_id,claim_type,cns_name,state,metadata,created_at, with?state,?limit,?offsetfilters- Route:
GET /v1/communities/:orgId/propertiesinbuilding-reflex-routes.js - Must return empty array
[]when no properties exist (honest empty state)
Acceptance criteria:
- Returns 200 with
{ data: [...], limit, offset } - Returns empty array
[]when no properties - 422 when
orgIdmissing - 503 when SurrealDB unavailable
- Test added to
__tests__/building-reflex-sourcing-mars.test.js(or new test file)
BL-002 — GET /v1/community/properties/:id — Get single property detail
| Field | Value |
|---|---|
| ID | BL-002 |
| Priority | P0 |
| Status | DONE |
| Files | apps/agent-bff/src/property-playbook.js (getProperty()), apps/agent-bff/src/building-reflex-routes.js (route mount) |
| Depends on | BL-001 |
What to build:
getProperty(input)inproperty-playbook.js:SELECT * FROM claim WHERE claim_id = $id AND claim_type = "property" LIMIT 1- Route:
GET /v1/community/properties/:idinbuilding-reflex-routes.js - Returns 404 when claim not found
Acceptance criteria:
- Returns 200 with
{ data: { claim_id, claim_type, cns_name, state, metadata, created_at } } - Returns 404 when claim not found
- 422 when
idmissing - 503 when SurrealDB unavailable
BL-003 — POST /v1/community/sourcing/offers/:id/accept — Accept a sourcing offer
| Field | Value |
|---|---|
| ID | BL-003 |
| Priority | P0 |
| Status | DONE |
| Files | apps/agent-bff/src/community-sourcing.js (acceptOffer()), apps/agent-bff/src/building-reflex-routes.js (route mount) |
| Depends on | ingestOffer() (already implemented in community-sourcing.js) |
What to build:
acceptOffer(input)incommunity-sourcing.js: validates the offer claim exists, creates a binding claim, transitions the requirement toBOUND- Route:
POST /v1/community/sourcing/offers/:offerId/acceptinbuilding-reflex-routes.js - Must emit canonical envelope via
ontology.normalizeSourcingRequirement()
Acceptance criteria:
- Returns 201 with
{ ok: true, created: true, claim_id: <bindingId>, cns_name: <bindingCNS>, organization_id, provider_id } - Returns 404 if offer not found
- Returns 422 if required fields missing
- Returns 503 when SurrealDB unavailable
- Emits
platform.criteria.v1.sourcing.requirementevent via outbox
BL-004 — GET /v1/community/:domain/:id — Generic claim detail
| Field | Value |
|---|---|
| ID | BL-004 |
| Priority | P1 |
| Status | DONE |
| Files | apps/agent-bff/src/jijibora-event-ontology.js (getClaim()), apps/agent-bff/src/building-reflex-routes.js (route mount) |
| Depends on | None |
What to build:
getClaim(input)injijibora-event-ontology.js:SELECT * FROM claim WHERE claim_id = $id LIMIT 1, returns canonical envelope shape- Route:
GET /v1/community/:domain/:idinbuilding-reflex-routes.js
Acceptance criteria:
- Returns 200 with
{ ok: true, claim_id, cns_name, state, metadata, created_at, envelope, subject } - Returns 404 when claim not found
- 422 when
idmissing - 503 when SurrealDB unavailable
BL-005 — POST /v1/community/:domain/:id/note — Operator note on a claim
| Field | Value |
|---|---|
| ID | BL-005 |
| Priority | P1 |
| Status | DONE |
| Files | apps/agent-bff/src/jijibora-event-ontology.js (appendNote()), apps/agent-bff/src/building-reflex-routes.js (route mount) |
| Depends on | BL-004 |
What to build:
appendNote(input)injijibora-event-ontology.js: appends a note toclaim.metadata.notesarray, emits canonical envelope- Route:
POST /v1/community/:domain/:id/noteinbuilding-reflex-routes.js
Acceptance criteria:
- Returns 200 with
{ ok: true, claim_id, note_id, created_at } - Returns 404 when claim not found
- Returns 422 when
notefield missing - Note is append-only (append to array, never overwrite)
Phase 2 — Billing & OpenMaint Facades
BL-006 — Kill Bill billing facade — GET /v1/billing/subscriptions, POST /v1/billing/invoices
| Field | Value |
|---|---|
| ID | BL-006 |
| Priority | P1 |
| Status | DONE |
| Files | apps/agent-bff/src/killbill-routes.js (new), apps/agent-bff/src/index.js (mount) |
| Depends on | billing-routes.js exists; commerce-killbill.js exists |
What to build:
- Create
apps/agent-bff/src/killbill-routes.js: thin proxy to Kill Bill bridgeGET /v1/billing/subscriptions→ proxies to Kill BillPOST /v1/billing/invoices→ proxies to Kill Bill
- Mount in
apps/agent-bff/src/index.js - Must fail closed when Kill Bill is unreachable (503)
Acceptance criteria:
- Returns 200 with Kill Bill response passthrough
- Returns 503 when Kill Bill unreachable
- 401/403 when unauthenticated
BL-007 — OpenMaint facade — GET /v1/properties/:id/work-orders, POST /v1/properties/:id/work-orders
| Field | Value |
|---|---|
| ID | BL-007 |
| Priority | P1 |
| Status | DONE |
| Files | apps/agent-bff/src/openmaint-routes.js (new), apps/agent-bff/src/index.js (mount) |
| Depends on | None |
What to build:
- Create
apps/agent-bff/src/openmaint-routes.js: thin proxy to OpenMaintGET /v1/properties/:id/work-orders→ proxies to OpenMaintPOST /v1/properties/:id/work-orders→ proxies to OpenMaint
- Mount in
apps/agent-bff/src/index.js - Must fail closed when OpenMaint is unreachable (503)
Acceptance criteria:
- Returns 200 with OpenMaint response passthrough
- Returns 503 when OpenMaint unreachable
- 401/403 when unauthenticated
Phase 3 — Route Mounting & Route Completeness
BL-008 — Mount building-reflex-routes.js in index.js
| Field | Value |
|---|---|
| ID | BL-008 |
| Priority | P0 |
| Status | DONE |
| Files | apps/agent-bff/src/index.js |
| Depends on | BL-001 through BL-007 |
What to build:
- Add
const brRoutes = require('./building-reflex-routes');and mount call inapps/agent-bff/src/index.js - Currently
building-reflex-routes.jsexists but is NOT mounted in index.js (verified: not in the require/mount section)
Acceptance criteria:
node --check apps/agent-bff/src/index.jspasses- All building-reflex routes respond (not 404) when BFF is running
run-gtm-live-observe.mjspasses (28/28)
BL-009 — Add missing tests for A1–A7 APIs
| Field | Value |
|---|---|
| ID | BL-009 |
| Priority | P1 |
| Status | DONE |
| Files | apps/agent-bff/src/__tests__/ (new test files or extend existing) |
| Depends on | BL-001 through BL-007 |
What to build:
- Test file
apps/agent-bff/src/__tests__/property-playbook.test.jsfor BL-001, BL-002 - Test file
apps/agent-bff/src/__tests__/sourcing-offer.test.jsfor BL-003 - Test file
apps/agent-bff/src/__tests__/generic-claim.test.jsfor BL-004, BL-005 - Test file
apps/agent-bff/src/__tests__/billing-facade.test.jsfor BL-006 - Test file
apps/agent-bff/src/__tests__/openmaint-facade.test.jsfor BL-007
Acceptance criteria:
- All new tests pass:
node --test apps/agent-bff/src/__tests__/*.test.js - Mock
querySurrealto simulate SurrealDB responses (fail-closed, no synthetic success)
Phase 4 — Backend Observability & Reliability
BL-010 — Add CNS interface catalog entries for new endpoints
| Field | Value |
|---|---|
| ID | BL-010 |
| Priority | P1 |
| Status | DONE |
| Files | database/seeds/cns/cns_interface_catalog_*.surql |
| Depends on | BL-001 through BL-007 |
What to build:
- Add CNS interface entries for property, sourcing offer accept, and generic claim endpoints in
cns_interface_catalog_building_reflex_sourcing_mars.surql - Each entry:
cns_path,version,major_version,interface_type,communication_pattern,input_schema,output_schema
Acceptance criteria:
- JSON parses:
node -e "JSON.parse(require('fs').readFileSync('...','utf8'))"for all .surql files - CNS seed imports without error (surreal import)
BL-011 — Add outbox anchors for all new write endpoints
| Field | Value |
|---|---|
| ID | BL-011 |
| Priority | P1 |
| Status | DONE |
| Files | apps/agent-bff/src/property-playbook.js, apps/agent-bff/src/community-sourcing.js |
| Depends on | BL-001 through BL-005 |
What to build:
- Every state-changing endpoint (complete, bind, accept, create) must write to
nats_outboxbefore returning - Use
ontology.buildOutboxEnqueue(envelope, subject)pattern - Fail closed: if outbox insert fails, return 503
Acceptance criteria:
- Every write endpoint returns
outboxobject in response outbox.record_key,outbox.subject,outbox.payloadpresentrun-gtm-live-observe.mjspasses
Phase 5 — Release & Observability
BL-012 — Restart agent-bff from current tree & verify all routes
| Field | Value |
|---|---|
| ID | BL-012 |
| Priority | P0 |
| Status | DONE |
| Files | None (ops) |
| Depends on | BL-001 through BL-011 |
What to build:
- Stop the stale agent-bff process (PID 20816, started 11/6)
- Start
apps/agent-bff/src/index.jsfrom current tree - Verify all new routes respond (not 404)
Acceptance criteria:
GET http://127.0.0.1:3030/healthreturns 200GET http://127.0.0.1:3030/v1/community/healthreturns 200- New routes return 200/404/422 (not 404 for missing route)
BL-013 — Run full verification suite
| Field | Value |
|---|---|
| ID | BL-013 |
| Priority | P0 |
| Status | DONE |
| Files | None (ops) |
| Depends on | BL-001 through BL-012 |
What to build:
- Run
node --test apps/agent-bff/src/__tests__/*.test.js— all must pass - Run
node scripts/run-gtm-live-observe.mjs --require-live—live_gate_met:true - Run
node scripts/check-criteria-subject-consistency.mjs— PASS - Run
node scripts/check-mars-freeze.mjs— PASS
Acceptance criteria:
- 20+ tests pass, 0 fail
live_gate_met:true- Subject consistency: 0 failures
- Mars freeze: PASS
Phase Order
Phase 1: BL-001 → BL-005 (Core claim & property APIs)
Phase 2: BL-006 → BL-007 (Billing & OpenMaint facades)
Phase 3: BL-008 → BL-009 (Route mounting & tests)
Phase 4: BL-010 → BL-011 (CNS catalog + outbox anchors)
Phase 5: BL-012 → BL-013 (Restart & verify)Estimated effort
| Phase | Items | Estimated effort |
|---|---|---|
| Phase 1 | BL-001 to BL-005 | 2–3 days |
| Phase 2 | BL-006 to BL-007 | 1–2 days |
| Phase 3 | BL-008 to BL-009 | 1 day |
| Phase 4 | BL-010 to BL-011 | 1 day |
| Phase 5 | BL-012 to BL-013 | 1 day |
| Total | 13 items | 6–9 days |
Done criteria for the entire backlog
node --test apps/agent-bff/src/__tests__/*.test.js→ all pass (20+ tests)node scripts/run-gtm-live-observe.mjs --require-live→live_gate_met:truenode scripts/check-criteria-subject-consistency.mjs→ PASSnode scripts/check-mars-freeze.mjs→ PASS- All 13 items marked DONE in this document