Session Breakdown Skill
What it produces
A single JSON file: <session_dir>/session_breakdown.json.
- Schema:
hyperloom.session_breakdown.v6.1(hardcoded; seeSCHEMA_VERSIONinbreakdown/schema.py). - Producer:
src/hyperloom/inference_optimizer/breakdown/exporter.py - Filename:
BREAKDOWN_FILENAME(=session_breakdown.json)
Beyond the envelope (schema_version / exported_at_utc / exporter_version), the JSON
carries the sections below. This is the subset most consumers use, not the full set —
breakdown/exporter.py (the breakdown = {...} literal) and breakdown/schema.py are
authoritative.
| Section | What it carries |
|---|---|
schema_version | The wire contract. Gate features on the major version, not the exact string. |
exported_at_utc | When this export was built. |
exporter_version | Which exporter built it. |
metadata | Session identity, launch configuration, grading -- the mode marker the session started in, not the axis promotions were decided on (outcome.validation.graded_on) -- component versions, Langfuse receipt, and warnings, how the export itself went, reported once and only here. |
outcome | Terminal status, stage reached, stop reason, the baseline and final measured results with the graded axes each reported, the validation that reconciles the stack's parts against its total on one axis, named by graded_on, and variants, the framework phase's bench runs counted by how each ended. |
timeline | The run itself: one event per stage, oldest first, each carrying its span, status, and an ext block of what that kind of stage records. Startup source events live under reports/sbd_v6/timeline/. |
close | What the session settled at close: the steps the sequencer ran and the artifacts it published. |
critic | The critic agent's own run, iteration by iteration: what it was asked about, how its rulings fell (verdict_counts), and the four artifacts each pass left behind. Per-proposal verdicts stay with the proposals, on the timeline. |
Who reads it
- A downstream stats/reporting service — primary consumer. Replaces the
MAE-synthesized
raw_report/fact_sheet. Recommended read order: prefersession_breakdown.jsonif present, fall back to legacy MAE output otherwise. - Offline / notebook analysis — single file, easy to load, no DB needed.
When to refresh (LLM Orchestrator decision tree)
The Coordinator's cli.py finally block writes session_breakdown.json
unconditionally at end-of-session — that's the safety net. But you
should ALSO refresh it eagerly when downstream dashboards may be
watching this session live:
- Always at end-of-session (handled by
cli.py finally— you do not need to dispatch this). - After every KEEP'd explore, specialist, framework, or kernel result whose benchmark changed the final stack.
- After a successful sweep or conc_sweep when a live dashboard is observing this session.
- Never mid-action — collectors expect a coherent state snapshot.
Dispatch action session_breakdown — a single 1-minute action, no inputs
required.
How to invoke
LLM-driven (Coordinator action)
yaml# Issue an action intent like any other action { "action": "session_breakdown", "params": {} }
Code-driven (Python import)
pythonfrom hyperloom.inference_optimizer.breakdown import build, write_breakdown_json # Just compute the dict (no side effects) breakdown = build("/workspace/hyperloom") # Compute + atomically write to <sd>/session_breakdown.json out_path = write_breakdown_json("/workspace/hyperloom")
CLI / offline (hyperloom session breakdown)
bash# Live session in this sandbox python -m hyperloom session breakdown # Historical session on a shared filesystem python -m hyperloom session breakdown \ --session-dir /shared/hyperloom-sessions/<user>/<sid> # Override output path (e.g. write to a staging area) python -m hyperloom session breakdown \ --session-dir <SD> --output /tmp/breakdown.json
Bulk historical (operator)
bashfor d in /shared/hyperloom-sessions/*/*; do [ -d "$d" ] || continue python -m hyperloom session breakdown \ --session-dir "$d" > /dev/null done
Field reference (what comes from where)
The collector for each section reads only from the listed sources. All
collectors are pure functions; failure in one section never poisons
another (each becomes a warnings[] entry instead). Like the table above,
this reference is partial — breakdown/exporter.py is authoritative.
| Section | Read from |
|---|---|
metadata | manifest.json + state.json, overlaid by the recorder's own session / task_config / grading / versions fragments. grading is recorded only: the axis is resolved at seed, where the run can still see its own configuration, and re-deriving it here would read the exporting subprocess's environment |
outcome | The recorder's close and stack fragments, plus state.{current_best, cumulative_gain_validated, optimization_stack} for the sessions that predate them |
timeline | The event fragments in the spool, closed and assembled per event; orphans left open by a killed phase are closed on first build |
close | The CLOSE sequencer's own close / close_step fragments |
critic | critic-workdir/<NNN>/{request,judge_bundle,emit,review}.json, recorded as each pass returns |
What is NOT in scope
- Real-time event streaming — use
claw_session_eventsfor that. - LLM-based attribution — every collector is deterministic /
rule-based. Attribution is
delta_pctmath, not natural language. - Schema migration — consumers MUST check
schema_versionand gate features on it, comparing the major version (vN) rather than the exact string: the producer emits both…v2and…v3.0today (see Versioning policy) and they are wire-compatible. - Cross-session aggregation — one file per session. Use a Jupyter notebook or downstream analytics job for fleet views.
Failure modes
| Symptom | Cause | Mitigation |
|---|---|---|
warnings: ["state.json missing"] | Session was created (manifest written) but Coordinator.save() never ran | Sections fall back to manifest-only data. |
warnings: ["manifest.json missing"] | Session was created without the standard cli.py path (rare) | session.session_id falls back to state.session_id. |
optimizations.available = false | No operations were recorded, or the projection failed | Read unavailable_reason. The empty arrays mean unknown, not none; a warnings entry says whether state.json knew of adopted work the recorder missed. |
optimizations.validation.unclaimed_integration_count > 0 | A change recorded as integrated has no adoption crediting it | The adoption write was lost. unattributed_gain_pct is overstated by whatever those steps earned. |
optimizations.entries[].gain_method = local_gain_projected | The step recorded no finishing throughput | Its gain is projected from the executor's own percentage, not measured against the chain. |
kernel_lifecycle.detected = [] | profile action never ran or its benchmark_report.json had no kernel_summary | Re-run profile, or fall back to recommended from state.last_trace_analyze. |
Large warnings[] | Multiple JSON parse failures on optimization_attempts.jsonl | Inspect kernel-agent-workspace/.../logs/ for the corresponding kernel-agent CLI logs. |
Versioning policy
schema_version(inschema.py) carries the major contract version; it is bumped ONLY on breaking changes (renamed/removed fields, changed semantics), apart from the declared 6.1 changes below.- New exports carry
hyperloom.session_breakdown.v6.1. 6.1 changes three things inside the 6 major, a declared exception to the major-bump rule: it renames one key, removes keys no producer populated (disposition: removed in 6.1, never populated) or that only carried a constant (roofline_measured, the baseline action'sconvergence; disposition: removed in 6.1, constant), and reportsattributed_gain_pct,unattributed_gain_pctand runemptyas null when unmeasured or not provided.throughput_tok_s_per_gpuinoutcome.baseline,outcome.finaland thebaselineevent measurement is nowoutput_throughput, and the old key is no longer written. Archived 6.0 documents and 1.1.x exports keep the old key, so readers must accept both or migrate. Re-exporting a 6.0 session directory yields a nulloutcome.baseline.output_throughput, keeps the session's oldcontract_digest, and overwrites the archived file at the default output path. - V6 was a breaking
cutover for the timeline: the actions record their own events as they run,
so an event's
start_timeis when the work began rather than when its artefacts were written, and the KERNEL and BASELINE projections are gone. Consumers that sorted around the old collapsed windows need to be rechecked. - Inside v6,
enablementgained the round ledger:rounds[]and its counters are added to the block, and the three state-sourced fields they replace (stall_streak,inflight_task_id,dispatch_tick) are no longer emitted. The block is runtime observability that is already{}on a session that ran no enablement, so it carries no field a consumer can gate a version on; the disposition of each replaced field is indocs/reference/session-breakdown.md. - V5 was the preceding cutover, for optimization results:
optimizationsis reshaped, and theoptimization_stack,attribution,geak_invocations,forge_invocations, andgemm_tuningprojections are gone. Consumers MUST match on thevNmajor prefix, never on exact-string equality, and archived V2/V3/V4/V5 documents need a migration before a V6 reader sees them. optimizationscarries its ownschema_version(currently5), independent of the envelope's.- Adding optional fields is never a breaking change.
exporter_versiontracks the exporter implementation independently; consumers can ignore it.
Testing
Each collector has a unit test under src/hyperloom/inference_optimizer/tests/
that runs it against a fixture session_dir tree. An end-to-end test calls
build(...) on a fully-populated fixture and JSON-schema-validates the
result. Run:
bashpytest src/hyperloom/inference_optimizer/tests/ -k breakdown -v


