- CLAUDE.md: triggers now query coordination API (/api/coord/status,
/api/coord/components, /api/coord/messages) instead of reading
PROJECT_STATE.md files
- COORDINATION_PROTOCOL.md: new doc covering locks, component states,
workflows, work items, and inter-session messages via ClaudeTools API
- guru-rmm/PROJECT_STATE.md: marked ARCHIVED, redirects to
COORDINATION_PROTOCOL.md for live state
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Frontend pass on the two embedded HTML templates in the FastAPI server. No
backend / Python logic changed; only template strings, CSS, and inline JS.
Index page: full CSS custom-property theme (light, #c39733 accent),
responsive viewport meta, search input with embedded SVG magnifier and
focus ring, control bar reorganised into divider-separated groups with
the browse-mode toggle rendered via :has() selector, hit cards with
hover-lift + arrow indicator and focus-visible outline, restyled Q/A
badges and score/topic chips, animated loading dots.
Episode page: sticky audio player and sticky aside (top: 130px,
max-height calc'd against viewport). New active-Q&A highlight builds a
sorted index of QA blocks at load time, computes each block's end as
the next block's start (capped at +180s), and on timeupdate/pause
toggles .active on both the body QA block and its aside list item; a
"NOW PLAYING" pill is revealed on .qa.active. Intro-marker also gets
.active. Audio preload bumped from none to metadata so #qa-<id> deep
links can seek without a prior user gesture.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Make the radio archive Q&A pairs actually browseable end to end:
- /api/qa list endpoint (year, min_score, exclude_banter, topic_class,
pagination, sort by air_date or score). Returns the same column shape as
/api/search Q&A hits.
- /api/audio/{episode_id} streams the MP3 with HTTP Range support so the
browser <audio> can seek. 206 + Content-Range when ranged, 200 when
full-file. Returns 404 cleanly when episodes/ tree is absent (Jupiter).
- /episode/{id} HTML transcript view: chronological segments with clickable
timestamps, Q&A blocks spliced inline (anchor #qa-<id>), intros marked
inline, right-rail summary. Hash-anchor on load auto-seeks the audio.
- New question_excerpt / answer_excerpt fields on /api/search Q&A hits and
on /api/qa items: trim leading run-on chatter, take ~300 chars, end on a
sentence boundary or word boundary with ellipsis.
- Index UI: each Q&A hit now links to /episode/{id}#qa-{qa_id}; new
"Browse all Q&A" toggle (year selector, sort, append-load 50 per page,
defaults to min_score=3); FTS snippet replaced with the plain excerpt
when available.
No new dependencies, no schema changes, no LLM calls. Uses
EPISODES_DIR env (default /data/episodes) — Jupiter compose still only
mounts /data so audio degrades gracefully to 404 there until episodes
are uploaded.
Backend min_score/exclude_banter wired through to HTML index. Adds
score badges (1-5 red->green), topic_class pills, dim styling on
banter rows. Live on http://172.16.3.20:8765/. Synced to portable
repo. pscp ENOSPC quirk worked around by plink-stdin streaming.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds quality-filter controls to the search UI: a "min score" select
(any/2+/3+/4+/5) and a "hide banter" checkbox. Q/A hits gain a small
color-coded usefulness badge (1-5, red->green) and a topic_class tag
(computer-help, banter, off-topic, promo). Low-score and banter rows
render dimmed by default so they're visible but de-emphasized.
Defaults to "any" + banter visible to preserve existing search habits.
Mike toggles up when he wants quality. URL-encoded params built via
URLSearchParams so empty values don't leak into requests.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Streams the read-only archive.db over the same Tailscale-routed port
as the search service. Companion to azcomputerguru/radio-archive-portable
which curl-fetches from this endpoint and runs locally on the laptop.
Disclosure equivalent to /api/search (which already exposes every
transcript), so no auth added. Deployed to Jupiter; verified GET
returns 60 MB SQLite blob with all 1,405 classifier rows intact.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
3.5h run on qwen3:14b processed 1,405/1,407 Q/A pairs (2 failed,
will retry on next invocation). 37% scored 4-5 (useful), 41%
scored 1-2 (banter/promo/off-topic). API filter ready; Jupiter
redeploy pending Mike's manual review.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds an Ollama-based content quality classifier and exposes the
results via the search API. 1,407 existing Q/A pairs were scored
in 3.5h via qwen3:14b (1,405 succeeded, 2 failed).
Distribution: 37% scored 4-5 (useful), 41% scored 1-2 (banter/promo/
off-topic). 43% flagged as banter overall. Default-on filtering at
search time will hide ~half of the noise without losing any real
listener questions.
Files:
- new classify_qa_quality.py: walks qa_pairs, calls Ollama qwen3:14b
per row, writes usefulness_score/topic_class/is_banter back to DB.
Idempotent (--rebuild to reprocess), --smoke for sample check, --limit
for partial runs. Detached run handles 1407 rows in ~3.5h on a 4090.
- server/main.py: /api/search accepts min_score (0-5) and exclude_banter
query params. NULL scores treat as "include" so unprocessed rows still
appear. Episode detail endpoint includes the new fields in qa results.
Schema migration in import_to_sqlite.py was made by the same agent run
(visible on the live archive.db: usefulness_score / topic_class /
is_banter columns now exist on qa_pairs).
Local archive.db updated; Jupiter container has NOT been redeployed
yet — that is a separate manual step.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Added cross-reference from FEATURE_ROADMAP.md to UI_GAPS.md tracking document.
Clarifies that features may be backend-complete but UI-incomplete.
Submodule commit: f76051a
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Updated DESIGN.md with two fundamental principles:
1. Holistic Feature Development - every feature needs full stack (backend, API, UI, docs)
2. AI-Optional Operation - product works without AI agents; AI features are enhancements
Submodule commit: e490307
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Updated GuruRMM roadmap with two major features:
- Network Discovery Node (P2): site-level device discovery and mapping
- Local Collection Node (P2): reduce WAN traffic by local aggregation
Submodule commit: db7d074
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Append to 2026-04-28-session.md covering the FastAPI/SQLite container
deploy: build + ship + verify, plus credentials, paths, and re-deploy
procedures for both DB updates and source updates.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Read-only HTTP layer over archive.db. Endpoints: /api/stats,
/api/episodes, /api/episodes/{id}, /api/episodes/{id}/transcript,
/api/search (FTS5 over segments + qa_pairs, bm25-ranked, snippets),
/api/callers. Single-file HTML index with debounced search UI.
Deployed: Jupiter (Unraid Docker), bound to 172.16.3.20:8765, LAN only.
Container path: /mnt/user/appdata/radio-archive/{app,data}.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Execution-only follow-on to 2026-04-27. Both batch passes done (519+53,
0 errors), import_to_sqlite.py run incrementally to bring archive.db
to final state. Next step: Jupiter Docker container deploy.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
QAPair gets caller_name and caller_role fields populated by a new
attach_caller_names(pairs, transcript_segments) helper. For each pair,
finds the active opening intro at the question_start time (8s forward
tolerance, no backward limit — a caller's call can run for 10+ minutes
and the intro happens once at the start) and attaches the speaker name.
Validation on 9-episode test set:
19/19 Q&A pairs (100%) now have caller names attached.
Examples of corrections from oracle attribution:
2018-s10e18 @ 73:36 Christopher (was misattributed to "Tara")
2015-s7e19 @ 35:45 William (was misattributed to "Tara")
2010-05-08-hr1 Jackie x3, Bruce
2012-03-10-hr1 Adam x2
2016-s8e43 John, Doug
2017-s9e30 Tom, Denise x3, Charlie
speaker_oracle.py: adds speaker_at(time, intros) helper used both by the
existing resolve_speakers() and the new caller-name attachment. Also
adds the "let's fit/bring/put X in/on" intro pattern variant (caught
Charlie at 70:21 in 2017-s9e30 that "talk to X" missed).
download_full_archive.py: SSH keepalive every 30s + per-file retry-on-
failure (up to 3 attempts with reconnect). Earlier run hung on a dead
connection at file 109 of 589 with no recovery; restarted run is now
running at ~10 MB/s vs ~2-3 MB/s before.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>