Skip to content

Commit 2bbaea0

Browse files
jdpigeonclaude
andcommitted
Fix six bugs from bugfix-todo
- saveEpochs: add missing closing paren so raw_epochs.save no longer raises a Python syntax error and silently fails - Remove dead Pause/'PAUSE' experiment action (zero references) - Remove dead `jitter` param (declared/assigned 200, never read) - pyodideReducer: ExperimentCleanup now also resets topoPlot and channelInfo (were left stale after cleanup) - DisconnectFromDevice: give it its own 'DISCONNECT_FROM_DEVICE' type instead of colliding with 'EXPERIMENT_CLEANUP'; an epic in experimentEpics translates it to ExperimentCleanup (behavior preserved) - Document the post-CSV string-coercion trap in .llms/learnings.md - .gitignore: exclude generated pyodide/src runtime payload and __pycache__ Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent ea47b82 commit 2bbaea0

13 files changed

Lines changed: 37 additions & 12 deletions

File tree

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,10 @@ dist
2828
.idea
2929
keys.js
3030
src/renderer/utils/webworker/src
31+
32+
# Generated Pyodide runtime payload (downloaded by install scripts, not source)
33+
src/renderer/utils/pyodide/src
34+
35+
# Python bytecode cache
36+
__pycache__/
37+
*.pyc

.llms/learnings.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,12 @@ In dev: use `/@fs<absPath>` (Vite's `/@fs/` serving). In prod: use `file://<absP
8282

8383
Any hook function (e.g. `initResponseHandlers` in `src/renderer/utils/labjs/functions.ts`) that needs the component ID must use `this.id`, not `this.options.id`. Using `this.options.id` will always be `undefined` for loop-cloned components, causing silent early returns and broken behavior (e.g. keydown handlers never installed).
8484

85+
## Behavior data: booleans become strings after the CSV round-trip
86+
87+
Experiments emit `this.data.correct_response` as a real boolean (`true`/`false`) and `response_given` as `'yes'`/`'no'` (see `src/renderer/utils/labjs/functions.ts`). But all consumers in `src/renderer/utils/behavior/compute.js` read data **after** it's been written to CSV and re-parsed, so every value is a **string**. That's why existing code gates on `row.correct_response === 'true'` and `row.response_given === 'yes'`, and parses numbers with `parseFloat`.
88+
89+
Trap: a new metric written naively (`row.correct_response === true`, or arithmetic on an unparsed string) will silently return `false`/`0`/`NaN` for post-CSV data — and may *work* on pre-CSV in-memory data, so it passes a quick test and fails in production. Always compare against the string `'true'`/`'yes'` and `parseFloat` before doing math.
90+
8591
## Pre-existing TypeScript errors (do not treat as regressions)
8692

8793
- `src/renderer/epics/experimentEpics.ts` (lines 170, 205) — RxJS operator type mismatch

src/renderer/actions/deviceActions.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ import { Device, DeviceInfo } from '../constants/interfaces';
99
export const DeviceActions = {
1010
// TODO: write type for devices
1111
ConnectToDevice: createAction<Device>('CONNECT_TO_DEVICE'),
12-
DisconnectFromDevice: createAction<void, 'EXPERIMENT_CLEANUP'>(
13-
'EXPERIMENT_CLEANUP'
12+
DisconnectFromDevice: createAction<void, 'DISCONNECT_FROM_DEVICE'>(
13+
'DISCONNECT_FROM_DEVICE'
1414
),
1515
SetConnectionStatus: createAction<CONNECTION_STATUS, 'SET_CONNECTION_STATUS'>(
1616
'SET_CONNECTION_STATUS'

src/renderer/actions/experimentActions.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import { ExperimentStateType } from '../reducers/experimentReducer';
1313

1414
export const ExperimentActions = {
1515
Start: createAction('START'),
16-
Pause: createAction('PAUSE'),
1716
Stop: createAction<{ data: string }, 'STOP'>('STOP'),
1817
SetType: createAction<EXPERIMENTS, 'SET_TYPE'>('SET_TYPE'),
1918
SetExperimentObject: createAction<ExperimentObject, 'SET_EXPERIMENT_OBJECT'>(

src/renderer/constants/interfaces.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ export type ExperimentParameters = {
2424
description?: ExperimentDescription;
2525
intro: string;
2626
iti: number;
27-
jitter: number;
2827
nbPracticeTrials?: number;
2928
nbTrials: number;
3029
presentationTime?: number;

src/renderer/epics/experimentEpics.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@ import {
99
tap,
1010
} from 'rxjs/operators';
1111
import { isActionOf } from '../utils/redux';
12-
import { ExperimentActions, ExperimentActionType } from '../actions';
12+
import {
13+
DeviceActions,
14+
ExperimentActions,
15+
ExperimentActionType,
16+
} from '../actions';
1317
import { RouterActions } from '../actions/routerActions';
1418
import {
1519
DEVICES,
@@ -214,12 +218,25 @@ const navigationCleanupEpic: Epic<any, ExperimentActionType, RootState> = (
214218
map(() => ExperimentActions.ExperimentCleanup())
215219
);
216220

221+
// Disconnecting a device tears down the experiment too: this triggers the BLE
222+
// disconnect (deviceCleanupEpic) and resets experiment + pyodide state.
223+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
224+
const disconnectFromDeviceEpic: Epic<any, ExperimentActionType, RootState> = (
225+
// DeviceActions input action is outside this slice's action union
226+
action$
227+
) =>
228+
action$.pipe(
229+
filter(isActionOf(DeviceActions.DisconnectFromDevice)),
230+
map(() => ExperimentActions.ExperimentCleanup())
231+
);
232+
217233
export default combineEpics(
218234
createNewWorkspaceEpic,
219235
startEpic,
220236
experimentStopEpic,
221237
updateSessionEpic,
222238
autoSaveEpic,
223239
saveWorkspaceEpic,
224-
navigationCleanupEpic
240+
navigationCleanupEpic,
241+
disconnectFromDeviceEpic
225242
);

src/renderer/experiments/faces_houses/params.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ export const params = {
3939
iti: 500,
4040
presentationTime: 1000,
4141
selfPaced: true,
42-
jitter: 200,
4342
sampleType: 'with-replacement',
4443
intro: `You will view a series of faces and houses. Press 1 when a face appears
4544
and 9 for a house. Press the the space bar on your keyboard to start doing the

src/renderer/experiments/multitasking/params.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ export const params = {
44
trialDuration: 1000,
55
nbTrials: 150,
66
iti: 1000,
7-
jitter: 200,
87
sampleType: 'with-replacement',
98
intro: `In this task you will learn about multitasking difficulties using a
109
task mixing and switching paradigm. You will go through several instruction

src/renderer/experiments/search/params.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ export const params = {
44
trialDuration: 1000,
55
nbTrials: 150,
66
iti: 500,
7-
jitter: 200,
87
sampleType: 'with-replacement',
98
intro: `You know how difficult it is to find your keys in a messy room. We
109
want to know how good you are in quickly finding your keys. Instead of keys,

src/renderer/experiments/stroop/params.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ export const params = {
1010
trialDuration: 1000,
1111
nbTrials: 150,
1212
iti: 500,
13-
jitter: 200,
1413
sampleType: 'with-replacement',
1514
intro: `In this experiment, your task will be to identify the color of the
1615
word shown on the screen. The word itself is immaterial - you can safely

0 commit comments

Comments
 (0)