Sign in to edit tickets from this page.

← all tickets · home

list_turns rows include events_emitted and entities_touched

resolved f75377f3-931f-4eac-ba04-cf27f7fbb7dd

created_at
2026-04-23
updated_at
2026-04-24
code_context
src/mcp.rs, src/persistence.rs
priority
P2
ticket_type
feature
parent
8894dd80
resolved_at
2026-04-24
resolution
accepted

Body

CONTEXT

Child of 8894dd80 (model adjustments for the web UI). See that parent ticket for motivation, non-gaps, out-of-scope list, and parallelization map. This ticket is narrowly scoped to Gap 1.

================================================================ THE CHANGE

handle_list_turns in src/mcp.rs currently returns rows shaped:

{ "turn": , "turn_ref": "turn_NNNNNN", "simulation_time": "", "entity_count": // count of world entities at that turn }

Add two fields to each row:

{ ...existing fields..., "events_emitted": , // count of audit events with turn == this turn, // filtered to the committed attempt "entities_touched": [] // sorted, deduped, taken from the // committed intent_adjudicated event(s) // for this turn }

Both values are computed server-side from the audit log for this turn. Do not change entity_count (it's a different thing — the roster size at that turn, not an event-derived value).

================================================================ WHERE THE DATA LIVES

The audit log is events.jsonl under the world's data dir, accessible through AuditLog in src/persistence.rs. Each line is an event with at minimum:

{ "_seq": , "turn": , "attempt_id": "", "attempt_status": "committed" | "failed", "event_type": "perception_emitted" | "intent_formed" | "intent_adjudicated" | "adjudication_rejected" | "turn_complete" | "attempt_failed", ... }

For events_emitted: count all events where turn == N AND attempt_status == "committed". This excludes failed-attempt noise from the count. The count should NOT include the turn_complete terminator itself, OR it should — handler's call; pick one and document the choice in a code comment. (My preference: include turn_complete. It IS an audit event emitted during that turn. Consistent with the count the kernel already logs at commit time.)

For entities_touched: read any intent_adjudicated events for the turn (turn == N, attempt_status == "committed", event_type == "intent_adjudicated"). Each such event carries an entities_touched array. Union them across all adjudication events for the turn, sort, dedupe, return.

Multiple agents per world is currently out-of-scope but the scheme must not blow up if it ever grows — hence "union across adjudication events for the turn."

================================================================ IMPLEMENTATION NOTES

handle_list_turns currently loops over turn refs and calls rt.turns.read_ref(r) for each. Don't add a per-turn call to scan the entire audit log — that's O(T × E). Instead, do ONE pass over the audit log up front, building a HashMap<u64, PerTurnRollup> keyed by turn number, where:

struct PerTurnRollup { events_emitted: u32, entities_touched: BTreeSet, }

Then while assembling each row, look up the rollup by turn number. Missing turn → both fields are zero/empty (turn 0, the seed, has no events; it should emit events_emitted: 0 and entities_touched: []).

The AuditLog::scan_all (or whatever read-path exists — check src/persistence.rs) reads sequentially. Single pass, then O(1) lookup per row.

================================================================ TESTING

Add unit or integration test: seed a world, run two turns, call list_turns, assert each row has events_emitted and entities_touched. The ant scenario is the obvious fixture — turn 0 should show events_emitted: 0, entities_touched: []; turn 1 should show events_emitted: 4 (or 3 depending on the turn_complete-inclusion choice above), entities_touched: ["ant"].

Include this test in tests/ant_scenario.rs or add a new case to tests/phase0.rs using the phase-0 inert scenario if a non-LLM fixture is reachable. The existing live-router test coverage is acceptable too; the ticket doesn't mandate one over the other.

================================================================ ACCEPTANCE

================================================================ SCOPE DISCIPLINE

Proposed resolution

Implemented, committed, merged, deployed, and smoke-verified in production.

What changed:

Unchanged: entity_count (still the roster size at that turn, a distinct concept); all other handlers; event emission paths; kernel; no cache.

Receipts:

Deploy + production smoke:

Performance: single pass over audit log per list_turns call, O(1) lookup per row. No per-row scan.

Child of 8894dd80 (model adjustments for web UI). Propose caller confirm this child so the parent queue clears. I am not confirming — over to you.

History (4 events)

Sign in as a human to drive this ticket from the page, or use the MCP tools.