Skip to content

refactor(#276): extract DashboardSession — phase 3b#282

Merged
BorisTyshkevich merged 1 commit into
mainfrom
refactor/dashboard-session-276
Jul 17, 2026
Merged

refactor(#276): extract DashboardSession — phase 3b#282
BorisTyshkevich merged 1 commit into
mainfrom
refactor/dashboard-session-276

Conversation

@BorisTyshkevich

Copy link
Copy Markdown
Collaborator

What & why

Part of #276 — Phase 3b, completing Phase 3 with #281 (3a). PRs so far: #278 (0–1), #279 (2), #281 (3a).

DashboardSession (src/ui/dashboard/dashboard-session.ts, 36-test spec; 100/94.9/100/100) owns the dashboard execution runtime: the 6-way tile pool, wave generations reserved at wave creation (the race-closing invariant the DOM suite asserts to the exact request count), per-slot AbortControllers, runFavoriteSource's gate/stream/classify core, filter waves + curated-field merge (persistence via injected ports), runAffected's cascade, and retryFilter. Constructible without App; the input is an explicit DashboardRuntimeInput built by the shell from the favorites list — a stored DashboardDocument can replace that source later without touching the session (issue §4 requirement). Every DOM write stays in the shell behind hooks; slots keep their route's own DOM for now (session-state/shell-DOM split deferred to Phase 5 — deliberate, documented in the module header). Tile supersede remains client-abort only, never a server KILL (intentional, preserved).

Strongest signal: renderDashboard(app) keeps its signature as the integration entry and tests/unit/dashboard.test.ts passed unmodified — every streaming/race/filter-wave/recents behavior pin holds against the extracted runtime.

Teardown (issue acceptance: "tests prove cleanup"): destroy() aborts all in-flight tile/KPI/filter work, tears down chart instances, disposes the filter bar, and flips a destroyed flag making runAll/runAffected/retryFilter no-ops (re-checked after the token-preflight await) — an orphaned filter-bar debounce timer firing post-teardown can never issue a request or trigger a sign-out. buildFilterBar now returns { el, dispose() }; both the dashboard shell and the detached Data view dispose the previous bar on re-render, closing a real pre-existing orphan-timer gap. Like WorkbenchSession, destroy() has no production caller until Phase 5's route shells (the dashboard is its own tab today) — documented.

Review round (3 angles + e2e): the port-fidelity angle caught a genuine regression — with progress routed through the live slot.loadLabel, a superseded request's last buffered chunk could corrupt the newer wave's label (the old closure-captured label was immune). Fixed with a stale-generation guard before the progress hook + a unit test modelling the buffered-chunk-after-abort window. Also fixed: destroy-during-preflight window, two stale doc comments. Cross-file + conventions angles otherwise clean.

Verification: full gate green (106 files / 3,090 tests + tsc + check:arch, ui/dashboard rules active), build green, e2e chromium+webkit fully green, firefox 40/40 serialized (parallel-run timeouts are this machine's documented flake).

Acceptance criteria this completes (with #281): Workbench and Dashboard have separate route-scoped sessions and cancellation state ✅; both expose destroy() with proven cleanup ✅; route sessions do not import one another (enforced) ✅; dashboard runtime input is an explicit interface, not a favorites-list assumption ✅.

Checklist

  • npm test passes (the per-file coverage gate is non-negotiable)
  • Tests added/updated in the same change as the code
  • npm run build succeeds (single-file dist/sql.html)
  • Layers kept honest: pure logic in src/core/, network in src/net/ (injected fetch), DOM in src/ui/
  • No new runtime dependency (or it's a deliberate, justified addition — see CONTRIBUTING)
  • README / CHANGELOG.md ([Unreleased]) updated if behavior or the deployed surface changed
  • Reconciled affected tracked work (roadmap Roadmap to 1.0.0 #68, the issue body, ADR/CHANGELOG) if this change reshaped it

🤖 Generated with Claude Code

src/ui/dashboard/dashboard-session.ts is the route-scoped owner of the
dashboard execution runtime: the 6-way tile pool, wave generations
(reserved at wave CREATION, not worker start — the race-closing
invariant), per-slot AbortControllers, runFavoriteSource's
gate/stream/classify core, filter waves + curated-field merge +
persistence ports, runAffected's cascade, and retryFilter.
Constructible without App (issue rule 1); input is an explicit
DashboardRuntimeInput built by the shell from the favorites list, so a
stored DashboardDocument can replace that source later without touching
the session. Every DOM write stays in the shell behind injected hooks;
slots keep their route's own DOM nodes for now (session-state/shell-DOM
split deferred to Phase 5, documented). Tile supersede remains
client-abort only — never a server KILL (intentional, preserved).

renderDashboard(app) keeps its signature as the integration entry —
tests/unit/dashboard.test.ts passed UNMODIFIED, the strongest
behavior-preservation signal this extraction has.

destroy() aborts all in-flight tile/KPI/filter work, tears down chart
instances, disposes the filter bar, and flips a destroyed flag that
turns runAll/runAffected/retryFilter into no-ops (re-checked after the
token-preflight await too) — so an orphaned filter-bar debounce timer
firing post-teardown can never issue a request or trigger a sign-out.
buildFilterBar now returns { el, dispose() }; the dashboard shell and
the detached Data view both dispose the previous bar on re-render,
closing today's orphan-timer gap. Review round also added a
stale-generation guard on streamed progress: a superseded request's
last buffered chunk can no longer write into the newer wave's live
loading label (the pre-refactor closure-captured label was immune;
the guard restores that).

Gate: 106 files / 3090 tests green (dashboard suite untouched), build
green, check:arch green (ui/dashboard rules active: no ui/workbench,
no editor imports). e2e chromium+webkit fully green; firefox 40/40
serialized (parallel-run flake is the documented environment issue).
New session spec: 36 tests; dashboard-session.ts 100/94.9/100/100,
dashboard.ts 100/97.9/97.4/100, filter-bar.ts 100/100/100/100.

Part of #276 (phase 3b — completes Phase 3 with #281; roadmap #68
updated).

Claude-Session: ad39bf19-1690-43c7-abb4-f0084fca00a2

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
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