Problem
Behavioural events — retry, retries_exhausted, loop_detected (kind + aborted), budget_denied, compaction (before/after tokens), policy_decision (kind), speculation_discarded, steered, provider_fallback, usage_incomplete — live only in the events journal. The observatory's /api/execution-tendencies (added with the Sessions tab, see crates/stella-observatory/src/sessions.rs::execution_tendencies) folds them per execution, which is the sanctioned per-execution events read. But session-, workspace-, and org-level tendency trends are impossible without a projection: the observatory's journal rule ('a cross-execution view needs a projection, not a wider WHERE', crates/stella-observatory/src/db.rs::execution_journal docs) forbids the query that would compute them.
Build
A turn_tendencies table in crates/stella-store/src/ddl.rs (one row per execution: retries, retries_exhausted, loop_detections, loop_aborts, stagnations, budget_denials, compactions, tokens_reclaimed, policy_blocked, secrets_detected, speculation_discarded, steered, provider_fallbacks, usage_incomplete), written at turn end from record_execution_end (crates/stella-cli/src/agent/persistence.rs, beside finalize_execution_reflection) — or live-projected like tool_calls v18. crates/stella-serve/src/observe/tally.rs::TallyFold already computes exactly this vector in memory; reuse its fold rather than writing a second one. Add the table to prune.rs::DEPENDENT_TABLES (its test enforces coverage).
Then the observatory can aggregate per session (JOIN executions ON session_id) and per day, and /api/execution-tendencies can read the projection with the journal fold as fallback for old stores.
Verify
Witness: seed an execution whose journal carries a retry + a loop_detected via Store::record_event, finish the turn, assert the turn_tendencies row exists with those counts — fails on main (no table). Observatory side: extend crates/stella-observatory/tests/schema_conformance.rs.
Constraints
- No I/O in stella-core (invariant 2); the fold is pure, the write goes through stella-store.
- Migration bumps
migrations::SCHEMA_VERSION; the observatory must keep degrading on stores without the table (is_missing_schema).
Refs #1461, #1476.
Problem
Behavioural events —
retry,retries_exhausted,loop_detected(kind + aborted),budget_denied,compaction(before/after tokens),policy_decision(kind),speculation_discarded,steered,provider_fallback,usage_incomplete— live only in theeventsjournal. The observatory's/api/execution-tendencies(added with the Sessions tab, seecrates/stella-observatory/src/sessions.rs::execution_tendencies) folds them per execution, which is the sanctioned per-executioneventsread. But session-, workspace-, and org-level tendency trends are impossible without a projection: the observatory's journal rule ('a cross-execution view needs a projection, not a wider WHERE',crates/stella-observatory/src/db.rs::execution_journaldocs) forbids the query that would compute them.Build
A
turn_tendenciestable incrates/stella-store/src/ddl.rs(one row per execution: retries, retries_exhausted, loop_detections, loop_aborts, stagnations, budget_denials, compactions, tokens_reclaimed, policy_blocked, secrets_detected, speculation_discarded, steered, provider_fallbacks, usage_incomplete), written at turn end fromrecord_execution_end(crates/stella-cli/src/agent/persistence.rs, besidefinalize_execution_reflection) — or live-projected liketool_callsv18.crates/stella-serve/src/observe/tally.rs::TallyFoldalready computes exactly this vector in memory; reuse its fold rather than writing a second one. Add the table toprune.rs::DEPENDENT_TABLES(its test enforces coverage).Then the observatory can aggregate per session (
JOIN executions ON session_id) and per day, and/api/execution-tendenciescan read the projection with the journal fold as fallback for old stores.Verify
Witness: seed an execution whose journal carries a retry + a loop_detected via
Store::record_event, finish the turn, assert theturn_tendenciesrow exists with those counts — fails on main (no table). Observatory side: extendcrates/stella-observatory/tests/schema_conformance.rs.Constraints
migrations::SCHEMA_VERSION; the observatory must keep degrading on stores without the table (is_missing_schema).Refs #1461, #1476.