Working log for the four-feature Ringside/Ringer upgrade (2026-07-04). Written while two
production swarms (aicred-docs-repair, aicred-site-fixes) were live — all edits happened in
an isolated git worktree, never in ~/fleet/swarm directly (its dashboard/dashboard.html is
re-read from disk on every live-dashboard HTTP request by the running orchestrators, so editing
it in place would have visibly corrupted Jon's screen recording).
- Repo:
~/fleet/swarm(git,mainbranch, Jon works here on the Mac alongside Lex; push feature branches + PRs). - Ringer (
ringer.py, stdlib-only Python 3.11+): orchestrator + per-run state writer (~/.ringer/runs/<run_id>.json, flushed every 1s) + optional embedded HTTP dashboard (Dashboardclass, servesdashboard/dashboard.html+/state.json, live-read from disk every request — no caching). - Ringside (
hud/): a Tauri v2 app (Rust backendhud/src/main.rs+ vanilla-JS/HTML frontend, NOT SwiftUI/React).productName: Ringside,identifier: com.jonedwards.ringside.- Rust side polls
~/.ringer/runs/*.jsonevery 1s (start_state_poller), computes live/finished/died state + sort order, emits aringer-runsTauri event with the full JSON array (fields are passed through mostly verbatim — new task/run JSON fields show up in the frontend for free, no Rust changes needed unless you need new file I/O or window behavior). - Frontend:
hud/dist/index.html(webview HTML+CSS+JS) is a generated copy of the repo's canonicaldashboard/dashboard.html— synced byhud/scripts/sync-dist.shat build time. Always editdashboard/dashboard.html, neverhud/dist/index.htmldirectly, then run the sync script (or lettauri build'sbeforeBuildCommanddo it). hud/frontend/hud.js(synced tohud/dist/hud.js) is the Tauri-only bridge: builds the custom title bar, window drag, close button, HUD ticker text. It listens forringer-runsand also calls the sharedupdate()function defined inline indashboard.html.dashboard.html's inline script also doespollLocalState()— afetch('/state.json')poll. That's for when the SAME html is served by ringer.py's own embedded HTTP dashboard (browser tab, no Tauri). Under Tauri it 404s harmlessly (try/catch swallows it) since there's no HTTP server backing the webview.- Window: 360x420 default, transparent, decorations:false, alwaysOnTop, resizable (min
280x220).
toggle_collapseTauri command shrinks it to a 34px title-bar-only strip. - Capabilities:
hud/capabilities/default.jsonlists allowed commands (core:default+ window permissions). New#[tauri::command]fns must be added to bothinvoke_handler!inmain.rsAND (if not covered bycore:default) get a permission there.
- Rust side polls
- Canonical repo:
~/fleet/swarm(do not edit while swarms are live). - Working worktree:
~/fleet/swarm-ringside-upgrades, branchfeat/ringside-upgrades. - Local only — never pushed, never touched
main.
File: ~/fleet/swarm-ringside-upgrades/ringer.py
- Added
ArtifactConfigdataclass +load_artifact_config(); new[artifact]config block (enabled,outtemplate,report_outtemplate,index_out) documented inconfig.sample.toml. Defaults write under~/.ringer/artifacts/. TaskRuntimegainedlast_check_output,last_check_returncode,last_check_timed_out— previously the verify/check output was logged to the eval sink and then thrown away; nothing else in the process remembered it, so neither a live page nor a final report could show why a task failed without greppingworker.log. Set inRingerRunner._run_taskright afterverify = await self.verifier.verify(...).StateWriternow takesmax_parallel+artifact: ArtifactConfig.snapshot()gained, per task:check(the check command),timeout_s,taskdir(absolute path, for report links),verdict(PASS/FAIL/TIMEOUT/ERROR — more specific thanstatus),check_returncode,check_output_tail(capped),log_tail_full(40 lines vs the existing 3-linelog_tail) — this is the data Ringside's detail view (feature 1) needed and didn't have.- Run-level snapshot gained
max_parallel,artifact_path,report_path,report_ready. flush()now also renders+atomically-writes the Tier 0 live status HTML (render_status_html) toartifact_pathon every 1s tick, best-effort (wrapped so a render bug can never fail the run) — this is the "live artifact" feature 2 needs; Ringside just has to display the file, not build the renderer.finish()now also renders the final report (render_final_report_html) toreport_pathonce, after the summary is built — this is feature 4. Self-contained single HTML file: full task table, per-task attempts/timings/verdict/check command/check output (fuller than the live page, capped ~4000 chars with a truncation note),file://links to each task'staskdir,worker.log, andreport.md/report.htmlif present. Reporter failures never gate run exit code (matches the plan's "pretty report failing shouldn't mark a green run red" rule).- Multi-run index:
render_artifact_index_html()+ best-effort write of~/.ringer/artifacts/index.htmlon every flush, scanningstate_dir/runs/*.json— gives one pane of glass across concurrent runs (tonight's exact scenario: 2 concurrent swarms). - New CLI flag
--no-artifact(bothrunanddemo), symmetric with--no-dashboard.
Added Feature 5: settings panel with persistent customization — accent/theme color, layout,
density (compact/comfortable), which columns/fields show, default view mode. Must persist across
Ringside restarts. Guidance: plain JSON file (not UserDefaults — this is Tauri, not Swift),
ideally at ~/.ringer/ringside-settings.json so the Tier 0 HTML artifact can read the same theme
later. Explicitly told to design this together with Feature 3 (view modes) since "default view"
becomes one persisted setting rather than a separate mechanism. Folded into priority after 4 and 1.
Settings schema (JSON, ~/.ringer/ringside-settings.json):
{
"version": 1,
"theme": {"accent": "#28d7ff"},
"density": "comfortable",
"columns": {"showTokens": true, "showActivity": true, "showChildren": true},
"layout": {"defaultView": "grid", "hiddenRunIds": []}
}hud/src/main.rs — 4 new #[tauri::command] fns, registered in invoke_handler! alongside
the existing hide_window/toggle_collapse (no capabilities/default.json change needed —
custom app-binary commands aren't gated by the ACL system the way plugin commands are; verified
empirically since hide_window/toggle_collapse already worked with only core:default +
window permissions listed):
resize_main_window(width, height)— feature 3 window-size presets (Medium/Large), separate fromtoggle_collapse's mini-strip bookkeeping so the two don't fight.read_artifact_html(path)— feature 2; reads a ringer.py-rendered HTML artifact off disk, canonicalized and restricted to be under the resolved ringerstate_dir(path-traversal safe). Frontend injects the result into an<iframe srcdoc>.load_settings()/save_settings(settings)— feature 5; plain JSON file at<state_dir>/ringside-settings.json(atomic tmp+rename write), not UserDefaults, so a future Tier 0 HTML artifact could read the same theme.
dashboard/dashboard.html (canonical source — hud/dist/index.html and hud/build.rs both
regenerate from this file; never edit dist/index.html directly):
- Feature 1: click a task card to expand a detail panel (engine, attempts+verdict, check
command, taskdir, full spec, check output, 40-line live worker.log tail) built from the new
ringer.py state fields. Per-task progress bar (
elapsed_s / timeout_s), hidden when no timeout is known. - Feature 2: new "Artifact" view mode embeds the run's live Tier 0 status page via
read_artifact_html+iframe.srcdoc; degrades to a friendly empty-state message outside Tauri. - Feature 3: toolbar with Grid/Artifact toggle, Collapse-all (per-run), a substring filter over run+task names ("show only some agents"), and Mini/Medium/Large window-size buttons.
- Feature 5: gear icon opens a settings panel — accent color (drives a new
--accentCSS variable; only the neutral "live" highlight is themeable, pass/fail keep fixed semantic colors), density (comfortable/compact), default view mode (feeds directly into feature 3's view-mode switch, not a separate mechanism), and per-column show/hide (activity line, child count, run token totals). Persisted via the new Tauri commands, debounced ~350ms; gracefully in-memory-only outside Tauri.
cd ~/fleet/swarm-ringside-upgrades
/opt/homebrew/bin/python3.11 ringer.py demo --no-dashboard --no-artifact # sanity: unchanged behavior
/opt/homebrew/bin/python3.11 ringer.py demo --no-dashboard # exercise new artifact code path
# artifacts land in ~/.ringer/artifacts/ (shared dir, but distinctly named per run_id — does not
# collide with or touch the two production runs' state files or dashboards)Rust: the Homebrew cargo/rustc on PATH (/opt/homebrew/bin, comes before ~/.cargo/bin) is
pinned to 1.87.0 and can't build current deps (darling/time/image need 1.88+). Use the
rustup-managed toolchain explicitly:
cd ~/fleet/swarm-ringside-upgrades/hud
PATH="$HOME/.cargo/bin:$PATH" cargo check # verified clean, 1.92.0Tauri build (do NOT run tauri build in a way that overwrites /Applications/Ringside.app —
stage output only):
cd ~/fleet/swarm-ringside-upgrades/hud
bash scripts/sync-dist.sh # regenerate dist/ from dashboard.html + frontend/hud.js
# tauri build/dev commands, if run, must not touch /Applications/Ringside.appringer.py(Python 3.11,/opt/homebrew/bin/python3.11): syntax check, a realdemo --no-dashboardrun (3 real codex tasks, all pass) producing a correct live-status HTML, final-report HTML, and multi-run index (the index correctly picked up the two live production runs read-only, proving the "one pane of glass across concurrent swarms" goal). Also unit-testedrender_status_html/render_final_report_html/render_artifact_index_htmldirectly with synthetic fail/retry/long-spec-truncation state to confirm the fail-block, "(retried)" marker, and check-output rendering. All test artifacts cleaned up from~/.ringer/runs/and~/.ringer/artifacts/afterward; the two production run state files were never touched (verified:finished:true, correct pass counts, all after the fact — they completed naturally, unrelated to this work).- Rust:
cargo checkclean (via rustup's 1.92.0 toolchain) after both the command additions and the dashboard.html changes (whichbuild.rsalso re-syncs from source). - Frontend: JS syntax (
node --check) and HTML parse both clean. Live smoke test via chrome-devtools MCP: servedhud/dist/index.htmlstatically with a syntheticstate.json(three tasks: running/pass/fail) and drove it in real Chrome —- task-card click expand/collapse (both a passing and the failing task, verifying check output
- live-tail rendering and the "2 (FAIL)" attempts/verdict line),
- progress bars rendered and color-matched to status,
- Collapse-all toggled the whole run's task grid off/on,
- agent filter correctly hid non-matching tasks (verified via direct DOM inspection — the MCP
fill/clicktools don't always dispatch a realinput/clickevent on native form controls; confirmed by dispatching the event manually and readingtask.hiddenback), - settings panel opened, "Activity line" checkbox correctly hid the activity row across all task cards, density/accent controls present and wired,
- Artifact view mode correctly hid the grid and showed the designed fallback message ("could not read the live artifact (Ringside desktop app only)") since no Tauri bridge exists in a plain browser — proving the code path is safe outside Ringside,
- zero new console errors through all of the above (one pre-existing unrelated 404, likely favicon).
- CSS bug found+fixed during this pass:
.toolbar-group[hidden]needed an explicit rule — the plain[hidden]UA-stylesheet rule was losing to the author.toolbar-group{display:flex}rule at equal specificity (author origin always wins over UA origin), so the non-Tauri window-size buttons weren't actually hiding. Fixed and reverified.
- task-card click expand/collapse (both a passing and the failing task, verifying check output
- Not tested (needs Jon or a follow-up session): the three Tauri-specific invoke commands
(
resize_main_window,read_artifact_html,load_settings/save_settings) were verified by code review only — I did not launch a live Ringside/Tauri GUI instance during this session because Jon was actively screen-recording the installed/Applications/Ringside.appand a second always-on-top Ringside-style window appearing mid-recording would have been disruptive. All three commands compile, are registered, and their JSinvoke()argument names match the Rust parameter names exactly (Tauri does no case conversion needed here since all args are already single lowercase words). Recommend a quickcargo tauri dev(or a staged debug build, never overwriting the installed app) after the recording session to confirm the real window-resize/read-artifact/settings-persist round trip.
- Live Tauri runtime verification of the 3 new commands (see above) — lowest-risk next step, just needs a moment when Jon isn't mid-recording.
- Multi-run index page (
~/.ringer/artifacts/index.html) has no UI entry point in Ringside yet — it's generated and correct, but nothing in the app links to it. Small follow-up: a toolbar button or menu item that opens it (e.g. via the existingopenshell pattern Dashboard.start() already uses, or anotherread_artifact_html-style embed) — worth it if Jon wants deeper history across concurrent/past runs than the live/report pair gives per-run. - Per-run manual collapse (vs. only "collapse all") wasn't built — the agent filter already
covers "show only some agents" reasonably well, but a per-run collapse chevron in
run-headwould be a small, cheap addition if wanted. - Settings currently only cover accent/density/columns/default-view, per Jon's explicit list;
no window position/size persistence was added beyond the existing
tauri-plugin-window-state(already handles that generically). - Not done: hooking the final-report/live-artifact paths into a "Reveal in Finder" / "Open" action from Ringside chrome (e.g. right-click a run) — currently the only access path is the new Artifact view mode (iframe) for the live page; the final report has no Ringside UI at all yet (by design the safest/lowest-risk piece was ringer.py-side rendering; a Ringside "open final report" button is a small, obvious follow-up once the 3 new commands are live-verified).