Found while planning #447 phase 1. Deferred: doing it now would relocate code that #447 phase 1 deletes.
What
src/dashboard/application/dashboard-viewer-session.ts is 1857 lines, of which
1354 are a single factory closure — createDashboardViewerSession starts at line 503 and runs
to the end of the file. tests/unit/dashboard-viewer-session.test.ts is 3441 lines.
The closure holds ~65 members with no internal boundary: tile runtime construction, the parameter
analysis, target resolution, the option-source/provider wave engine, published-state assembly, the
execution pool, the commit API, and the session lifecycle all capture the same scope.
That has three concrete costs:
- Review surface. Any change to one concern reads as a change to the whole session.
- The 90% branch floor (
tests/vitest.config.ts, perFile: true, 100/95/90/100) is enforced
over the whole 1857-line file, so a defensive branch in one concern can only be covered through
the public session API, via a fixture that sets up every other concern too. That is a large part
of why the spec is 3441 lines.
- Closure capture instead of parameters. Members read
tiles, filters, analysis,
documentRef and friends directly from scope, so there is no stated contract between concerns.
Why after #447, not before
#447 phase 1 removes the curated-filter/provider infrastructure, which accounts for roughly
500 lines (~27%) of this file:
| Region |
Lines |
What |
FilterSourceRuntime |
405-424 |
provider record |
filterInitialActive / filterDefaultValue |
458-468 |
persisted-default readers |
sourceBackedParams, filterSources |
610-640 |
provider index |
curatedControls, staticFilterDiagnostics, mergeControls |
730-807 |
provider/control merge |
optionsSignature … runFilterSourceWave |
1105-1466 |
the whole option-source wave engine |
Splitting first would mean carefully extracting code that is about to be deleted, and would put two
large diffs on the same file back to back. After phase 1 the file is ~1350 lines with a clearer set
of responsibilities, and the extraction boundaries below fall out of what remains.
Proposed split (post-#447)
| File |
Contents |
dashboard-viewer-session.ts |
public facade + lifecycle: start/refresh/refreshTile/syncDocument/destroy/setTileSearch/cancelTile/setGridRenderMode/setDashboardStyle/preflight |
viewer-tile-runtime.ts |
TileRuntime, buildTileRuntime, runTile, tileResultMeta, markTextAndErrorTiles, runnableTiles, isRunnableTileRuntime, runPool, supersede |
viewer-variable-runtime.ts |
variable runtime records, rawValues/activeMap/committedRootValues/prepareBatch, the commit API (setFilter/applyFilter/applyFilters/clearFilter/clearAllFilters/resetFilters), commitAndRerun/runAffectedWave/reserveAffected |
viewer-state.ts |
ViewerTileState/ViewerFilterState/DashboardViewState + buildState/publish |
dashboard-viewer-session.types.ts |
DashboardViewerDeps, ViewerExecutor, ViewerConnection, ViewerReadRequest (type-only seam contract, per ADR-0002 phase 0) |
tests/unit/dashboard-viewer-session.test.ts splits alongside, one spec per new module.
Acceptance
- No behaviour change; the existing session public API is byte-identical to callers
(src/ui/dashboard.ts is the only consumer).
- Each new file independently meets 100/95/90/100. This is the real work — extracted
collaborators must take explicit inputs rather than capture closure scope, and each one must be
reachable from its own spec without standing up the whole session.
npm run check:arch still passes: src/dashboard/application may not import src/ui,
src/editor, src/application, src/state.ts, src/net, src/dashboard/ui
(build/check-boundaries.mjs), and the new siblings inherit that rule.
- No new runtime dependency; no seam changes.
Non-goals
- Changing the session's public API or the published
DashboardViewState shape.
- Splitting
src/ui/dashboard.ts (2641 lines) — related, but a separate call.
Found while planning #447 phase 1. Deferred: doing it now would relocate code that #447 phase 1 deletes.
What
src/dashboard/application/dashboard-viewer-session.tsis 1857 lines, of which1354 are a single factory closure —
createDashboardViewerSessionstarts at line 503 and runsto the end of the file.
tests/unit/dashboard-viewer-session.test.tsis 3441 lines.The closure holds ~65 members with no internal boundary: tile runtime construction, the parameter
analysis, target resolution, the option-source/provider wave engine, published-state assembly, the
execution pool, the commit API, and the session lifecycle all capture the same scope.
That has three concrete costs:
tests/vitest.config.ts,perFile: true, 100/95/90/100) is enforcedover the whole 1857-line file, so a defensive branch in one concern can only be covered through
the public session API, via a fixture that sets up every other concern too. That is a large part
of why the spec is 3441 lines.
tiles,filters,analysis,documentRefand friends directly from scope, so there is no stated contract between concerns.Why after #447, not before
#447 phase 1 removes the curated-filter/provider infrastructure, which accounts for roughly
500 lines (~27%) of this file:
FilterSourceRuntimefilterInitialActive/filterDefaultValuesourceBackedParams,filterSourcescuratedControls,staticFilterDiagnostics,mergeControlsoptionsSignature…runFilterSourceWaveSplitting first would mean carefully extracting code that is about to be deleted, and would put two
large diffs on the same file back to back. After phase 1 the file is ~1350 lines with a clearer set
of responsibilities, and the extraction boundaries below fall out of what remains.
Proposed split (post-#447)
dashboard-viewer-session.tsstart/refresh/refreshTile/syncDocument/destroy/setTileSearch/cancelTile/setGridRenderMode/setDashboardStyle/preflightviewer-tile-runtime.tsTileRuntime,buildTileRuntime,runTile,tileResultMeta,markTextAndErrorTiles,runnableTiles,isRunnableTileRuntime,runPool,supersedeviewer-variable-runtime.tsrawValues/activeMap/committedRootValues/prepareBatch, the commit API (setFilter/applyFilter/applyFilters/clearFilter/clearAllFilters/resetFilters),commitAndRerun/runAffectedWave/reserveAffectedviewer-state.tsViewerTileState/ViewerFilterState/DashboardViewState+buildState/publishdashboard-viewer-session.types.tsDashboardViewerDeps,ViewerExecutor,ViewerConnection,ViewerReadRequest(type-only seam contract, per ADR-0002 phase 0)tests/unit/dashboard-viewer-session.test.tssplits alongside, one spec per new module.Acceptance
(
src/ui/dashboard.tsis the only consumer).collaborators must take explicit inputs rather than capture closure scope, and each one must be
reachable from its own spec without standing up the whole session.
npm run check:archstill passes:src/dashboard/applicationmay not importsrc/ui,src/editor,src/application,src/state.ts,src/net,src/dashboard/ui(
build/check-boundaries.mjs), and the new siblings inherit that rule.Non-goals
DashboardViewStateshape.src/ui/dashboard.ts(2641 lines) — related, but a separate call.