Releases: a3mc/abracadabra
Release list
v0.5.0
Minor release. Aligns the parser with the community cluster's move
to the main Agave repository (Alpenglow: 200 ms blocks, ~250 ms
finalization) and adds two visible features in the TUI: a
selected-slot detail card in the Slots tab and a top-3 packed
blocks leaderboard in the Live tab's chain widget.
Changed
- Parser format alignment. Upstream Alpenglow refactored the
Blocktype from a(Slot, Hash)tuple tostruct Block { slot, block_id }; theDebugoutput that abracadabra scrapes changed
shape for six event categories (Block,Block Notarized,
Block notar-fallback,Finalized,Parent ready,Produce window). All six parsers now consume the new struct form. Old
tuple-format logs are no longer parseable — block, notarize,
and finalize events will silently drop. This is intentional: the
community cluster restarted onto the Alpenglow build, so historic
tuple logs from the pre-move chain are no longer operationally
relevant. Includes a regression guard (DRIFT-01) that
include_str!s a slab of real log lines and asserts every event
category yields at least one parse — future upstream drifts fire
on the firstcargo testafter a rebase. - Latency band recalibration. Old thresholds (assembly 500/600,
lifecycle 600/1000 ms) were tuned for the pre-Alpenglow slot
cadence; under Alpenglow every value coloured green (p99 lifecycle
= 495 ms, well below the old warn line). Recalibrated to
assembly 250/300 ms and lifecycle 380/500 ms, backed by empirical
p50/p95/p99 measurements on a 3 h reference log. The middle band
now uses cyan (accent_style) instead of yellow via a new
band_latency_softhelper — yellow reads too harsh for the small
window between "healthy" and "degraded" under the new cadence.
Skip-rate and canonical-skip bands keep their yellow warning
semantics.
Added
- Selected-slot detail card in the Slots tab. The reference
panel now shows a compact per-slot timeline for the cursor-focused
row: block id (short), signature count, and every captured event
timestamp as a delta from an anchor. Anchor labels name the event
class ((start)forfirst_shred,(emit)for our own leader
slots,(replay)for repair-fetched slots,(anchor)for
skipped-slot fallbacks) so the reader can tell what the deltas
span without consulting the footer. Every value is a direct
log-event timestamp — no inference. sigscolumn in the Slots table —signature_countfrom
thebank frozenlog line. Empty for slots the local node did
not bank (repair-fetched, gossip-observed).- Top-3 packed blocks leaderboard in the chain pane (Live tab).
Small right-side panel listing the three signature-heaviest
blocks observed since the pane started. Fork-safe (first-wins on
the underlyingsignature_count), zero-sig blocks excluded,
session-long (no reset). Layout mirrors the tx stream on the
right side of the bucket with a one-column border gap.
Fixed
- Slots detail card path tags. The
(slow-path)suffix on
notar_fallbackrows was unconditional, but the vast majority of
BlockNotarFallbackevents are benign auto-emitted companions of
successful notarize certs — on any fast-finalized slot the card
printednotar_fallback (slow-path)immediately followed by
finalized (fast-path), contradicting itself. Now gated on the
row's actual path. - Slots detail card anchor note. The footer text branched on
we_are_leaderand claimedt0 = our local replay completefor
every non-leader slot — but for the majority of observed slots
the anchor isfirst_shred_at(shred receipt), not replay. The
note now branches on the actual anchor event and describes each
case correctly. - Latency-band legend labels now use
</≥to match the
underlying band function's inequality (previously≤/>— a
one-off at exact boundaries).
v0.4.1
Minor release. Adds an alternative input source — operators can now
stream directly from a systemd journal unit instead of pointing at a
log file. Useful on setups where log files are not exposed or where
rotation makes the right path hard to pin down. Also includes a
follow-up to the LIVE-70 work that lets the shred-streams snapshot
surface a (peak N) annotation when the chart shows recent err
activity that the last batch sample alone would hide.
Added
-
--unit <SERVICE>— stream the log from a systemd journal unit
instead of a file. Mutually exclusive with the positional log
path via clapArgGroup(exactly one must be passed). Spawns
journalctlvia argv-safeCommand::args([...])— no shell
involved, so operator-supplied unit names cannot inject. The
child is held inArc<Mutex<Child>>onTailHandle;Drop
signals shutdown, kills the child, and waits. -
--since <SPEC>— historical scan window for journal mode.
Default10 minutes ago. Accepts anythingjournalctl --since
understands (today,yesterday,"1 hour ago", ISO dates).
Bounds the historical bulk read only; the Live tab tail starts
following from "now" viajournalctl -n 0 -fregardless of the
--sincewindow. -
LogSourceenum (File(PathBuf)/Journal { unit, since })
in a newcrate::sourcemodule.runnerandlive::tailboth
match on it;mainbuilds it from the parsed CLI args.
Changed
- Shred-streams snapshot in the Live tab appends
(peak N)after
theerrfield when the visible chart window has seen a batch
with more errors than the most recent sample (LIVE-70). The
annotation is suppressed in the steady state, so the line stays
compact when there is nothing extra to surface. Matches the
chart's visible-window scale 1:1 viaLaneSpark::peak_recent.
Documentation
- README now demonstrates the journal-mode invocation pattern and
notes that--sincecontrols the historical scan only — the
Live tab always follows from "now" once tailing starts. Journal
mode is documented as Linux-only (requiresjournalctlon
PATH).
v0.4.0
Tracks alpenglow ag-v0.4.0. Major release adds the Live tab — a
real-time animation surface that tails an actively-written validator
log and presents chain progression, leader-window outcomes, shred
ingress, transaction pressure, and slot lifecycle as continuously
updating visualisations. Plus a runtime truecolor detector + 6×6×6
cube fallback for terminals that misparse 24-bit RGB, and a four-band
fast-finalize verdict ladder calibrated against operator-observed
cluster performance.
Added
- Live tab — real-time tail of an actively-written log. Hidden when
the log is static (rotated / stale / no size growth observed).
PressSPACEto start tailing,pto pause the animation,
hto toggle an in-app glossary that explains every widget
label and glyph. Composite scene engine with six panes:- Shred streams — turbine particle stream + repair/drop/err
sparklines fromShredFetch/ShredFetchRepair/
ShredSigverify/RecvWindowInsertmetric datapoints. - Slot outcomes — fast/slow/skip/FEC lane bars per
time-slice over a rolling 64-slot window. Skip lane uses
left-pointing triangle glyphs to read distinctly from the
slow lane's dots. - Chain — cannon-particle visualisation: tip-slot chip and
cannon at the top, particles fall into a paged 25×5 bucket
with magic-wipe transition between pages. Left-side tx
stream surfaces recentBankFrozen.signature_countper
slot (filters skipped slots and zero-sig blocks). Four
status chips showcadence/assembly/consensus/
lifecyclep50/p95 in ms. - Block production — recent leader windows as cards with
per-slot bank time, signatures, broadcast time, shreds, tx
counts, and outcome icons ([✓]/[~]/[…]/[✗]/
[A]). Headline summarisesbank avg/sig max/
sh max/since last block. Right-aligned timer column
lands cleanly in the right card slot. - Tx pressure —
BankFrozen.signature_countthermal area
chart with cold→hot gradient + dimmed area fill. Header
surfacesnow / avg / peakover the visible window. - Decorations — status strip with tail health, recent
event summary, pause indicator.
- Shred streams — turbine particle stream + repair/drop/err
- Truecolor detection + 6×6×6 cube fallback (
tui::truecolor).
Routes everyColor::Rgbcallsite through a runtime helper
that detects terminal capability at startup. Detection ladder:
CLI flag →NO_COLOR→COLORTERM=truecolor/24bit→
TERM_PROGRAM=Apple_Terminal→TERMsubstring match
(kitty / alacritty / wezterm / ghostty / iterm / vscode) →
default true. On the fallback path quantises to the nearest
of the 216 colours in the ANSI cube so chip backgrounds and
gradients render correctly on terminals that misparse 24-bit
SGR sequences (notably macOS Terminal.app). --no-truecolorCLI flag to force the 256-colour fallback.--force-truecolorCLI flag to skip the detection ladder
(escape hatch for SSH sessions where COLORTERM was stripped).
Mutually exclusive with--no-truecolorper clap.NO_COLORenv var honoured per the no-color.org convention.- Replay xtask (
cargo xtask replay-log) for offline Live-tab
development against a captured log. - Parser support for
ProduceWindow,Unable to produce window,
Triggering parent ready,ParentReady, and the
shred-pipeline metric datapoints (ShredFetch,
ShredFetchRepair,ShredSigverify,RecvWindowInsert,
ShredInsertIsFull,BankingStageCounts).
Changed
- Overview tab
fast-finalizeheadline rebanded to four
verdicts:perfect (>=98%)(green BOLD),OK (95-98%)
(green),fine (80-95%)(yellow),degraded (<80%)(red).
Verdict text shows each band's actual range rather than a
single lower threshold so the operator reads "I'm in the
80-95% band" rather than the misleading "above 80%". Text-
mode runner cutoff aligned with the newFAST_FIN_GOOD_PCT
(95) to keep CLI and TUI consistent. - Slots tab gains the
LSKIPfilter (we voted skip on a slot
the network ultimately kept canonical — local-skip on a
canonical slot). Distinct fromCSKIP(canonical-skip across
the cluster). - Status bar advertises
[h] helpon the Live tab so the
glossary toggle is discoverable.
Notes
- Tab key bindings:
1–7now span all tabs (Live = tab 7,
hidden in static-log mode). - Test count: 461 (was 222 at 0.3.3 release). Includes 4 new
integration tests incrates/abracadabra/tests/truecolor_render.rs
that prove everyColor::Rgbcallsite routes through the
truecolor detector by walking the rendered ratatui buffer
and asserting zero RGB cells when the fallback is active.
v0.3.3
Tracks alpenglow ag-v0.3.2. Post-release polish on standstill
reporting, runner text-mode verdicts, and Time Series presentation,
plus a new transaction-pressure card.
Added
- Transaction-pressure card on the Time Series tab. Series is
Σ signature_count / bucket_secondsfrombank frozenlog
lines, rendered astx/s(bucket-size-independent so 10m and
1h zoom levels read on the same scale). Stats line surfaces
min / avg / peakper second alongside an overall
tx/block (avg)derived fromΣ signature_sum / Σ blocks. Metric::with_subtitleon the Time Series renderer for inline
secondary stats on existing cards (no new grid cell required).Kind::Ratevariant for per-second metrics. Distinct from
Kind::Countbecause summing a rate across buckets is
meaningless; the stats line therefore drops thetotaland
/bktfields forRatecards.
Changed
StandstillObservedalerts dedup by anchor slot: repeated
StandstillExtendingevents at the sameat_slotmerge into
one alert withcount(firings) andlast_at(most recent
extension). Index lives onOverallStats.standstill_alert_indices
(transient). Previously a 2.5h standstill produced one alert
per extension batch (hundreds of rows).- Standstill alert detail pane shows
anchor / firings / first at / last at / spaninstead of a single timestamp. - Sparkline x-axis caption marked
(autoscale)so readers know the
range is data-driven, not a fixed window. - Headline-health canonical-skip line shortened from
(N canonical of M vote-skips · K indeterm)to
(N of M · K indeterm)to fit single-line width. - Local-leader summary row drops the
(4-slot bursts, per NUM_CONSECUTIVE_LEADER_SLOTS)qualifier. runner.rstext-mode health lines take separateok_noteand
bad_notestrings. Previously both paths reused one note,
producing contradictory output like[✗] no liveness issues.
Standstill bad-path verdicts now readSTANDSTILL OBSERVED/
resume activityinstead ofno liveness issues.- Runner crashed-leader count is standstill-aware when
standstill_rangesexist: subtracts TCLs that fired inside a
standstill window from the headline figure. cargo xtask lint-prodmirrors the CI strict clippy gate
exactly:cargo clippy --workspace --all-targets --locked -- -D warnings. Previously it omitted--all-targets,
--locked, and-D warnings, allowing local "pass" while
CI failed.
Fixed
- Tx-pressure stats line previously claimed
total N peak K/bkt avg J/bktfor a per-second series.totalwas the sum of
rates (meaningless); the/bktsuffix lied about the unit
(values were per-second). Fixed by routing the card through
Kind::Rate. clippy::derivable_implsonSkipClassification: replaced
manualDefaultimpl with#[derive(Default)]+
#[default]onNotSkipped.clippy::manual_checked_ops(Rust 1.96+) on the
avg_tx_per_blockdivide-by-zero guard: replaced
if d > 0 { a / d } else { 0 }with
a.checked_div(d).unwrap_or(0).
Toolchain
- Added
rust-toolchain.tomlpinning the project to Rust
1.96.0..github/workflows/{ci,release}.ymlinstall the
same version viadtolnay/rust-toolchain@masterwith
explicittoolchain: 1.96.0. Previously local and CI both
said "stable" but tracked different versions, so new lint
categories (e.g.manual_checked_opsin 1.96) appeared at
push time. Bumping Rust now means editing one file plus the
two workflow inputs in lockstep.
v0.3.2
Version aligned with the alpenglow ag-v0.3.2 tag this release was
validated against.
Added
- Stage 1 canonical-skip classifier (
aggregator::classify_skips):
walks parent pointers from observedFinalizedslots, assigns
everyvoted_skipslot toDirectFinalize/Ancestry/
Indeterminate. The headline operator-facing metric
canonical-skip %is derived from this classification. EventKind::TriggeringParentReadyparser variant for the
event_handler::add_missing_parent_readylog line; counter on
OverallStats(intentionally unsurfaced, reserved for future
per-window analysis).- Standstill range tracking:
OverallStats.standstill_rangesbuilt
fromStandstillExtending/StandstillEndedevents, including
EOS-orphan close. Newtimeout_crashed_leaders_outside_standstill
counter computed inanalyzefilters TCLs that fired inside a
standstill window (where the per-slot timeout is stretched and the
leader did not actually misbehave). SlotViewRow.consensus_invertedflag rendered as↶in the
Slots-tabconsensuscolumn for rows where the cluster's
Finalizedcert arrived before the local node'sBlockevent
(cluster outran local replay). Plain-is now reserved for
genuinely missing data.theme::TRUE_FB_ELEVATED_PCTthreshold constant.ProduceWindowcorruption guards (oversized span> 16,
inverted rangeend < start), counted in
OverallStats.malformed_produce_window.MirrorSparklinewidget on the time-series tab for the paired
vote-skip / canonical-skip mirror chart (one rises from the
bottom, the other hangs from the top, shared x-axis).
Changed
- Slot status labels:
SKIPrenamed toVSKIP(we Voted skip,
no canonical evidence).CSKIPunchanged. TheV/Cprefix
convention makes both labels self-describing — both refer to
our vote; the prefix indicates whether canonical evidence
exists. - Slots-tab filter hotkeys rotated:
[v]= VSKIP only,[c]= CSKIP only,
[s]= SLOW,[x]= clear all filters,
[b]freed.SlotFilters::vskip_onlyreplacesskipped_only
with narrowed semantic; the skip-family pair (vskip_only,
canonical_skip_only) now uses OR semantics — pressing both
shows the union. - Slots-tab
consensuscolumn now distinguishes three states:
positive value in ms (normal),↶(cluster outran local
replay),-(missing data). - Slots-tab
Latency bandsreference panel adds aconsensus
threshold row (≤ 300 ms · 300–600 ms · > 600 ms). - Slots-tab KPI strip: leader-slot share metric moved from the
buried right-panel footer ontoslot statsline 2 next to the
lifecycle percentiles.p95value is now health-coloured at the
KPI site, replacing the previous footer row. - Slots-tab legend: static column-value reference (status, vote,
consensus glyph) moved to the bottom of the panel below the
[x] clear all filtersseparator. The filter section above is
now exclusively interactive toggles. - Slots-panel title shortened: drops the redundant total in
slots (N total | cursor M / N)→slots (cursor M / N). The
filtered variant retainsM of N. vote & cert totalswidget on Overview rewritten as three
horizontal columns (votes/certs/finalized) instead
of stacked text. Headings styled intitle_style.- Overview headline-health
fast-finalizerow dropped the
fast NN.NN% / slow NN.NN%breakdown (the numbers now live
in thevote & cert totalsfinalizedcolumn). Verdict mark
and text retained. - Alerts-tab
detailblock + list block + Overviewalerts
widget gainPadding::new(...)for breathing room from the
block border. - Alerts-tab
detailwidget adds explicit blank lines between
thelastandspanrows and between thefirst sample
label and the sample body. - Leader-timeouts trend chart uses a packing-search algorithm
to pick(bar_width, bar_count)that fills the panel width.
bar_widthcapped at 6 columns so very wide terminals do not
collapse the chart to a handful of thick bars. - Header
crashed leadersmetric on Overview adopts the
standstill-aware count when standstill ranges exist (raw count
appears on a continuation line); when no standstill ranges
exist, the single-line display is unchanged. - Canonical-skip lower-bound marker (
≥) now applied on the
Slots-tab KPI strip whenindeterminate_skips > 0, matching
the header, overview, windows, and--textrunner surfaces. stake sharelabel removed. Renamedleader-slot share
(window-relative, not on-chain stake — the previous label
overstated what the tool measures).Severity::from_usthresholds (1.5 s / 3.0 s) documented as
provisional pending empirical calibration across multi-day
log archives.- True-FB cutoff
0.5%consolidated into
theme::TRUE_FB_ELEVATED_PCTand consumed at both render
sites (was a duplicated literal). - True-FB percentage formatted as
{:.2}%(was{:.3}%— no
false precision on an integer-count ratio).
Fixed
- Slots-tab canonical-skip percentage was missing the
≥
lower-bound marker even when indeterminate skips existed.
Same number now reads consistently across all surfaces. - Seven previously-undocumented
OverallStatsfields received
reserved-for-future docstrings explicitly marking them as
intentionally unsurfaced (matching the existing
parent_ready_recoveriesprecedent):malformed_produce_window,
standstill_extending_events,standstill_ended_events,
no_epoch_info_for_slot,updating_epoch_metadata,
evicting_epoch_metadata,invalid_cluster_slots_update.
Internal
- Test count: 187 → 201 across the workspace.
aggregator/tests.rs(989 LOC) split into 6 sub-modules under
aggregator/tests/(ingest,standstill,classify_skips,
log_patterns,produce_window,analyze_alerts).tui/app.rsinlinemod tests(309 LOC) extracted to
tui/app_tests.rsvia#[path]declaration; production
module drops to 753 LOC, under the 800 strong-warn threshold.OverallStatsunique-slot count docstrings clarified —
finalized_slot_count + skipped_slot_count + pending_slot_count
is overlap, not partition (a canonical-skip slot is counted in
bothfinalized_slot_countandskipped_slot_count).- rustfmt + strict clippy fixes for the workspace:
explicit_iter_loop,doc_overindented_list_items,
type_complexity,items_after_statements,derivable_impls.
v0.1.1
Added
- Slots tab: new
mfilter for rows where the validator voted
both Notarize AND Skip on the same slot. Mixed-vote rows now
display asN+S/N+F+S(previously the Skip was silently
dropped from the vote-pattern column). - Memory ceilings on aggregator state:
LogIssueGroup.timestamps
capped at 1,000,000 entries with overflow counter;
ProduceWindowevents rejected whenend - start > 16(4×
the AlpenglowNUM_CONSECUTIVE_LEADER_SLOTS = 4invariant)
withmalformed_produce_windowcounter. Defence against
memory-exhaustion on pathological / corrupted log lines.
Changed
- Yank target moved from
/tmp/abracadabra-yank-N.txtto
$XDG_RUNTIME_DIR/abracadabra/abracadabra-yank-<pid>-<n>.txt
(fallback$HOME/.cache/abracadabra/yank/...). Per-process
pid in the filename prevents cross-session collisions in the
persistent fallback dir. - Windows / Overview / Slots / Leader-timeouts tabs share a
single precomputedLatencySnapshot; eliminates per-frame
O(n log n) recomputation on the render path. - Alerts list viewport follows the
j/kcursor (ListState).
Fixed
- Alerts list orders globally by
(severity desc, at asc).
The TUI title's "CRIT first, by count" promise is now
load-bearing across inline + LogPattern alerts. SlotRecord::delta_usreturnsNonefor inverted intervals
(was returning negative microseconds and polluting percentile
calculations downstream).Severity::from_usno longer mis-classifies negative input
as Normal via integer-division truncation.- Window slot-duration percentiles count only strictly adjacent
(n, n+1)pairs; gap-separated pairs no longer inflate p95. percentile()clampspto[0.0, 1.0](NaN → 0.0).fast_slow_pct()partitions exactly to 100.- Per-hour rate displays use actual log duration (was clamped
to a 1-hour minimum, deflating sub-hour rates up to ~60×). - Scroll keys on non-list tabs no longer clobber the Slots
cursor position. - Single-metric sparkline cards fill the full panel width.
- StackedBars paints
Color::Reseton every cell branch. - Stale
/tmpreferences andtui/mod.rsmodule doc updated.
Security
- Panic hook restores terminal state before propagating;
mid-render panic no longer leaves a wedged terminal. - Yank file write uses
O_CREAT | O_EXCL | O_NOFOLLOW;
symlink attacks on the previous/tmptarget neutralised. - TUI sanitises log-derived spans before rendering (strips
ESC, DEL, replaces other C0). Crafted log lines cannot
cursor-manipulate the terminal. - Parser sanitises
Parsed::Issuebodies at construction:
strips ESC/DEL/LF/CR, replaces other C0 with?, preserves
multi-byte UTF-8. CSI sequences cannot reach stdout via
--textmode. - Hash captures length-bounded
{32,48}on every parse path
(regex andstrip_prefix). Slot-digit captures bounded{1,20}. ProduceWindow { start, end }clamped atend - start ≤ 16;
malformed log lines withend = u64::MAXcannot trigger
exabyte-scale allocation.- Removed unused
AlertKind::LeaderTimeoutCrashedvariant. cargo audit: 0 vulnerabilities, 0 warnings (95 transitive
deps, advisory DB 2026-05-23).- Still 0
unsafeblocks in project sources.
Internal
- Test count 84 → 187 across the workspace.
aggregator/mod.rstest module extracted to sibling
aggregator/tests.rs(mod.rs 845 → 484 LOC).
v0.1.0
Added
- Initial public release.
- Streaming line parser for Solana Alpenglow validator logs
(tested againstag-v0.3.2; Alpenglow is in active development
upstream and log formats may shift — file an issue if parsing
breaks on a newer cluster version). Recognises 16 votor events,
root-utils events,
bank events,solana_core::cluster_slots_serviceknown-issue
lines, andsolana_metrics::metricsdatapoints. - Per-
(severity, module)aggregation of unparsed WARN/ERROR log
lines intoLogPatternalerts with timestamp tracking. - TUI dashboard with six tabs:
- Overview — file metadata, headline health verdicts (3-tier
bands for fast-finalize %, FIN %, vote skip rate), vote/cert
totals, latency-stage breakdown, leader-timeout summary. - Time series — 10-card grid bucketed by configurable
interval. Stacked-bar fast-vs-slow finalize chart with sub-cell
block-character precision; single-series sparklines (baseline-
subtracted) for the remaining 9 metrics. - Windows — rolling-window comparison across 24h / 12h / 6h /
3h / 1h windows. - Slots — KPI strip + scrollable filterable per-slot table.
Seven keyboard-toggleable filter dimensions covering status,
finalization path, leader role, and event tags. - Leader timeouts —
TimeoutCrashedLeaderanalysis with
severity bands, distribution histogram, per-bucket trend bars,
incident list. - Alerts — severity rollup, grouped alert list with per-
pattern timestamp sparklines, copy-to-file viaykey.
- Overview — file metadata, headline health verdicts (3-tier
- CLI flags
--text(non-interactive summary),--bucket <DUR>
(time-series bucket size, 1m..=24h),--version. - GitHub Actions CI (
cargo fmt --check, build, test, strict
clippy --all-targets,xtask lint-prod) onubuntu-22.04. - Tag-triggered binary release workflow building
x86_64-unknown-linux-gnu(glibc 2.35+ compatible) and
x86_64-unknown-linux-musl(static, distro-agnostic), each with
SHA-256 checksum and auto-generated release notes. - Dual-licensed under MIT OR Apache-2.0.
Security
cargo audit: 0 vulnerabilities, 0 warnings against 95
transitive dependencies (advisory DB 2026-05-23).- 0
unsafeblocks in project sources.