Skip to content

Dashboard freeze + Gemini 0.39.0 JSONL support#21

Merged
channyzf6 merged 5 commits intomainfrom
gemini-jsonl-and-edit-freeze
Apr 23, 2026
Merged

Dashboard freeze + Gemini 0.39.0 JSONL support#21
channyzf6 merged 5 commits intomainfrom
gemini-jsonl-and-edit-freeze

Conversation

@channyzf6
Copy link
Copy Markdown
Owner

Summary

Two independent bugs surfaced by a user report. Bundled because they shipped together from the same investigation.

Phase A — dashboard edit-freeze

isEditingName() in the poll() guard wedged the entire dashboard whenever any group-name contenteditable held focus. A user clicking + New group (which auto-focuses the new name span) and walking away froze every card's pill across all hosts indefinitely.

Fix: move the gate to render()'s structural-rebuild branch only — the in-place pill-update fast-path keeps running during edits, so pills + "synced Xs ago" stay live; only group-header rebuilds defer until blur. Adds a global pointerdown blur-on-outside handler so abandoned focus auto-clears.

Phase B — Gemini 0.39.0 JSONL support

Per google-gemini/gemini-cli#23749, Gemini CLI 0.39.0 migrated from atomic .json full-file rewrites to append-only .jsonl streaming. Our GeminiAdapter only matched .json in _locate and used a single JSON.parse in scanActivity, so 0.39.0+ users silently got null pills — the adapter found nothing to read.

Fix: parseJsonl() walks the documented record types (header, MessageRecord, MetadataUpdateRecord $set, RewindRecord $rewindTo) and materializes the same ConversationRecord shape the existing _deriveSnapshot() consumes. scanActivity() dispatches by file extension; _locate() accepts both. registry.mjs probeGeminiDir got the same extension-filter fix so cross-host detection tie-breaks remain accurate.

Adds a node:test scaffold (zero new deps) with 7 fixture-driven tests: legacy regression, JSONL basic, $set merge, $rewindTo truncation, malformed-line skip, partial-trailing-line survival.

Test plan

  • Phase A live verification: open the dashboard, click + New group, walk away. DevTools → Network: /sessions polls continue every 2s. Pills stay live. Click outside the focused name span → focus auto-clears.
  • Phase B unit tests: node --test lib/host/gemini.test.mjs — 7 pass / 0 fail.
  • Phase B legacy regression (live): confirm existing Gemini 0.38.x sessions still show non-null activityState (legacy .json path).
  • Phase B 0.39.0+ smoke (live): confirm a Gemini 0.39.0+ session shows non-null activityState (new .jsonl path) — pending a 0.39.0 user to verify if the dev machine isn't on 0.39 yet.

isEditingName() in the poll() guard blocked all fetches indefinitely
whenever any .name contenteditable held focus. The intent was to
protect a focused element from being ripped out by a structural
rebuild, but the side effect was that every card's pill froze the
moment a user clicked + New group (which auto-focuses the new name
span) and walked away.

Move the gate from poll() entry to render()'s structural-rebuild
branch only. The fast-path updateCardInPlace loop still runs during
edits, so pills + 'synced Xs ago' keep updating; only group-header
rebuilds defer until blur. Add a global pointerdown blur-on-outside
handler so an abandoned focus auto-clears.
Per google-gemini/gemini-cli#23749, Gemini 0.39.0 migrated from atomic
.json full-file rewrites to append-only .jsonl streaming. Adapter only
matched .json in _locate and used a single JSON.parse in scanActivity,
so 0.39.0+ users silently got null pills.

Add parseJsonl() that replays the documented record types — header,
MessageRecord, MetadataUpdateRecord ($set), RewindRecord ($rewindTo)
— into the same ConversationRecord shape the legacy .json path
produces. _deriveSnapshot stays unchanged; it consumes either format
through one entry point. scanActivity dispatches on file extension.

Both formats coexist in the chats/ dir because Gemini preserves
history rather than migrating across the version bump.
Adds four edge-case tests covering \$set metadata merge, \$rewindTo
truncation, malformed-line skip-and-continue, and partial-trailing-
line survival. Brings the GeminiAdapter test count to 7.

Rewrites the file-header comment to describe both .json (Gemini <=0.38
atomic) and .jsonl (Gemini >=0.39 append-only, per PR #23749) format
paths, since the previous wording asserted Gemini 'is NOT an append-
only JSONL' which became false in 0.39. Also flips the _parsedCache
field comment from 'cached parsed JSON' to 'cached parsed
ConversationRecord' (format-neutral).
probeGeminiDir's mtime tie-break scan filtered chat files by .json
only, so a fresh Gemini ≥0.39 install (which only writes .jsonl) fell
back to the less accurate tmp-dir mtime when computing cross-host
detection ties against Claude/Codex. One-line filter widened to
match both extensions, mirroring the gemini.mjs adapter fix.

Also refreshes the activity-watch comment in index.mjs whose
'rewritten-JSON re-read for Gemini' phrasing became half-true after
the dual-format dispatch landed.
@channyzf6 channyzf6 merged commit b858643 into main Apr 23, 2026
@channyzf6 channyzf6 deleted the gemini-jsonl-and-edit-freeze branch April 23, 2026 19:13
channyzf6 added a commit that referenced this pull request Apr 26, 2026
#38)

gemini-cli 0.39.0 broke our installer's gemini registration with
"Not enough non-option arguments: got 1, need at least 2." Root
cause: gemini's mcp add subcommand uses yargs, which treats '--'
as the "end of options" marker and shoves the post-'--' content
into a SEPARATE bucket (argv['--']) -- not into the positional
args list. So <commandOrUrl> + [args...] never get populated.
Claude (Commander.js) and codex (custom parser) both treat '--'
as a command boundary and propagate post-'--' content as positional
args, so they keep working.

Fix: per-CLI dashSeparator flag in FLAG_PROFILES. claude/codex stay
on '--', gemini drops it and passes command + args inline as
positionals. Verified live with dummy registrations against all
three CLIs; only gemini's behavior changes.

Also updated docs/INSTALL.md gemini snippet to match.

This used to work in gemini-cli 0.38.x; 0.39.0 made the parser
stricter (likely yargs upgrade with populate-- enabled). Two
breaking changes from 0.39.0 in our codebase: this one and the
JSONL chat-log format change fixed in PR #21.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant