Skip to content

Optimize Constellation and Journal with incremental history - #21

Open
aakashH242 wants to merge 9 commits into
mainfrom
feat/ui-optimizations
Open

aakashH242 wants to merge 9 commits into
mainfrom
feat/ui-optimizations

Conversation

@aakashH242

@aakashH242 aakashH242 commented Nov 12, 2026 •

Copy link
Copy Markdown
Member

Large Journal histories used to mount every event and group header, and Constellation used to repeat layout and animation work across the full scene. The Journal now mounts a measured window around the viewport. Constellation keeps its existing scene nodes and limits repeated work to the state and geometry that changed.

Journal

The complete event, group and date order stays in a logical model. A cumulative height index finds rows around the viewport, with up to 400 px of overscan on each side. A shared ResizeObserver measures mounted rows and preserves the visible event's offset when heights change. The same anchor is used when history is replaced or extended.

Group headers remain native sticky disclosures. Collapsing a group retains its events in the model, and reveal can expand a collapsed group and scroll to an unmounted row. A focused row stays mounted across scroll and history changes, and Tab follows logical row order. The app cancels queued work before replacing the Journal or leaving the timeline. The virtualizer preserves event order and grouping.

On the 390 px Chromium fixture with 10,000 events in 5,000 dynamic groups, peak mounted event rows fell from 10,000 to 11 and headers from 5,000 to 6. Initial mount plus synchronous layout went from 1,070.60 to 31.60 ms median. On the 50,000-event single-group fixture, peak mounted event rows fell from 50,000 to 16, and median initial mount plus layout went from 3,500.50 to 95.90 ms. Scripted scrolling adds a small amount of mounting work per window. The 50,000-event jump step measured 1.70 ms median in Chromium. The virtualizer retains the loaded logical history in memory.

Constellation

  • Zoom updates prepared geometry in place, preserving node identity, focus, and motion.
  • Ambient frames skip unchanged label layout, measurements, and navigation writes. Overview skips timeline label placement and keeps hidden labels out of scroll bounds.
  • A horizontal index bounds label and spark work to the viewport plus 128 px of overscan. It keeps crossing segments and animation phase, preserves nearest-person ties, and pins selected or focused labels where needed.

The viewport change matched 483 Chromium baseline checkpoints. At 10,000 entries, four manual-scroll frames went from 40,000 label visits to 26 and from 239,616 spark/trail coordinate writes to 180. The existing Constellation nodes stay mounted.

Verification

  • 629 local tests including 15 offline Chromium tests, focused Node suites, 80 headless tests, Ruff, package build and wheel asset checks.
  • Journal browser coverage includes large jumps, variable-height corrections, native disclosure and sticky headers, keyboard navigation, live prepend/append, and parent replacement. An independent model oracle matched 556,110 height operations up to 50,000 events.
  • Gauntlet v1.2.0 PASS_WITHIN_SCOPE after three clean re-attacks covering model correctness, accessibility and ownership, and paired performance evidence. Browser checks used synthetic data and Chromium. Timings measure synchronous work and exclude raster/compositing. All four CI jobs passed.

Incremental history and exact totals

Issue #25 follows the timeline from SQLite to both layouts. The previous client awaited every page before rendering. XFlog now accepts one page of up to 50 records and renders it with the exact totals. Journal near-end scrolling, Constellation older navigation and the Load older activity control request one continuation at a time. Failed requests retain the accepted cursor and loaded rows for explicit retry. The scrubber loads on commit.

A shared request owner cancels stale requests on scope change or unmount, coalesces near-end callbacks, and lets older navigation preempt a live read. Live catch-up uses per-source insertion bounds, accepts up to three pages per poll, and exposes pending work while keeping the accepted older cursor. A selected event can refresh from local data without walking older pages. Profile resolution remains opt-in on the first ungrouped page. Aggregates and continuation pages use local data.

The first /api/timeline page returns events, next, and aggregate. The aggregate contains full-scope event and distinct-actor totals, counts by kind, an active-lens bucket breakdown, an opaque scope, the revision, the oldest matching receipt and the profile revision. Page membership and aggregate share a read-only SQLite WAL snapshot. Keyset continuations bind the account, subject, filter, receipt cutoff, lens, enabled settings and both source watermarks. Equal receipts retain source and sequence priority. Mutable profile and post fields reflect each request's local snapshot. The offline demo supports the same paging model and holds its initial synthetic clock across continuations. The Activity HUD endpoint keeps its existing contract.

Activity and kind counts use SQL grouping. Account size, account age and verification stream the recorded attributes through the registered classifiers. Profile location uses the registered normalization and latest matching bucket descriptor. Previous encounters uses one chronological SQLite window over the saved account log, including history outside the current range and disabled event kinds. Both layouts distinguish loaded records from saved totals. Journal can show an aggregate-only bucket as a summary and promote it to a disclosure as pages arrive. Constellation shows a summary of groups with no loaded records and positions stars only for fetched events.

The server changes are in app.py, the audience-group registry, and history/{timeline,timeline_queries,timeline_cursor,timeline_aggregate,demo}.py. history/store.py and interactions/store.py add four indexes for account receipt/sequence, actor receipt/sequence and interaction account/sequence seeks. No table migration or aggregate cache. The browser changes cover assets/{app,live-history,audience-groups,journal,journal-model,constellation,constellation-layout,constellation-colors,format}.js, the shell and timeline styles. The README documents paging. Tests cover the mixed SQL oracle and browser/Node histories and layouts.

Measurements

Paired Chromium 156 runs used the actual shell and a mocked local API, 50 rows per page, one warmup and three samples on Linux with an i7-12700F. First useful DOM includes requests, JSON parsing and rendering and excludes SQLite. Heap is post-GC V8 used heap above baseline. Bytes are compact uncompressed history JSON bodies.

50,000-event fixture First useful DOM median / p95 Requests Transferred Retained V8 heap
Grouped Journal 2,339.4 / 2,490.8 ms → 7.3 / 7.7 ms 1,000 → 1 16,805,287 → 17,371 B 24.67 → 0.10 MB
Ungrouped Constellation 8,599.9 / 8,800.4 ms → 14.5 / 15.5 ms 1,000 → 1 16,538,140 → 16,642 B 62.21 → 0.26 MB

At 1,000 and 10,000 records the paired fixtures also dropped from 20 and 200 requests to one. All candidate fixtures retained 50 rows at first render. Sampled peak V8 heap at 50,000 went from 43.08 to 1.09 MB for Journal and 73.70 to 1.52 MB for Constellation. Browser-native memory and FPS were outside the measurement.

Separate Python 3.12.13 and SQLite 3.53.1 runs used one warmup and nine measured samples for the candidate. At 1k/10k/50k, mixed Activity first-page medians including the aggregate were 4.55/11.63/43.14 ms, older pages 3.32/3.26/2.60 ms, and 20-record live deltas 2.95/10.42/42.14 ms. The initial bodies were 33,986/34,104/34,221 bytes including 759/773/786 bytes of metadata. The old local eager drain at 1k/10k took 82.94 ms/11.99 s median and issued 60/600 data reads, versus four reads for the new first page. The old 50k run was censored after three minutes. Query plans verified bounded per-source candidates before enrichment and indexed delta sequence ranges. The 10k prior-encounter aggregate initially exposed a 1.86 s SQLite nested-scan plan. Removing the join and applying the scope after the chronology window brought the first page to 58.89 ms median.

