std.compute.coordination
std.compute.coordination
Section titled “std.compute.coordination”std.compute.coordination is a CPU-first :compute module for deterministic
multi-agent planning. It is meant for civilian swarms: field drones, farm
robots, sensor walkers, irrigation rovers, inspection crawlers, and similar
systems that need bounded coordination without a central operator in every loop.
The module is intentionally not an autopilot. It does not own sensors, radio, motors, filesystem handles, or network links. Callers provide observed agent state, work zones, hard obstacles, Gaussian influence fields, and output buffers. The planner writes assignments.
Import
Section titled “Import”Current AOT examples use a direct module import for functions and selective type imports for struct literals:
use std.compute.coordinationuse std.compute.coordination { AgentState, Assignment, AssignmentStats, CoordHealth, GaussianKernel, Obstacle, PlanConfig, WorkZone,}The aggregate std.compute module also re-exports coordination. Use
std.compute.coordination_stl when a decision summary should become an STL
event:
use std.compute.coordination_stlCurrent Scope
Section titled “Current Scope”| Area | Surface |
|---|---|
| Constants | DECISION_FRAME_BYTES, COORD_CELL_KEY_BYTES, COORD_CELL_NEIGHBOR_COUNT, COORD_UNREACHABLE_TRAVEL_TIME |
| Geometry | distance_sq, distance |
| Gaussian fields | GaussianKernel, gaussian_at, field_at |
| Agent memory | AgentState, AgentReadiness, agent_stale_ticks, agent_memory_remaining_ticks, agent_memory_valid, agent_readiness, agent_can_travel |
| Work model | WorkZone, Obstacle, PlanConfig, ZoneReadiness, zone_readiness, zone_blocked |
| Candidate facts | CandidateFacts, zone_idle_ticks, travel_time, candidate_facts, candidate_status |
| Catalog keys | CoordCell, cell_from_units, coord_cell_equal, coord_cell_neighbor, write_coord_cell_neighbors, encode_coord_cell_key, decode_coord_cell_key |
| Planning | score_candidate, plan_greedy, Assignment, AssignmentStats, CoordHealth, assignment_ok, assignment_rejected, assignment_conflict, count_assignments_by_status, find_assignment_by_agent, find_assignment_by_zone, empty_assignment_stats, assignment_stats, assignment_stats_rejected_count, assignment_stats_conflict_count, assignment_stats_ok_rate, assignment_stats_rejected_rate, assignment_stats_conflict_rate, assignment_stats_stale_memory_rate, assignment_stats_health |
| Audit summary | DecisionFrame, DecisionLedger, summarize, encode_decision_frame, decode_decision_frame, merge_decision_frame, replay_decision_frames, decision_frame_total_rows, decision_ledger_total_rows, decision_ledger_rows_per_frame, decision_frame_health, decision_ledger_health, decision_ledger_tick_span, decision_frame_assignment_rate, decision_frame_rejected_rate, decision_frame_conflict_rate, decision_frame_stale_agent_rate, decision_ledger_assignment_rate, decision_ledger_rejected_rate, decision_ledger_conflict_rate, decision_ledger_stale_agent_rate |
| STL bridge | std.compute.coordination_stl: event_kind_for_frame, make_event, set_effect_from_frame, decode_event_frame, merge_event_frame |
plan_greedy is deterministic and allocation-free. It writes one
Assignment per input agent, marks stale agents as stale_memory, avoids hard obstacles,
applies soft Gaussian coverage/avoidance fields, and gives each zone at most
one owner per tick.
Gaussian Fields
Section titled “Gaussian Fields”Gaussian kernels are the shared bridge between agricultural spray planning,
robot influence fields, and the Gaussian-splat work already planned for
:compute.
let coverage = GaussianKernel { x: 0.0, y: 4.0, sigma: 2.0, amplitude: 3.0,}
let strength = coordination.gaussian_at(&coverage, 0.0, 4.0)Use positive coverage fields to pull work toward under-served soil patches,
sensor gaps, or inspection zones. Use avoid fields as positive penalties for
spray drift, no-spray boundaries, people, roads, ponds, or fragile crops.
Hard geometry still belongs in Obstacle; Gaussian fields are soft scores, not
safety barriers.
Candidate Facts
Section titled “Candidate Facts”Use CandidateFacts when a caller needs preflight logs, UI explanations, or a
simple filter before invoking the planner.
let facts = coordination.candidate_facts(&agent, &zone, &cfg)
if facts.can_travel do let eta = facts.travel_timeendcandidate_facts reports distance, speed-based travel time, ticks since the
zone was last served, and whether the agent has fresh memory, positive tank,
and positive speed. If speed is zero or negative, travel time is reported as
COORD_UNREACHABLE_TRAVEL_TIME. Use candidate_status when a caller needs
the planner’s status vocabulary for one agent-zone pair before scoring:
let status = coordination.candidate_status(&agent, &zone, obstacles, obstacle_count, &cfg)
if status == .unsafe_assignment do // hard geometry rejects this pairendcandidate_status returns ok, stale_memory, no_candidate, or
unsafe_assignment; capacity_exceeded remains a caller-buffer result from
plan_greedy. Use agent_stale_ticks when a caller needs the clamped age of
an observation, and agent_memory_remaining_ticks when it needs the freshness
budget before checking agent_memory_valid:
let age = coordination.agent_stale_ticks(&agent, &cfg)let remaining = coordination.agent_memory_remaining_ticks(&agent, &cfg)Future observation timestamps clamp to zero, matching zone_idle_ticks for
future last_served_tick values. Expired memory and exact-TTL memory both
report zero remaining ticks. Use agent_readiness when a caller needs the
priority-ordered reason an agent cannot be dispatched:
let readiness = coordination.agent_readiness(&agent, &cfg)
if readiness == .stopped do // memory and tank are usable, but speed is zeroendAgentReadiness is ready, inactive, stale_memory, empty_tank, or
stopped. Use agent_can_travel when a caller only needs the boolean form
before choosing a zone:
if coordination.agent_can_travel(&agent, &cfg) do // memory is valid, tank is positive, and speed is positiveendcandidate_facts intentionally does not fold in Obstacle geometry.
Use zone_readiness when a caller needs the reason for hard obstacle rejection
before planning:
let zone_state = coordination.zone_readiness(&zone, obstacles, obstacle_count, &cfg)
if zone_state == .blocked do // hard geometry rejects this zone before scoringendZoneReadiness is ready or blocked. Use zone_blocked when a caller only
needs the boolean form:
if coordination.zone_blocked(&zone, obstacles, obstacle_count, &cfg) do // the planner will reject this zone for hard geometryendHard safety remains in score_candidate and plan_greedy; zone_readiness
and zone_blocked expose the same hard-obstacle predicate for preflight logs
and operator UI.
Catalog Cells
Section titled “Catalog Cells”CoordCell is the LMX-facing key material. It does not open or write an LMX
store; it provides deterministic cell IDs and canonical key bytes for callers
that maintain read-heavy field maps or zone catalogs.
let cell = coordination.cell_from_units(x_units, y_units, cell_size_units, 0)var key: [24]u8 = undefined
if coordination.encode_coord_cell_key(key[0..], &cell) != .ok do return 1endThe key layout is "JCK1" magic, level as u32 little-endian, then x and
y as i64 little-endian. cell_from_units uses floor division for negative
coordinates, so cells stay stable across the origin. Callers own the
world-units quantizer; the planner does not guess how meters, rows, map tiles,
or simulation units should map to integer space.
For nearby reads, write the center cell plus its eight adjacent cells:
var cells: [9]CoordCell = undefinedlet count = coordination.write_coord_cell_neighbors(&cell, cells, 9)The order is deterministic: center, west, east, south, north, southwest,
southeast, northwest, northeast. Use coord_cell_neighbor for a single offset
cell and write_coord_cell_neighbors when an LMX caller wants to probe the
3x3 catalog window around an agent or zone.
Planning
Section titled “Planning”var assignments: [3]Assignment = undefined
let written = coordination.plan_greedy( agents, 3, zones, 3, obstacles, 1, coverage_fields, 1, avoid_fields, 1, &cfg, assignments, 3,)The score combines:
- readiness gating through
agent_readiness - hard-safety gating through
zone_readiness - zone demand
- zone priority
- positive Gaussian coverage
- negative Gaussian avoid fields
- distance cost
- stale-memory penalty
- recharge priority when an agent has low tank state
Inactive and stale agents are emitted as stale_memory rows. Empty-tank and
stopped agents are emitted as no_candidate rows before zone selection, so a
zero-speed agent cannot receive an assignment simply because a zone scored
well. If a ready agent has no safe unassigned zone because hard geometry blocks
the candidate set, the row is emitted as unsafe_assignment.
The first shipped planner is deliberately simple. It is the stable scalar reference surface. More sophisticated planners can later sit beside it, but they must keep the same evidence discipline: deterministic inputs, explicit buffers, and testable assignment output.
Assignment Queries
Section titled “Assignment Queries”Planner output is just caller-owned Assignment rows. The query helpers keep
common UI/controller loops deterministic:
var row: Assignment = undefined
if coordination.find_assignment_by_agent(assignments, written, agent_id, &row) == .ok do if coordination.assignment_ok(&row) do // row.zone_id is owned by this agent for the tick endendUse assignment_rejected when one row failed to produce usable zone ownership.
Use assignment_conflict when a single row should be treated as a safety or
capacity conflict:
if coordination.assignment_conflict(&row) do // unsafe_assignment or capacity_exceededendUse count_assignments_by_status for a single status bucket. Use
assignment_stats when a dashboard or controller needs all buckets in one pass:
let stats: AssignmentStats = coordination.assignment_stats(assignments, written)let ok_rate = coordination.assignment_stats_ok_rate(&stats)let stale_rate = coordination.assignment_stats_stale_memory_rate(&stats)let rejected = coordination.assignment_stats_rejected_count(&stats)AssignmentStats records total rows plus ok, no_candidate,
stale_memory, capacity_exceeded, and unsafe_assignment buckets.
assignment_stats_conflict_count folds the two deconfliction/capacity buckets
into one count. Use assignment_stats_ok_rate,
assignment_stats_rejected_rate, assignment_stats_conflict_rate, and
assignment_stats_stale_memory_rate when a UI needs normalized live-buffer
signals. find_assignment_by_zone only returns successful rows, so rejected
or stale rows with zone_id == 0 never look like zone ownership.
For a compact controller or dashboard signal, classify the same stats:
let health: CoordHealth = coordination.assignment_stats_health(&stats)CoordHealth is empty, healthy, degraded, failed, or conflicted.
Conflicts win over success counts; otherwise all-ok rows are healthy, partial
success is degraded, and zero successful rows is failed.
LSM, LMX, And STL
Section titled “LSM, LMX, And STL”Storage is outside the hot loop.
std.db.lsmis the write-optimized substrate for observation logs, mission event streams, and replay input.std.stl.lsm_storeis the audit layer: encode aDecisionFrameintoEvent.effect_inline, choose an intent event kind, then append through the LSM-backed STL store.std.db.lmxis the read-optimized B+ tree path for future static field maps, zone catalogs, and geometry indexes.CoordCellplus theJCK1key codec provide stable key material for those catalogs, while the store remains owned by the caller.
DecisionFrame is the handoff point:
let frame = coordination.summarize(assignments, written, cfg.tick)var effect: [28]u8 = undefined
if coordination.encode_decision_frame(effect[0..], &frame) != .ok do return 1endFor callers that already use STL events, std.compute.coordination_stl performs
the event shaping:
var e = coordination_stl.make_event(&frame, timestamp_nanos, epoch)The bridge selects IntentConflicted when conflicts are present,
IntentSatisfied when at least one assignment succeeded, IntentRejected when
all rows were rejected, and IntentReceived for an empty frame. The module
lives under std.compute, not std.stl, because core STL must not depend on a
higher-profile compute planner.
The LSM-backed proof appends those generated events through
std.stl.lsm_store, reads them back by id and insertion rank, decodes the
inline frame, merges it into a DecisionLedger, flushes the store, and verifies
post-flush readback.
That frame is small enough for STL inline effects. It records the tick,
assigned count, rejected count, conflict count, and stale-agent count. Full
observations and field maps should stay in LSM/LMX keyed storage; use
CoordCell keys for read-heavy LMX catalogs, and use the STL event to anchor
the decision summary for replay and accountability.
After reading events back from STL/LSM, decode each inline effect and merge it into a ledger:
var ledger = coordination.empty_decision_ledger()var decoded: DecisionFrame = undefined
if coordination.decode_decision_frame(effect[0..], &decoded) == .ok do coordination.merge_decision_frame(&ledger, &decoded)endDecisionLedger accumulates first tick, last tick, frame count, assigned rows,
rejected rows, conflicts, and stale-agent rows. Use decision_ledger_tick_span
to report last_tick - first_tick for the replay window. Empty and single-tick
ledgers return zero. Use decision_frame_total_rows /
decision_ledger_total_rows when a caller needs the denominator behind the rate
helpers. Use decision_ledger_rows_per_frame to report average replay load per
decoded frame. Use
decision_frame_assignment_rate / decision_ledger_assignment_rate for bounded
success-rate signals, decision_frame_rejected_rate /
decision_ledger_rejected_rate for normalized rejection pressure, use
decision_frame_conflict_rate / decision_ledger_conflict_rate when a
dashboard needs normalized deconfliction pressure, and use
decision_frame_stale_agent_rate / decision_ledger_stale_agent_rate to track
memory freshness pressure while the full event stream remains in storage.
Use decision_frame_health for one decoded frame and
decision_ledger_health for an accumulated replay ledger when a consumer wants
the same CoordHealth vocabulary used by planner-output stats.
The focused proof gate is:
cd janus./scripts/zb test-coordination./scripts/zb test-coordination-stl./scripts/zb test-coordination-stl-lsmThe aggregate ./scripts/zb test also depends on the same smoke harnesses.