Found in review of #459 (PR #474).
VariableBarApp — the shared port both the Dashboard and the detached Data view
build the variable bar through — is named after one caller's persisted Workbench
field:
src/ui/variable-bar.ts:58 — state.filterActive: Record<string, boolean>
src/ui/variable-bar.ts:66 — params: Pick<WorkbenchParameterSession, 'saveVarValues' | 'saveFilterActive' | 'clearVarRecent'>
- read/written at
src/ui/variable-bar.ts:434, 443, 473, 482, 539 by the
single-select, the multi-select, and the plain text control
The detached Data view (src/ui/results.ts:1076) passes the real app
(app as VariableBarApp), so those writes land on the persisted
AppState.filterActive. But the Dashboard (src/ui/dashboard.ts:773-781)
satisfies the same port with a purely local draft map and a no-op save:
state: { varValues: draftValues, filterActive: draftActive, varRecent: state.varRecent },
params: { saveVarValues: () => {}, saveFilterActive: () => {}, … },
So for one of the port's two callers there is no Workbench state and nothing
persisted behind the name filterActive.
#459 deliberately left this alone and its ADR-0003 addendum records why. The
concept the name denotes — activation of a parameter's optional /*[ … ]*/
filter block — is a live SQL-filter concept that predates the curated Dashboard
model and survived its removal, so this is not a surviving curated-filter
identifier. But naming a shared port after one caller's persisted field is a
leaky abstraction and worth fixing on its own terms.
Goal
Rename the port's members to caller-neutral names — e.g. state.activeByName
and params.saveActive — while leaving AppState.filterActive,
saveFilterActive, effectiveFilterActive and the persisted key
asb:filterActive completely unchanged. No migration.
Why this needs its own change, not a rename pass
Two traps make this behaviour-risky rather than mechanical:
params is a Pick<WorkbenchParameterSession, …>. Rename a member and it
stops being a Pick. The detached caller currently passes the app straight
through and must instead build an adapter object.
- The bar mutates the caller's map in place.
variable-bar.ts:443/482/539
do app.state.filterActive[p.name] = … and then call
app.params.saveFilterActive(). Any adapter MUST pass the same object
reference for activeByName. An adapter that copies the map would silently
stop persisting activation in the detached Data view — and no test today
would fail, because that write-through is asserted nowhere.
Acceptance
VariableBarApp declares no member named after a filter; AppState,
WorkbenchParameterSession and asb:filterActive are untouched.
- Adapters in
src/ui/results.ts and src/ui/dashboard.ts alias (never copy)
the caller's activation map.
- A new test proves the write-through: committing a value in the detached
Data view's variable row flips the entry in the real AppState.filterActive
and calls saveFilterActive(). This is the guard whose absence makes the
rename dangerous, and it is worth adding whether or not the rename lands.
tests/helpers/fake-app.ts and the port-shaped objects in app.test.ts,
workbench-parameter-session.test.ts, variable-bar.test.ts,
state.test.ts, dashboard.test.ts updated in the same change.
npm test, npm run check:arch, npm run test:e2e green.
Found in review of #459 (PR #474).
VariableBarApp— the shared port both the Dashboard and the detached Data viewbuild the variable bar through — is named after one caller's persisted Workbench
field:
src/ui/variable-bar.ts:58—state.filterActive: Record<string, boolean>src/ui/variable-bar.ts:66—params: Pick<WorkbenchParameterSession, 'saveVarValues' | 'saveFilterActive' | 'clearVarRecent'>src/ui/variable-bar.ts:434, 443, 473, 482, 539by thesingle-select, the multi-select, and the plain text control
The detached Data view (
src/ui/results.ts:1076) passes the real app(
app as VariableBarApp), so those writes land on the persistedAppState.filterActive. But the Dashboard (src/ui/dashboard.ts:773-781)satisfies the same port with a purely local draft map and a no-op save:
So for one of the port's two callers there is no Workbench state and nothing
persisted behind the name
filterActive.#459 deliberately left this alone and its ADR-0003 addendum records why. The
concept the name denotes — activation of a parameter's optional
/*[ … ]*/filter block — is a live SQL-filter concept that predates the curated Dashboard
model and survived its removal, so this is not a surviving curated-filter
identifier. But naming a shared port after one caller's persisted field is a
leaky abstraction and worth fixing on its own terms.
Goal
Rename the port's members to caller-neutral names — e.g.
state.activeByNameand
params.saveActive— while leavingAppState.filterActive,saveFilterActive,effectiveFilterActiveand the persisted keyasb:filterActivecompletely unchanged. No migration.Why this needs its own change, not a rename pass
Two traps make this behaviour-risky rather than mechanical:
paramsis aPick<WorkbenchParameterSession, …>. Rename a member and itstops being a Pick. The detached caller currently passes the app straight
through and must instead build an adapter object.
variable-bar.ts:443/482/539do
app.state.filterActive[p.name] = …and then callapp.params.saveFilterActive(). Any adapter MUST pass the same objectreference for
activeByName. An adapter that copies the map would silentlystop persisting activation in the detached Data view — and no test today
would fail, because that write-through is asserted nowhere.
Acceptance
VariableBarAppdeclares no member named after a filter;AppState,WorkbenchParameterSessionandasb:filterActiveare untouched.src/ui/results.tsandsrc/ui/dashboard.tsalias (never copy)the caller's activation map.
Data view's variable row flips the entry in the real
AppState.filterActiveand calls
saveFilterActive(). This is the guard whose absence makes therename dangerous, and it is worth adding whether or not the rename lands.
tests/helpers/fake-app.tsand the port-shaped objects inapp.test.ts,workbench-parameter-session.test.ts,variable-bar.test.ts,state.test.ts,dashboard.test.tsupdated in the same change.npm test,npm run check:arch,npm run test:e2egreen.