A real offline SQLite walkthrough paged 50 → 100 → 150 loaded rows in the two layouts while keeping the 240-event and 47-person totals. The 50-row ungrouped Journal append probe at 50k retained events measured 67.65 → 0.65 ms median for synchronous controller work and retained mounted rows. Grouped Journal rebuilds the logical index while preserving compatible measurements and controls. Constellation rebuilds when membership changes because star positions, selected bindings and connection endpoints share a geometry generation. It restores focus, selection, zoom and anchor. The rebuild measured roughly 0.93 s at 10k retained stars, so very large manually loaded scenes remain costly.

Issue #25 verification

  • uv run --no-sync pytest -q -ra passed 653 tests, including 23 Chromium lifecycle cases. The held-response tests cover one-page initialization, retry, repeated cursors, layout switches, stale response delivery, focus/anchor retention, scrubber commit and a bounded live burst concurrent with older navigation.
  • node tests/live_history_check.mjs src/xflog/assets and node tests/constellation_selection_check.mjs src/xflog/assets passed. node tests/journal_check.mjs src/xflog/assets "$catalog" and node tests/constellation_check.mjs src/xflog/assets "$catalog" passed with the all-enabled audience catalog from xflog.audience_groups.catalog.
  • uv run --no-sync ruff check src tests .github/scripts, uv run --no-sync ruff format --check src tests .github/scripts, git diff --check, bash -n install.sh, uv lock --check and uv build passed. In a Gradio-free environment, uv run --no-sync pytest -q tests/test_runtime.py tests/test_identity.py tests/test_cli.py passed 80 tests.
  • All four GitHub CI jobs passed on 13e6a51.
  • Gauntlet v1.2.0 completed three distinct clean re-attacks on the delivery snapshot. The passes covered query scope and snapshot membership, request and DOM lifecycle, and performance/oracle sensitivity. Reproduced and fixed the SQL planner regression, near-end auto-retry, double request from scrubber input and commit, and receipt collation mismatch. Reviewed on local dummy data without live X credentials. The follow-up re-attacks covered exact correction membership against 30 independent mixed-scope fixtures, manual paging between live batches, an actual SQLite/Chromium regression with filtered-out predecessors, and indexed correction preflight.

Exact derived counts still scan local data and location metadata scales with the number of occupied buckets. The 50k synthetic location and previous-encounter first pages took 420 and 271 ms median. Loaded history grows as the user pages. In Previous encounters, a backdated arrival can update existing group assignments, including when the arrival is outside the current filters. The live keyset includes update-only corrections. They replace already-loaded records and share the same 50-row cap. Older pages retain their original membership and use the accepted aggregate revision for encounter classification. Ordinary arrivals bypass correction discovery after indexed receipt and sequence checks. In a 50,000-event local SQLite fixture, a 175-record chronological live burst used four pages with a 277.18 ms median first page including the aggregate and 8.39 ms total for the remaining pages. A 200-record backfill also corrected 400 older rows, took 12 pages, and measured 384.14 ms median for the full local batch. Both used seven samples after warmup. These backend measurements exclude HTTP and browser work. A large backfill can require several catch-up batches while the status shows that updates are pending.

Closes #20
Closes #22
Closes #23
Closes #24
Closes #25

@aakashH242

Copy link
Copy Markdown
Member Author

@codex review

@aakashH242

Copy link
Copy Markdown
Member Author

@codex review

@aakashH242

Copy link
Copy Markdown
Member Author

@codex review

@aakashH242 aakashH242 changed the title Update Constellation geometry in place on zoom Reuse Constellation layout across zoom and ambient frames Nov 12, 2026
@aakashH242 aakashH242 changed the title Reuse Constellation layout across zoom and ambient frames Optimize Constellation zoom, ambient frames, and viewport rendering Nov 13, 2026
@aakashH242 aakashH242 changed the title Optimize Constellation zoom, ambient frames, and viewport rendering Optimize Constellation rendering and virtualize the Journal Nov 13, 2026
@aakashH242 aakashH242 changed the title Optimize Constellation rendering and virtualize the Journal Optimize Constellation and Journal with incremental history Nov 13, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant