Skip to content

WS6: integrate the approved Clean design - #285

Merged
jdpigeon merged 14 commits into
mainfrom
feat/ws6-clean-integration
Sep 28, 2026
Merged

jdpigeon merged 14 commits into
mainfrom
feat/ws6-clean-integration

Conversation

@jdpigeon

@jdpigeon jdpigeon commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor

Summary

The Clean route now renders the approved design from #280 (components/Clean/). A student:

  • picks one complete raw recording;
  • leaves out trials and flags sensors;
  • accepts or restores auto-flag suggestions;
  • watches the Live ERP;
  • saves, then goes on to Analyze.

Ended-early recordings stay hidden until revealed. Revealed ones are labelled, can't be selected, and can be moved to the Trash after an in-app confirm. The workflow badges update as soon as a save lands.

Commits (one per plan task):

  • T1 feat(main): two new IPC channels, fs:readWorkspaceIncompleteEEGData and fs:deleteIncompleteRecording. The path check and the behavior-file pairing live in the pure incompleteRecordingFiles in recordings.ts. Main only calls shell.trashItem on its result.
  • T2 fix(analysis): apply_rejection always assigns info['bads'], so un-flagging a sensor reaches the saved file.
  • T3 refactor(pyodide): LoadEpochs takes one path (createAction<string>). The epic reads [path]. The reducer's LoadEpochs case clears epochArrays and suggestedRejections.
  • T4 feat(clean): the panes say "trials", including the aria labels. FitPane stays, with a ponytail: note on its ceiling.
  • T5 feat(clean): CleanComponent/index.tsx is rewritten as the container-facing screen around CleanDatasetSelect and CleanReview. Removed from the screen: <select multiple>, the auto-merge effect, showMessageBox, CleanExplainer, renderAnalyzeButton and duration.
  • T6 feat(shell): useWorkspaceProgress(…, refresh). AppShellContainer passes cleanedEpochsSave.revision; WorkspaceAreaGate passes 0.
  • T7 test(clean): CleanRejections.test.tsx is rewritten against the new contract.
  • A final chore commit formats the files I changed (unrelated reformatting reverted) and adds a learnings entry.

Decisions followed

All 16 decisions are implemented as written. Notes on a few:

  • 2 (suggestions): accepted = rejected.has(index). The "N by you" count is CleanReview's own rejected.size - acceptedCount, which equals "rejected indices that are not suggestions".
  • 5–6 (saving): the save destination is kept in a ref. Try again sends CleanEpochs({ dropIndices: [], badChannels: [...] }).
  • 7 (exclusions): rejected is reset during render when the epochArrays identity changes (React's pattern for adjusting state when a prop changes). No effect is involved, so dropped trials never flash back as un-rejected for a frame. badChannels persists across saves and is reset only by Start cleaning.
  • 10 (no trials): checked. get_epochs_arrays handles 0 epochs (it returns n_epochs: 0 and writes a 0-byte buffer, with an MNE warning), so no Python fix or extra test was needed.
  • 11 (delete): the delete confirm now says "…will be moved to your computer's Trash."

Deviations, with reasons

  1. One confirmation, not a chain. For Save & analyze I show a single dialog picked by priority: rejectAll, then removeSelected, then applyChannels. This matches Clean.stories.tsx (onSave uses else if) and the old "exactly one prompt" behavior. Main was told.
  2. One line of primer copy. Step 1 said "Every column in the Epochs panel is one trial". Decision 14 renames that heading to "Trials", so the line now says "Trials panel", instead of pointing at a heading that no longer exists. Main was told.
  3. The primer collapses on the first edit. It opens on the first review at step 1 (decision 16) and closes on the first toggle, accept or restore, as the story harness does. Before that, its pointer covers the reviewer.
  4. Dead props dropped. Props no longer declares type, deviceType, epochsInfo or session. The screen doesn't use them, and CleanContainer is unchanged.

Verification

  • npm run typecheck: clean. It was also clean after every task.
  • npx vitest run src/renderer/components/CleanComponent src/renderer/components/AppShell src/main: 9 files, 51 tests passed.
  • python -m pytest tests/analysis/test_apply_rejection.py tests/analysis/test_epoch_arrays.py (native MNE 1.12.1): 9 passed.
  • Stories: a throwaway composeStories render of all 13 Domain/Clean stories passed. The file was deleted afterwards.

Electron playtest. This ran in the Electron window over CDP port 9366 (never :5173), using the fixture headset and the throwaway workspace WS6-Playtest-2026-09-27.

  • Recordings:
    • P01-A-1 was ended early with "End experiment early", which produced -raw.incomplete.csv and -behavior.incomplete.csv.
    • P01-A-2 was a complete Faces/Houses run: 126 key presses, 54 epochs.
  • Screenshots are in /tmp/ws6-integration/. Each state was captured at 1366x768 and 1280x720, at device pixel ratio 1:
# State
01 Select: complete recordings only, ended-early hidden ("1 ended-early recording hidden · Show")
02 Recording chosen; the rail shows the pick
03 Ended-early revealed: "ENDED EARLY" row, not selectable, Delete button
04 Loading ("Loading P01-A-2-raw.csv…"); the reducer clears the old arrays
05 Review with the primer at step 1, pointer on the reviewer, "Averaged over 54 trials"
06 2 trials left out: "2 of 54 trials left out (all by you)", 52 averaged
07 Sensor AF7 flagged
08 Second sensor flagged: flag applied at once, "More than one bad sensor flagged" dialog, Got it
09 Suggest noisy trials (threshold lowered to 20 µV): suggestions card; rejected unchanged
10 Accept: the index is added ("3 of 54 … (all suggested)")
11 Restore: the index is removed ("2 of 54")
12 Apply exclusions: "✓ Cleaned dataset saved", badge "1 cleaned", Analyze marked NEXT →, 52 trials, AF7 still flagged
13 Save & analyze with 1 trial left out: "Remove the selected trials?" dialog
14 Confirmed: saved, then navigated to #/analyze
15 Delete ended-early confirm: "…will be moved to your computer's Trash."
16 After the delete, the row is gone
  • Files checked on disk:
    • After the delete, both P01-A-1-raw.incomplete.csv and P01-A-1-behavior.incomplete.csv show up in Finder's Trash (checked with osascript).
    • The saved P01-cleaned-epo.fif has 51 epochs (54 − 2 − 1) and bads == ['AF7'].
  • Console: 0 console errors and 0 uncaught exceptions for the whole run. Errors were collected across reloads by an init script, plus the CDP error log. The only warnings were Electron's CSP warning and Radix's DialogContent description warning, both already present before this change.
  • Cleanup: the throwaway workspace was deleted and my Electron process was killed.

Known ceilings

  • FitPane zoom makes the Live ERP canvas soft at about 0.6×. The upgrade path is resize-aware canvases (see the ponytail: note).
  • Delete leaves the <stem>-events.json sidecar. Fixed in f06f72a: Delete also trashes the sidecar.
  • The suggestion count treats a trial you left out that auto-flag also suggests as "suggested", because accepted is derived from rejected.
  • Out of scope, as the plan says: a worker-error state for Clean loads, and a suggested highlight inside EpochReviewer.
  • Seen while playtesting, not changed here:
    • With session 1 already on disk as incomplete, Collect's "Run & record" did nothing until the session number was edited.
    • lab.js can hang when automated key presses arrive too quickly. This is already in learnings.

In-app verification of the fix round (7441b01)

An independent CDP playtest in the Electron window, at 1366×768 and 1280×720. Screenshots are in /tmp/ws6-check/.

  • Loading: Save and Apply are disabled until the trials load.
  • Many suggestions: at the lowest threshold there are 193 suggestions. The page stays 768/768 and 720/720 (no scroll); only the list scrolls, with its header pinned. "✓ Left out" shows, and 0 of 193 reasons overflow.
  • Flag AF7 and Apply: during saving, the slider and Accept/Restore are disabled. After the save, the AF7 row is still visible and shown as flagged. Un-flagging and applying again gives saved bads == [] (read with native MNE).
  • Save & analyze navigates to /analyze.
  • Delete an ended-early recording: the files go to the Trash.
  • Console: 0 page errors and 0 exceptions.

@jdpigeon
jdpigeon merged commit 4914a22 into main Sep 28, 2026
15 checks passed
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