feat(api): /api/journal + /api/journal/stream (#323)#330
Merged
Conversation
Phase 1 of the journal panel rework (epic #322). Adds a unified backend surface that flattens the in-process EventBus and a new lemond log ring into one JournalEntry shape, so the dashboard journal panel can render both sources through a single component without per-source branching. New module hal0.journal.LemondLogRing mirrors EventBus's bounded ring + per-subscriber fan-out for lemond log lines, fed by a lifespan-owned background bridge that wraps LemonadeClient.stream_logs() with exponential-backoff reconnect so a lemond restart doesn't permanently silence the panel. GET /api/journal: backfill with source / level / q / since / limit filters; returns sorted merged page + next_since cursor. GET /api/journal/stream: SSE with 50-entry replay then live multiplex across both subscriber queues; 15s keep-alive frames + clean disconnect on client close. Both endpoints are open (no auth) per ADR-0012; same first-run rationale as /api/events. Closes #323 Refs #322 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
8 tasks
5 tasks
thinmintdev
added a commit
that referenced
this pull request
May 25, 2026
Phase 3 of epic #322 — replace two silent fallbacks in the dashboard footer with live backend wiring: * Journal pane streams /api/journal/stream (PR #330) instead of rendering HAL0_DATA.journal as a fallback. Source chip rebuilds the SSE with ?source=hal0|lemond|merged (debounced ~200ms); search box filters the in-memory ring client-side. Empty ring renders "No events yet" instead of mock prose like "loaded model 'qwen3.6-27b-mtp' via llamacpp:rocm". * Footer update chip reads useUpdateState() directly. The hardcoded "hal0 v0.2.2 available" literal is gone; chip composes the version string from the live `available` field and self-hides when there is no update (or current === available). The updateAvailable prop thread from main.jsx is dropped — Phase 2's UpdateBanner owns its own dismiss state and the chip is allowed to keep nagging until a new release lands. Other touched surfaces: * useLogs.ts hook rewritten against /api/journal* with source/level/q filter params + SSE reconnect-on-change + exponential backoff on EventSource error. JournalEntry replaces the loose LogEntry shape. * LogsView (extras.jsx) updated to consume the new envelope ({entries, next_since}) and pass server-side filter params. * HAL0_DATA.journal block deleted; mock.ts buildLogs replaced with buildJournal that returns an empty envelope (no synthetic copy). * mock.ts: window.__hal0UpdateStateOverride seam added so Playwright specs can drive forced-mock update state — same seam Phase 2's PR #329 adds (identical content; will trivially merge). * Two new e2e specs (10 tests): - footer-journal-pane-v3.spec.ts — pane closed cold load, SSE connects on expand, hal0 chip narrows source + client-side residue filter, search filters case-insensitively, empty ring renders "No events yet", deleted journal prose absent from bundle. - footer-update-chip-v3.spec.ts — chip hidden when no update or current === available, chip renders live `${available}` string, chrome.jsx no longer hardcodes "v0.2.2". Closes #325 Refs #322 Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced May 25, 2026
thinmintdev
added a commit
that referenced
this pull request
May 28, 2026
…lease-manifest (#389) Add docs/internal/v0.3-state.md as the canonical v0.3 ground-truth doc (repo HEAD, five-stream status with PR/issue citations, terminology canon, stale-memory list, open blockers, ADR index). Writers cite this to stop triangulating PLAN.md + auto-memory + GH issues per word. Add ADR-0015 (Draft, alpha.2 target) — hal0 is an MCP host platform. Generalises ADR-0013's per-agent allow-list pattern to third-party MCP *servers*: registry at /etc/hal0/mcp/servers/<name>.toml, systemd template hal0-mcp@<name>.service, slot-style lifecycle states, curated catalog at installer/manifests/mcp-catalog.toml. Closes the gap the hal0_mcp_host_platform auto-memory calls out and the design behind issue #224's placeholder install-from-URL UI. Add ADR-0017 (Accepted) — bell+inbox approval UX for destructive MCP calls. Documents the contract shipped via Epic #322 (PRs #321 #328 #329 #330 #332): every MCP tool is classified READ-ONLY or DESTRUCTIVE via MCP annotations, unclassified defaults to DESTRUCTIVE, no per-agent trust override, pending forever. Third-party MCPs per ADR-0015 inherit the contract. Refresh release-manifest.md against v0.2/v0.3 reality. The runtime is no longer v0.1.x toolbox containers — it's the Lemonade embeddable tarball + the FastFlowLM .deb. Add optional `lemonade` and `flm` manifest blocks mirroring the installer's LEMONADE_* / FLM_* pins. Mark `toolbox_images` as historical (retained for out-of-tree consumers). Switch CF Pages references to Vercel per the actual deploy path. Light fix in api-errors.md: the 401 / auth.required example was documented as enforced by ADR-0001's FastAPI auth layer, which ADR-0012 removed entirely in v0.3.0-alpha.1. Reframe as a shape contract for any future re-introduced auth and for the MCP identity middleware, not a live request path. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
thinmintdev
added a commit
that referenced
this pull request
May 28, 2026
…rough + gut installer auth section (#390) - docs/operate/lemonade.md (new, .md canonical): operator reference for the v0.2 Lemonade runtime — what it is, where state lives, the /v1/* proxy + dispatcher fallthrough (PRs #248/#277), slot ↔ Lemonade model mapping (PRs #281/#282), max_loaded_models = 8 LRU cap (PR #283), per-type LRU eviction per ADR-0008 (supersedes nuclear-evict ADR-0007), OFFLINE-on-eviction (PR #276), and the three known v0.3 caveats (Vulkan KV gauge missing, whisper RUNPATH workaround, GPU cleanup unload hang). - docs/dashboard/v3.md (new, .md canonical, new docs/dashboard/ dir): page-by-page tour of the v3 React dashboard shipped in v0.3.0-alpha.1 (PR #235). Covers the shell + Mock-badge convention, /dashboard (system overview after #356), /chat (real surface per #309/#314/#315/#351), /slots (sidebar mirror per #357 + #344 UX sweep), /models (#313/#319/#353), /mcp (#304/#300), /agents (Peers per #299), /memory (graph #297, throughput #308), Settings (no Auth tab post-ADR-0012), and the footer journal (Epic #322 — PRs #321/#328/#329/#330/#332). Mock-fallback issues linked via the dashboard-v3 label, not enumerated. - installer/README.md: gut ~95 lines of stale auth prose (Caddy, Bearer-token mint/use/revoke, first-run OTP claim wizard, HAL0_AUTH_ENABLED/HAL0_AUTH_DISABLED, password recovery, basic_auth upgrade path, the TLS recipe). Replace with one paragraph pointing at docs/operate/auth.mdx for the reverse-proxy recipe and docs/agents/identity.md for the X-hal0-Agent identity model. Auth was removed in v0.3.0-alpha.1 per ADR-0012; the README hadn't caught up. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Phase 1 of the journal panel rework (epic #322 / issue #323). Adds a backend surface that flattens the in-process
EventBusand a newLemondLogRinginto oneJournalEntryshape so the dashboard journal panel can render both sources through a single component. The lemond ring is fed by a lifespan-owned bridge task that wrapsLemonadeClient.stream_logs()with exponential-backoff reconnect (1s → 30s), so a lemond restart never permanently silences the panel.Endpoints
GET /api/journal— backfill withsource(hal0|lemond|merged|all),level(info|warn|error),q(case-insensitive substring onmsg),since,limit ≤ 500. Returns{ entries: JournalEntry[], next_since }.GET /api/journal/stream— SSE: 50-entry replay then live multiplex across both subscriber queues, 15s keep-alive frames, clean disconnect on client close.Both endpoints are open (no auth) per ADR-0012 — same first-run rationale as
/api/events.Files changed
src/hal0/journal/__init__.py— newLemondLogRing(deque(maxlen=500) + per-subscriberasyncio.Queuewith drop-oldest overflow) +start_lemond_bridgebackground-task helper.src/hal0/api/routes/journal.py—JournalEntryPydantic model + the two endpoints + the source-specific mappers.src/hal0/api/__init__.py— wires the router under/api/journal, constructs the ring onapp.state.lemond_log_ring, spawns the bridge task in the lifespan, cancels it cleanly on shutdown.tests/api/test_journal_routes.py— 10 tests: HTTP empty/shape/source/level/q/since cases + SSE handshake + replay + live-emit. The bridge is stubbed in tests so we never try to open a real WebSocket.Test plan
pytest tests/api/test_journal_routes.py -x→ 10 passed in 0.9spytest tests/api/ -x(full sweep) → 374 passed, 3 pre-existing skipped, no regressionsruff check src/ tests/→ All checks passedruff format --check src/ tests/→ 298 files already formattedCloses #323
Refs #322