D-09 — Optimization Capability (developer)
Scope: The developer contract for promoting sourcing, solving, and optimizing to first-class platform capabilities: the solver capability catalog, the sourcing bus contract, the optimization loops, per-playbook participation, sentinel/event sources, and the failure catalog. Status: as of 2026-11-09 Reader: developers building or integrating playbooks on the sourcing spine. Blueprint sources: docs/event-sourcing/SOURCING_SOLVER_OPTIMIZER_INTEGRATION.md; docs/schemas/decision-contract-v1.md; docs/plans/DECISION_SOURCING_SUBSYSTEM_BACKLOG.md; Data Fabric — Message Bus and Engine Interfaces (Developer Concept Doc); Kutuma Notify (Notification Platform — Novu Lineage, White-Labeled + Keycloak Gate + Solved Pitfalls); docs/event-sourcing/EVENT_SOURCING_IMPLEMENTATION_PLAN.md; docs/event-sourcing/SOURCING_ENGINE_LIVE_RUNBOOK.md; D-03 — Jijibora Playbook Designs (developer); D-04 — Environmental Playbooks (developer); docs/commercialization/33_EVENT_SPINE_PLAYBOOK_BACKLOG.md
Conventions:solver-gateway :3070is the single solving surface;decision-request-v1/decision-result-v1is the single envelope; Soft (practice) vs LIVE (production) governs every claim about readiness. No inbound logic lives in procedural store-of-record functions — SurrealDB is record-only. Runtime lookups come from NATS KV, never filesystem reads (source: docs/event-sourcing/SOURCING_SOLVER_OPTIMIZER_INTEGRATION.md; source: docs/commercialization/33_EVENT_SPINE_PLAYBOOK_BACKLOG.md).
1. Capability catalog
Every capability follows the same shape: consumer → candidate/constraints input → solver surface → verdict contract → evidence chain. The solver runs only after a real match has produced a shortlist (source: docs/event-sourcing/SOURCING_SOLVER_OPTIMIZER_INTEGRATION.md).
The gateway contract is decision-request-v1 → POST /v1/decisions on solver-gateway :3070, with catalogue GET /v1/capabilities; NATS request-reply is svc.solver.v1.<capability>.<version>.solve; HTTP failures fail closed with 502/503 (source: docs/schemas/decision-contract-v1.md; source: docs/event-sourcing/SOURCING_SOLVER_OPTIMIZER_INTEGRATION.md). The headless requisition path /v1/kazisasa/requisitions/:id/solve calls the real gateway with capability procurement.basket_split@1 and branch_and_bound 1.4.0 (source: Kutuma Notify (Notification Platform — Novu Lineage, White-Labeled + Keycloak Gate + Solved Pitfalls)). The solver bridge invokes the matching engine with capability procurement.basket_split@1 and branch_and_bound 1.4.0 and emits an outbox anchor for every solver invocation (source: Data Fabric — Message Bus and Engine Interfaces (Developer Concept Doc)).
1.1 procurement.basket_split@1 — basket split across shops/sources
- Consumer: procurement / headless requisition solve path; multi-line or multi-shop split (UC-4 style) (source: docs/event-sourcing/SOURCING_SOLVER_OPTIMIZER_INTEGRATION.md; source: docs/schemas/decision-contract-v1.md).
- Candidate/constraints input: lines with quantities, and offers carrying price, stock, and quantity per line; stop constraints and economics per line (source: docs/schemas/decision-contract-v1.md).
- Solver surface: solver-gateway
:3070, engine chainbranch_and_bound→greedy.branch_and_boundreturnsOPTIMALwith a bound when the search completes; at deadline it returns the greedy-seeded incumbent asFEASIBLEwith bindingdeadline; greedy alone isFEASIBLEwith no bound (source: docs/schemas/decision-contract-v1.md; source: Kutuma Notify (Notification Platform — Novu Lineage, White-Labeled + Keycloak Gate + Solved Pitfalls)). The same capability identity withbranch_and_bound 1.4.0is the FINMON/KAZI headless solver reference in deployment docs (source: Kutuma Notify (Notification Platform — Novu Lineage, White-Labeled + Keycloak Gate + Solved Pitfalls)). - Verdict contract:
decision-result-v1statusOPTIMALproven best (bound = objective),FEASIBLEvalid not proven best,INFEASIBLEproven impossible (certified),TIMEOUTno answer in deadline,ERRORengine failure;200is returned for any solver outcome includingINFEASIBLE/TIMEOUT(source: docs/schemas/decision-contract-v1.md). - Evidence chain (decision record):
decision_id,objective/bound/gap, engine actually used plus upstream engines,fallback_used/fallback_reason(never silent), binding constraints naming tight/failed rules, independent checker verdict (passed/rejected/not_applicablewith checks ≥ 1 for anyOPTIMAL/FEASIBLE), SHA-256config_hash/input_hash/output_hash, andprovenancewith every attempt in order (source: docs/schemas/decision-contract-v1.md).
1.2 procurement.multi_source_fill@1 — quantity allocation across shortlisted suppliers
- Consumer: sourcing pipeline stage after qualify/affinity shortlist; the mango-example fill (need 100 kg across farms offering 30/20/60 kg) (source: docs/event-sourcing/SOURCING_SOLVER_OPTIMIZER_INTEGRATION.md).
- Candidate/constraints input:
requirement_id,need_qty+ unit, shortlisted candidates withqty_availableand score ≥ emit floor (min_score 0.8), objectivemin_suppliers_then_score(source: docs/event-sourcing/SOURCING_SOLVER_OPTIMIZER_INTEGRATION.md; source: docs/event-sourcing/rules/sourcing-multi-source-fill-rule.example.json). - Solver surface: solver-gateway
:3070viadecision-request-v1, exact-fill → greedy chain (source: docs/event-sourcing/SOURCING_SOLVER_OPTIMIZER_INTEGRATION.md). - Verdict contract:
sourcing.fill_plan(feasible, with allocations +unmet_qty+decision_id+ provenance) orsourcing.fill_infeasible(explicit infeasible, never a silent award) (source: docs/event-sourcing/rules/sourcing-multi-source-fill-rule.example.json). - Evidence chain: fill plan carries causation to the upstream
match.firedevent, per-line allocations, and the decision record from §1.1-style provenance; reserve/award consumes the plan downstream (source: docs/event-sourcing/SOURCING_SOLVER_OPTIMIZER_INTEGRATION.md).
1.3 procurement.consolidated_order@1 — basket + route composite
- Consumer: consolidated purchasing (basket + waypoints) (source: docs/schemas/decision-contract-v1.md).
- Candidate/constraints input: basket lines + offers as in §1.1, plus vehicle capacity and pickup/drop-off structure (source: docs/schemas/decision-contract-v1.md).
- Solver surface: solver-gateway
:3070, composite enginecope_composite(basket split + VROOM route; greedy-nearest-neighbour on haversine if VROOM fails, declared) — outcomeFEASIBLE(source: docs/schemas/decision-contract-v1.md). - Verdict contract: basket rules on the split, one pickup per supplier used, one drop-off last at the buyer, ETAs non-decreasing, weight/capacity and economics consistency;
matrix.sourceprovenance passed through (source: docs/schemas/decision-contract-v1.md). - Evidence chain: decision record with route provenance and checker verdict per §1.1 (source: docs/schemas/decision-contract-v1.md).
1.4 logistics.route@1 — pickup/delivery routing after a fill plan
- Consumer: post-fill routing stage; collector-truck pickup sequence / CVRP (source: docs/event-sourcing/SOURCING_SOLVER_OPTIMIZER_INTEGRATION.md; source: docs/schemas/decision-contract-v1.md).
- Candidate/constraints input: jobs at supplier coords (or stop coordinates), vehicle capacity, time windows, skills,
max_tasks(source: docs/schemas/decision-contract-v1.md). - Solver surface: solver-gateway
:3070, chainvroom(OSRM matrices) →greedy(nearest neighbour, haversine, capacity-aware; refuses shipments, time windows, breaks, skills, caller matrices); heuristic,FEASIBLE(source: docs/schemas/decision-contract-v1.md). - Verdict contract: vehicles real and used once; every job routed once or unassigned; load within capacity at every step; service starts inside windows; totals consistent; objective = summary cost; legacy
amountfield is rejected400because the routing engine would silently ignore it (source: docs/schemas/decision-contract-v1.md). - Evidence chain: route plan envelope with VROOM routes + matrix provenance, decision record, and fill-plan causation (source: docs/event-sourcing/SOURCING_SOLVER_OPTIMIZER_INTEGRATION.md).
1.5 scheduling.curfew_pour@1 — time-windowed scheduling
- Consumer: time-windowed fulfillment (curfew pours) (source: docs/schemas/decision-contract-v1.md; source: docs/event-sourcing/SOURCING_SOLVER_OPTIMIZER_INTEGRATION.md).
- Candidate/constraints input: pours with plant, volume/rate, transit, slump-life, silo, and shared-pump constraints; earliest start, finish ≤ min(latest finish, 420) (source: docs/schemas/decision-contract-v1.md).
- Solver surface: solver-gateway
:3070, CP-SAT (cpsat_python) and MiniZinc (cp-sat backend) both report a proven bound (source: docs/schemas/decision-contract-v1.md). - Verdict contract: batch minutes exact arithmetic; objective = Σ priority × finish + 2 × slump age;
OPTIMALmeans bound = objective;INFEASIBLEcertified when a pour cannot be reached within slump life (source: docs/schemas/decision-contract-v1.md). - Evidence chain: decision record with proven bound, checker verdict, and binding constraints per §1.1 (source: docs/schemas/decision-contract-v1.md).
Catalogue parity note: branch_and_bound 1.4.0 + procurement.basket_split@1 is cited in deployment docs as asserted in the KAZI headless path only; the live :3070 solve and decision-request contract were not executed in that read (source: Kutuma Notify (Notification Platform — Novu Lineage, White-Labeled + Keycloak Gate + Solved Pitfalls)). Treat any claim of a live basket-split solve as UNKNOWN until a live gateway probe succeeds (source: Kutuma Notify (Notification Platform — Novu Lineage, White-Labeled + Keycloak Gate + Solved Pitfalls)).
2. The sourcing bus contract
Subjects are fixed-arity canonical subjects; no second bus or envelope may be invented (source: docs/commercialization/33_EVENT_SPINE_PLAYBOOK_BACKLOG.md). Each stage mints a new event_id and sets causation.causal_event_id to the upstream event (source: docs/event-sourcing/SOURCING_SOLVER_OPTIMIZER_INTEGRATION.md).
| Subject family | Producer → Consumer | Payload essentials / semantics |
|---|---|---|
platform.criteria.v1.sourcing.requirement (requirement record) | Community/buyer desk → sourcing continuous-match / qualify stage (source: docs/event-sourcing/rules/jijibora-community-sourcing-rule.example.json) | entity_type = sourcing_requirement; keyed meta.tenant + payload.match_key; pipeline pipe:sourcing:qualify-to-shortlist, stage requirement_opened (source: docs/event-sourcing/rules/jijibora-community-sourcing-rule.example.json) |
platform.criteria.v1.candidate.state (offer delta) | Offer publisher / normalize → continuous-match join (source: docs/event-sourcing/rules/jijibora-community-sourcing-rule.example.json; source: docs/event-sourcing/rules/sourcing-continuous-match-rule.example.json) | entity_type = candidate; keyed meta.tenant + payload.match_key; facets only, never raw metadata (source: docs/event-sourcing/rules/jijibora-community-sourcing-rule.example.json) |
platform.criteria.v1.match.fired (match-filed event) | Continuous-match stage → affinity/credit → shortlist; and → multi-source fill stage filtered meta.pipeline_stage = continuous_match (source: docs/event-sourcing/rules/sourcing-multi-source-fill-rule.example.json) | Event types sourcing.match.fired / building.reflex.fired / emergency.dispatch.fired; inherit_causation: true; continuous-match emit floor min_score 0.8 (source: docs/event-sourcing/rules/sourcing-continuous-match-rule.example.json; source: docs/event-sourcing/rules/jijibora-community-sourcing-rule.example.json) |
Shortlist ready (platform.criteria.v1.match.shortlisted or affinity-apply result) | Affinity/shortlist stage → solver stage (source: docs/event-sourcing/SOURCING_SOLVER_OPTIMIZER_INTEGRATION.md) | requirement_id, candidates[{id, qty_available, score, geo}] (source: docs/event-sourcing/SOURCING_SOLVER_OPTIMIZER_INTEGRATION.md) |
Solver request (solver.sourcing.fill.request / NATS req-reply svc.solver.v1.procurement.multi_source_fill.1.solve) | Kestra/Camel solver stage → solver-gateway (source: docs/event-sourcing/SOURCING_SOLVER_OPTIMIZER_INTEGRATION.md) | decision-request-v1 input (capability, version, tenant, deadline, seed, input, context) (source: docs/schemas/decision-contract-v1.md) |
platform.criteria.v1.sourcing.fill_plan (fill plan terminal) | Solver stage → reserve/award (source: docs/event-sourcing/rules/sourcing-multi-source-fill-rule.example.json) | allocations[{supplier_id, qty}], unmet_qty, decision_id, provenance (source: docs/event-sourcing/SOURCING_SOLVER_OPTIMIZER_INTEGRATION.md) |
platform.criteria.v1.sourcing.fill_infeasible (infeasible terminal) | Solver stage → buyer desk (explicit no-award) (source: docs/event-sourcing/rules/sourcing-multi-source-fill-rule.example.json) | Explicit infeasible verdict; never a silent award; infeasible shortlist → fill_infeasible (source: docs/event-sourcing/rules/sourcing-multi-source-fill-rule.example.json) |
platform.criteria.v1.sourcing.route_plan (route terminal) | Optional routing stage → Fleetbase/dispatch (source: docs/event-sourcing/SOURCING_SOLVER_OPTIMIZER_INTEGRATION.md) | VROOM routes + matrix provenance; jobs at farm coords + vehicle capacity (source: docs/event-sourcing/SOURCING_SOLVER_OPTIMIZER_INTEGRATION.md) |
Reserve / award (platform.sourcing.award.v1 / process transition) | Reserve CAS → award/dispatch (source: docs/event-sourcing/SOURCING_SOLVER_OPTIMIZER_INTEGRATION.md) | Per-allocation CAS lines; award causation → fill_plan event id (source: docs/event-sourcing/SOURCING_SOLVER_OPTIMIZER_INTEGRATION.md) |
Offer deltas vs requirement records keep the two-input join semantics: continuous match is a two-input (requirement + candidate) join, kind: join, min_score: 0.8, non-terminal (source: D-03 — Jijibora Playbook Designs (developer)).
3. Optimization loops
Where they run
- Kestra + Camel are the timeline drivers. The solver stage is a declarative pipeline stage after qualify/affinity, orchestrated as an AccumulationPlan/Kestra step invoking the gateway capability from the KV pack — not hardcoded in the BFF (source: docs/event-sourcing/SOURCING_SOLVER_OPTIMIZER_INTEGRATION.md).
- Scheduler events wake re-optimization; cron is not the primary wake. Schedule ticks are
schedule.v1.tick.bucket.<yyyyMMddHHmm>produced viaPOST /v1/sourcing/schedule-tick/produce(source: docs/event-sourcing/SOURCING_ENGINE_LIVE_RUNBOOK.md; source: docs/event-sourcing/EVENT_SOURCING_IMPLEMENTATION_PLAN.md). Standing-order watchers use the same discipline: observe state → evaluate breach per affected order → emit breach assessment → fire command only when breached and not in cooldown; recovery sweep exists only as recovery (source: D-04 — Environmental Playbooks (developer)). - Continuous matching runs on the stream join (Flink Job A) when fan-out warrants; Camel/Kestra remain until measured fan-out exceeds the Camel budget (source: docs/event-sourcing/SOURCING_SOLVER_OPTIMIZER_INTEGRATION.md).
What a re-optimization is
- Replan on telemetry: an arrival/completion/location change, a missed stop, or a new condition invalidates the current plan. A new solve is issued against the freshest feasible snapshot and a new plan displaces the old one with causation preserved — never silent deletion of the prior stop/plan (source: docs/event-sourcing/SOURCING_SOLVER_OPTIMIZER_INTEGRATION.md; source: D-04 — Environmental Playbooks (developer)). Municipal sequencing expects this shape: refuse rounds re-sequence around morning trade and grouped road reports; missed stops are rescheduled as new fire commands (source: D-04 — Environmental Playbooks (developer)).
- Re-score on arrival/conditions changes: scores are recomputed against the same emit floor (
0.8Soft) so a candidate that drops below floor leaves the feasible set and forces a re-solve rather than carrying a stale allocation (source: docs/event-sourcing/rules/sourcing-continuous-match-rule.example.json; source: docs/event-sourcing/SOURCING_SOLVER_OPTIMIZER_INTEGRATION.md). - Re-optimization cadence values (exact timers, backoff, jitter) are UNKNOWN in the read set — they must be declared per pack via KV lookup, not hardcoded (source: docs/commercialization/33_EVENT_SPINE_PLAYBOOK_BACKLOG.md).
Honesty constraints
- Optimizer = evidence producer. The loop emits proposals (fill plan / route plan / replan) with decision records, provenance, and checker verdicts. It never emits an approval, award, or settlement (source: docs/event-sourcing/SOURCING_SOLVER_OPTIMIZER_INTEGRATION.md; source: D-03 — Jijibora Playbook Designs (developer)).
- Soothsayer discipline = never closes evidence. The optimizer proposes; a named human or governed terminal handler disposes. A recommendation never becomes a receipt until evidence (photo/GPS/inspector or governed proof) closes the loop (source: docs/jijibora-government-trial/04_UNIFIED_ONTOLOGY_AND_REACTIVE_GOVERNANCE.md; source: D-04 — Environmental Playbooks (developer)).
- Stale snapshots refuse. Entitlement/presence/supply-delta gates deny when the snapshot is stale; the solver is not invoked on stale facts (source: docs/event-sourcing/SOURCING_SOLVER_OPTIMIZER_INTEGRATION.md).
4. Per-playbook participation
Participation vocabulary: attempt = requirement/offer attempt handling; match = continuous-match join participation; plan = solver-stage plan (fill/route/schedule); loop = optimizer replanning loop active; nothing = consciously no sourcing/solve/optimize role.
| Playbook | Sourcing (attempt / match) | Solve (plan) | Optimize (loop) | Notes |
|---|---|---|---|---|
| Building reflex | nothing — sense only, no sourcing (source: docs/event-sourcing/rules/jijibora-building-reflex-rule.example.json) | nothing (source: docs/event-sourcing/rules/jijibora-building-reflex-rule.example.json) | nothing — terminal actuation owns commands (source: docs/event-sourcing/rules/jijibora-building-reflex-rule.example.json) | Terminal actuate only; non-terminal stages emit criteria only (source: docs/event-sourcing/rules/jijibora-building-reflex-rule.example.json) |
| Community sourcing (qualify-to-shortlist + continuous match) | attempt + match — sourcing.requirement + candidate.state join, min_score 0.8 (source: docs/event-sourcing/rules/jijibora-community-sourcing-rule.example.json) | solver-gateway before shortlist (source: docs/event-sourcing/rules/jijibora-community-sourcing-rule.example.json) | nothing yet — shortlist is terminal; fill/loop belongs to procurement tier downstream (source: docs/event-sourcing/SOURCING_SOLVER_OPTIMIZER_INTEGRATION.md) | TGC qualify + solver before shortlist; sealed content never in match (source: docs/event-sourcing/rules/jijibora-community-sourcing-rule.example.json) |
| MARS/ICE dispatch | nothing — governance signal, not sourcing (source: docs/event-sourcing/rules/jijibora-mars-ice-dispatch-rule.example.json) | nothing — criteria engine only (source: docs/event-sourcing/rules/jijibora-mars-ice-dispatch-rule.example.json) | nothing — terminal dispatch + PoCE owns commands (source: docs/event-sourcing/rules/jijibora-mars-ice-dispatch-rule.example.json) | Child cannot widen parent scope (source: docs/event-sourcing/rules/jijibora-mars-ice-dispatch-rule.example.json) |
| Civic maintenance pilot | attempt (fair sourcing of technicians) (source: docs/jijibora-government-trial/04_UNIFIED_ONTOLOGY_AND_REACTIVE_GOVERNANCE.md) | plan — scheduling/routing stage (source: docs/jijibora-government-trial/04_UNIFIED_ONTOLOGY_AND_REACTIVE_GOVERNANCE.md) | loop — rescheduling on report/state change via breach→fire discipline (source: D-03 — Jijibora Playbook Designs (developer)) | Headless subject names UNKNOWN in read set (source: D-03 — Jijibora Playbook Designs (developer)) |
| Procurement tier (multi-source fill) | match (consumes match.fired continuous_match) (source: docs/event-sourcing/rules/sourcing-multi-source-fill-rule.example.json) | plan — procurement.multi_source_fill@1 (+ optional logistics.route@1) (source: docs/event-sourcing/rules/sourcing-multi-source-fill-rule.example.json) | loop where pipe:sourcing:multi-fill-route-dispatch is declared (fill→route→dispatch) (source: docs/event-sourcing/SOURCING_SOLVER_OPTIMIZER_INTEGRATION.md) | min_score 0.8, min_suppliers_then_score; solver down → 503 (source: docs/event-sourcing/rules/sourcing-multi-source-fill-rule.example.json) |
| Garbage / collection-route management | attempt via maintenance channel (household overflow report) (source: D-04 — Environmental Playbooks (developer)) | plan — sequencing only after a real shortlist exists (source: D-04 — Environmental Playbooks (developer)) | loop — route replanning + missed-stop rescheduling (source: D-04 — Environmental Playbooks (developer)) | Waste/route/stop headless subjects UNKNOWN in read set and must not be invented (source: D-04 — Environmental Playbooks (developer)) |
| Weather preparation (rain/flood ladder) | attempt only where prep sourcing is required (pre-positioning) (source: D-04 — Environmental Playbooks (developer)) | plan where truck diversion / drain-clearing sequencing is required (source: D-04 — Environmental Playbooks (developer)) | loop via standing-order breach discipline (source: D-04 — Environmental Playbooks (developer)) | Preparation = reversible; dispatch belongs to SOS family (source: D-04 — Environmental Playbooks (developer)) |
| Geofence / SCE proximity | nothing — subjects/predicates UNKNOWN (source: D-04 — Environmental Playbooks (developer)) | nothing — UNKNOWN (source: D-04 — Environmental Playbooks (developer)) | nothing — UNKNOWN (source: D-04 — Environmental Playbooks (developer)) | Treat as design-only until a rule example appears (source: D-04 — Environmental Playbooks (developer)) |
| Civic claim adjudication (benefit/compensation) | nothing — quorum/SoD desk, not sourcing (source: D-03 — Jijibora Playbook Designs (developer)) | nothing — guarded transitions, not allocation (source: D-03 — Jijibora Playbook Designs (developer)) | nothing (source: D-03 — Jijibora Playbook Designs (developer)) | Headless subjects UNKNOWN in read set (source: D-03 — Jijibora Playbook Designs (developer)) |
| Audit-file assembly | nothing — assembles receipts, does not source (source: D-03 — Jijibora Playbook Designs (developer)) | nothing (source: D-03 — Jijibora Playbook Designs (developer)) | nothing (source: D-03 — Jijibora Playbook Designs (developer)) | Refuses export without verified receipt (source: D-03 — Jijibora Playbook Designs (developer)) |
| Citizen notifications | nothing — renders templates, does not source (source: D-03 — Jijibora Playbook Designs (developer)) | nothing (source: D-03 — Jijibora Playbook Designs (developer)) | nothing (source: D-03 — Jijibora Playbook Designs (developer)) | Tenant-scoped + APF + break-glass only (source: D-03 — Jijibora Playbook Designs (developer)) |
| AI hooks (advisory-only) | attempt (propose assignment from facets) — proposal only (source: docs/jijibora-gtm/03_ENTRY_POINT_AND_STARTER_PACK.md) | nothing — never auto-allocates (source: docs/commercialization/33_EVENT_SPINE_PLAYBOOK_BACKLOG.md) | nothing — never auto-approves (source: docs/commercialization/33_EVENT_SPINE_PLAYBOOK_BACKLOG.md) | Core path works with hooks empty; no second runner (source: docs/commercialization/33_EVENT_SPINE_PLAYBOOK_BACKLOG.md) |
| Industrial / local-content gate | attempt (supplier as candidate) (source: Playbook Registry — What You Can Run, Who Runs It, What You Get Back) | nothing — verdict gate before award, not allocation (source: Playbook Registry — What You Can Run, Who Runs It, What You Get Back) | nothing (source: Playbook Registry — What You Can Run, Who Runs It, What You Get Back) | Missing verdict blocks award (source: Playbook Registry — What You Can Run, Who Runs It, What You Get Back) |
| Facet-notify (lifelong facets) | match (facet join, scrubbed facets only) (source: docs/event-sourcing/rules/lifelong-entity-state-rule.example.json) | nothing (source: docs/event-sourcing/rules/lifelong-entity-state-rule.example.json) | nothing — non-terminal notify (source: docs/event-sourcing/rules/lifelong-entity-state-rule.example.json) | Facets only, never raw metadata (source: docs/event-sourcing/rules/lifelong-entity-state-rule.example.json) |
5. Sentinel / event sources
Producer → subject → consumers, per family:
| Family | Producer → Subject | Consumers |
|---|---|---|
| Sourcing requirement delta | Buyer/community desk + intake channels → platform.criteria.v1.sourcing.requirement (source: docs/event-sourcing/rules/jijibora-community-sourcing-rule.example.json) | Continuous-match join; qualify stage (pipe:sourcing:qualify-to-shortlist) (source: docs/event-sourcing/rules/jijibora-community-sourcing-rule.example.json) |
| Offers / open intake | Offer publisher → platform.criteria.v1.candidate.state (source: docs/event-sourcing/rules/jijibora-community-sourcing-rule.example.json) | Continuous-match join; entitlement/presence/supply-delta gates before solver (source: docs/event-sourcing/SOURCING_SOLVER_OPTIMIZER_INTEGRATION.md) |
| Match fired | Continuous-match stage → platform.criteria.v1.match.fired (sourcing.match.fired) (source: docs/event-sourcing/rules/jijibora-community-sourcing-rule.example.json) | Affinity/credit → shortlist; multi-source fill stage filtered meta.pipeline_stage = continuous_match (source: docs/event-sourcing/rules/sourcing-multi-source-fill-rule.example.json) |
| Fill/route terminals | Solver stage → platform.criteria.v1.sourcing.fill_plan / .fill_infeasible / .route_plan (source: docs/event-sourcing/SOURCING_SOLVER_OPTIMIZER_INTEGRATION.md) | Reserve CAS → award/dispatch; buyer desk on infeasible (source: docs/event-sourcing/SOURCING_SOLVER_OPTIMIZER_INTEGRATION.md) |
| Telemetry signals | Crew/vehicle/citizen channels + sensors/M2M (gauge, meter, rain station, phone report) → criteria entity-state family (e.g. platform.criteria.v1.building.entity_state, platform.criteria.v1.governance.signal) (source: docs/event-sourcing/rules/jijibora-building-reflex-rule.example.json; source: docs/event-sourcing/rules/jijibora-mars-ice-dispatch-rule.example.json) | Reflex/sense stages; standing-order breach evaluators; optimizer replans on freshness (source: D-04 — Environmental Playbooks (developer)) |
| Replan ticks | schedule-tick producer → schedule.v1.tick.bucket.<yyyyMMddHHmm> (source: docs/event-sourcing/SOURCING_ENGINE_LIVE_RUNBOOK.md) | Pipeline scheduler consumers; Kestra/Camel timeline drivers (source: docs/event-sourcing/SOURCING_SOLVER_OPTIMIZER_INTEGRATION.md) |
| Solver request/reply | Kestra/Camel stage → svc.solver.v1.<capability>.<version>.solve (source: docs/event-sourcing/SOURCING_SOLVER_OPTIMIZER_INTEGRATION.md) | Solver-gateway :3070 (source: docs/schemas/decision-contract-v1.md) |
Telemetry ingestion subjects for garbage/vehicle state and exact tick→replan wiring beyond the tick bucket are UNKNOWN in the read set and must not be invented (source: D-04 — Environmental Playbooks (developer)).
6. Failure catalog (overrun guards)
| # | Failure | Guard (fail closed, no invention) |
|---|---|---|
| F1 | Solver-gateway down / unreachable | 503 invent_refuse — never return synthetic OPTIMAL, fill plans, or routes (source: docs/event-sourcing/SOURCING_SOLVER_OPTIMIZER_INTEGRATION.md) |
| F2 | VROOM / OSRM down | No invented routes; refuse or declare fallback (greedy haversine) with provenance — never silent vroom_reachable (source: docs/event-sourcing/SOURCING_SOLVER_OPTIMIZER_INTEGRATION.md; source: docs/schemas/decision-contract-v1.md) |
| F3 | NATS / Kestra down | 502/503; no fill invented; LIVE flag unmet → refuse (source: docs/event-sourcing/SOURCING_SOLVER_OPTIMIZER_INTEGRATION.md) |
| F4 | Infeasible shortlist | Emit sourcing.fill_infeasible (certified) — never over-allocate; quantity sum must equal min(need, Σ available) (source: docs/event-sourcing/SOURCING_SOLVER_OPTIMIZER_INTEGRATION.md) |
| F5 | Solver TIMEOUT within deadline | 200 with status TIMEOUT (or greedy-seeded FEASIBLE incumbent with binding deadline for basket split) — never upgraded to OPTIMAL (source: docs/schemas/decision-contract-v1.md) |
| F6 | Checker rejects an answer | status: ERROR, error.code: checker_rejected; chain moves to next engine; counted in GET /health checker_rejections; nothing leaves unchecked — a checker crash also counts as rejection (source: docs/schemas/decision-contract-v1.md) |
| F7 | No-invention caps | Hard caps before LIVE: ≤64 shortlisted suppliers per requirement, ≤16 covering-subset exact fill — beyond budget, refuse rather than invent a Flink LIVE or OPTIMAL fill (source: docs/event-sourcing/SOURCING_SOLVER_OPTIMIZER_INTEGRATION.md) |
| F8 | Stale entitlement / presence / supply snapshot | Refuse (G2 deny) before solver invoke; never solve on stale facts (source: docs/event-sourcing/SOURCING_SOLVER_OPTIMIZER_INTEGRATION.md) |
| F9 | Non-terminal stage attempts a command | Refuse — side effects owned only by the terminal handler (source: docs/event-sourcing/rules/jijibora-community-sourcing-rule.example.json; source: D-03 — Jijibora Playbook Designs (developer)) |
| F10 | Sealed content / unattested provider | Refuse — sealed content never travels in a match; unattested provider refused (source: docs/event-sourcing/rules/jijibora-community-sourcing-rule.example.json) |
| F11 | Telemetry goes down (crews offline) | Standby on last-known facts with explicit staleness; optimizer does not invent arrivals/completions; standby holds plans instead of replanning blind (source: docs/jijibora-government-trial/08_LIMITS_AND_RISKS.md) |
| F12 | Magic-fill regression (legacy) | Banned patterns: hardcoded farm lists, magic 100kg answers, catch → OPTIMAL synthetic fills — covered by fixture parity tests (Soft fill greedy) and promotion gates (source: docs/event-sourcing/SOURCING_SOLVER_OPTIMIZER_INTEGRATION.md) |
Standby semantics: F1/F2/F3/F11 put the pipeline in standby — the last feasible plan holds, new solves are refused, and readiness probes report the real downstream state. Readiness is probed honestly (GET gateway /health — never fake vroom_reachable) (source: docs/event-sourcing/SOURCING_SOLVER_OPTIMIZER_INTEGRATION.md).