BioFlow Requirements
System Requirements
SYS-001
change in progress 1. First-launch readiness for local enrolment SYS-001

When the device is opened on a workstation that has no pre-existing BioFlow data directory, a default local clinic shall be present and immediately available for the clinician to attach newly-enrolled patient records, without requiring the clinician to first create or configure a clinic record manually.

  • SRS-001
    change in progress 1. First-launch database initialisation SRS-001

    On first launch, when no database file exists at the configured application data path, the software shall create the local database, apply the current schema version, and create a default local clinic record that serves as the container for locally-created patient records.

    • ARCH-001
      change in progress 1. Local persistence subsystem ARCH-001

      The local persistence subsystem owns the application's local database — a SQLite file encrypted at rest via SQLCipher, located under the configured AppData path — applies schema migrations via Drift on first open, and exposes a typed API to the application layer for the clinic, patient, and recording domain entities.

      • IT-001
        change in progressPassed 1. First-launch DB initialisation IT-001

        Instantiate AppDatabase against a temporary filesystem path at which no database file exists. Trigger the initialisation pathway by performing any query that forces Drift's onCreate migration to run. Inspect the resulting state of the database on disk and of the seeded tables.

    • ST-001
      change in progressSkipped 1. Local clinic visible in Patients menu ST-001

      With the application launched on a workstation that has no pre-existing BioFlow data directory, navigate to the Patients menu and open the clinics dropdown. The local clinic entry shall be visible in the dropdown.

  • ST-001
    change in progressSkipped 1. Local clinic visible in Patients menu ST-001

    With the application launched on a workstation that has no pre-existing BioFlow data directory, navigate to the Patients menu and open the clinics dropdown. The local clinic entry shall be visible in the dropdown.

SYS-002
change in progress 2. Confidentiality of patient data at rest SYS-002

The device shall keep clinic, patient, clinical-user, recording, and audit-log data stored on the local workstation confidential against any party gaining access to the workstation's filesystem without the operator's BioFlow credentials.

  • SRS-002
    change in progress 2. Local database confidentiality at rest SRS-002

    The software shall keep the local database file unreadable to any process that opens it without supplying the database credential, and shall reject database read and write operations when the credential is missing or incorrect.

    • ARCH-002
      change in progress 2. Local database encryption ARCH-002

      The local SQLite database is encrypted on disk via the SQLCipher extension. The encryption key is held by flutter_secure_storage, which on Windows persists it as an entry in a DPAPI-encrypted file (flutter_secure_storage.dat) under the user's local app-data directory. On application bootstrap, the key is retrieved from secure storage and generated-and-persisted on first launch if absent. On every database connection open, the database connection service applies the key as the first statement (PRAGMA key) before any other operation; SQLCipher then transparently encrypts and decrypts SQLite pages.

      • IT-002
        change in progressPassed 2. Database file is encrypted at rest IT-002

        Initialise an AppDatabase against a temporary filesystem path through the production openEncryptedConnection pathway with a known test key. Write a canonical row through the typed clinic API and close the database. Attempt to open the resulting file with the plain sqlite3 driver (no credential supplied) and read any user table. Attempt to open the same file through the production pathway with an incorrect key and read any user table. Then re-open the file through the production pathway with the correct key and read the canonical row.

SYS-003
change in progress 3. Integrity of patient data at rest SYS-003

The device shall preserve the integrity of clinic, patient, recording, and audit-log data stored on the local workstation against accidental alteration, partial writes, referential inconsistency, and concurrent-write corruption — such that any record returned by the device represents a complete and consistent state authored through the application.

  • SRS-003
    change in progress 3. Local database integrity enforcement SRS-003

    The software shall enforce referential integrity between clinic, patient, and recording records through database foreign-key constraints; shall wrap any multi-row or cross-table mutation in a single transaction such that a crash or process termination during the mutation leaves the database in either the pre-mutation state or the post-mutation state and never a partial state; and shall reject — rather than silently truncate or drop — any write that would violate a NOT NULL, FOREIGN KEY, or CHECK constraint declared in the schema.

    • ARCH-003
      change in progress 3. Local database integrity mechanisms ARCH-003

      The local SQLite database enforces referential integrity and write atomicity through three schema- and connection-level mechanisms. First, the Drift-declared schema carries foreign-key constraints linking patient rows to their clinic and to their assigned clinical user, and recording rows to their patient. Column-level constraints declared in Dart (NOT NULL by default, plus CHECK on enumerated text columns such as clinics.origin, patients.origin, and recordings.upload_status) cause the SQLite engine to reject schema-violating writes at insert time. Second, the database connection service enables foreign-key enforcement on every connection by issuing PRAGMA foreign_keys = ON immediately after applying the encryption key, so the schema-declared FK relations are enforced by the SQLite engine for every read and write throughout the application's lifetime. Third, application-layer use cases that perform multi-row or cross-table writes execute them inside a single Drift transaction, so a crash or process termination during the mutation leaves the database in either the pre-mutation or the post-mutation state and never a partial state. Together, these three properties of how the persistence subsystem is configured realise SRS-003.

      • IT-003
        change in progressPassed 3. Database integrity mechanisms reject corrupt writes and roll back partial state IT-003

        Initialise an AppDatabase against a temporary filesystem path through the production openEncryptedConnection pathway with a known test key, forcing first-launch onCreate so the seeded LOCAL_CLINIC and the schema (including foreign-key declarations) are in place. Then, in four independent scenarios, exercise each of the rejection categories named in SRS-003 / ARCH-003.

        (a) Foreign-key enforcement — attempt to insert a patient row whose clinic_id references a clinic that does not exist, and verify the SQLite engine rejects the write.

        (b) Transactional atomicity — open a Drift transaction that performs two writes (insert a new clinic, then insert a patient referencing that clinic), then throw an exception inside the transaction; re-open the database and verify neither row is present.

        (c) CHECK constraint enforcement — attempt to insert a clinic row whose origin column value is outside the enumerated set ('local', 'remote', 'cloud') declared on the schema, and verify the SQLite engine rejects the write.

        (d) NOT NULL constraint enforcement — issue a raw INSERT (via customStatement, bypassing Drift's typed companion which guards NOT NULL at compile time) that supplies NULL for the clinics.name column, and verify the SQLite engine rejects the write at runtime. Bypassing Drift is necessary to prove the engine — not the compile-time guard — is what protects the data when a future migration, an unsafe raw write, or another customStatement caller bypasses the type system.

SYS-004
change in progress 4. Audit trail of clinical record operations SYS-004

The device shall maintain an audit trail that independently records every operation that creates, modifies, or deletes a clinical record on the local workstation, and every action that exports such a record to an external system. Each entry shall capture the action, the affected record, and a system-generated UTC timestamp. Audit entries shall not be modified in a way that obscures previously recorded information after they are written.

  • SRS-004
    change in progress 4. Audit logging of clinical record operations SRS-004

    The software shall write one audit-log entry for each of the following operations on local clinical records: creation, modification, and deletion of a patient; start, stop, and deletion of a recording; and successful upload of a recording to the cloud. Each entry shall carry the action, the identifier of the affected record, and a system-generated UTC timestamp. After an entry is written, the software shall not modify it in a way that obscures previously recorded information, and shall not delete audit entries.

    • ARCH-004
      change in progress 4. Audit-logging subsystem ARCH-004

      The audit-logging subsystem realises SRS-004 through three software items in the hexagonal architecture. A domain-layer audit-repository port whose surface offers create and read operations only — no update or delete is exposed to the application. A service in the application/infrastructure boundary that exposes one semantic logging method per audited operation (logPatientCreated, logPatientUpdated, logPatientDeleted, logRecordingStarted, logRecordingStopped, logRecordingDeleted, logRecordingUploaded). A Drift-backed adapter that persists entries into the audit_logs table of the local persistence subsystem (ARCH-001). Application- layer use cases invoke the service after the corresponding record mutation; the service composes an entry (action, entity type, entity identifier, actor identifier when available, JSON details, and timestamp) and delegates persistence to the port.

      • IT-004
        change in progressPassed 4. Audit-log entries are written for each clinical record operation IT-004

        Initialise an AppDatabase against a temporary filesystem path through the production encrypted-connection pathway with a known test key, forcing first-launch onCreate so the schema and seeded LOCAL_CLINIC are in place. Construct the use cases or the production callers that participate in the audit chain, wired with a real AuditService backed by the production audit-repository adapter against the same database. Then, in seven independent scenarios, exercise the SRS-004 commitment that each named operation writes its audit entry.

        (a) Patient creation: invoke the create-patient use case for a fresh patient; query audit_logs for entries on that patient; verify exactly one entry of action "PATIENT_CREATED" exists with the patient's identifier and a non-null timestamp.

        (b) Patient update: create a patient (setup), invoke the update-patient use case with a changed field; verify exactly one entry of action "PATIENT_UPDATED" exists with the patient's identifier.

        (c) Patient deletion: create a patient (setup), invoke the delete-patient use case; verify exactly one entry of action "PATIENT_DELETED" exists with the patient's identifier.

        (d) Recording start: drive the recording-start pathway through the production recording-notifier (with the signal-processing engine and sleep-prevention port replaced by test doubles); verify one entry of action "RECORDING_STARTED" exists with the recording identifier.

        (e) Recording stop: drive the recording-start then recording-stop pathways through the production recording-notifier with test doubles for the heavy collaborators; verify one entry of action "RECORDING_STOPPED" exists with the recording identifier (in addition to the "RECORDING_STARTED" entry).

        (f) Recording deletion: create a recording (setup), invoke the delete-recording use case; verify one entry of action "RECORDING_DELETED" exists with the recording identifier.

        (g) Upload: insert a recording (setup), drive the upload pathway through the production upload-manager with a test stub for the cloud client; verify one entry of action "RECORDING_UPLOADED" exists with the recording identifier.

    • ARCH-005
      change in progress 5. Audit-log record shape and immutability ARCH-005

      The audit_logs table in the local SQLite database stores one row per audit entry. Each row carries an audit identifier (UUID, primary key, immutable), an action string drawn from the application's enumerated set of audited operations, an entity_type and entity_id polymorphic reference to the subject record, a nullable user_id for the authenticated actor, a JSON-serialised details payload, and a UTC timestamp defaulted at the database level. No production code path issues UPDATE or DELETE against the table; the audit-repository port surface (ARCH-004) does not expose those operations.

      • IT-005
        change in progressPassed 5. Audit-log records have the required shape and are immutable IT-005

        Initialise an AppDatabase against a temporary filesystem path through the production encrypted-connection pathway with a known test key. Construct the audit chain (AuditService + production audit-repository adapter against the same database). Then, in two scenarios, verify the ARCH-005 commitments on the audit_logs row shape and the SRS-004 immutability rule.

        (a) Record shape: write a known audit entry via the audit service and query audit_logs for it; verify the row has a non-empty audit identifier, an action string drawn from the application's enumerated audit actions, the expected entity_type and entity_id, a non-null UTC timestamp generated by the database (not by the caller), and a parseable JSON details payload.

        (b) Write-then-read sanity check: write an audit entry via the audit service, re-read it, and assert the details payload and system timestamp match what was written. Immutability is enforced structurally — the audit-repository port (SRS-004 / ARCH-005) exposes only create-and-read methods, so no application path can mutate a row. This scenario is the runtime corollary, confirming that the storage layer round-trips an entry faithfully.

    • ST-003
      change in progressSkipped 3. Audit trail captures every operator-initiated clinical record operation ST-003

      With the application launched, the operator shall perform each audited clinical-record operation through the UI — patient creation, patient update, recording start, recording stop, recording deletion, and patient deletion — and the audit trail surfaced in the Audit Log tab shall contain one entry per operation, naming the affected patient or recording. Recording is exercised against the device's playback signal source so the test does not depend on a live amplifier.

  • SRS-005
    change in progress 5. Audit-trail review and export SRS-005

    The software shall present the local audit trail to an authorised operator in a form that lists each entry's action, affected record, and system timestamp, ordered by time. The software shall additionally provide a means to export the current audit trail to a file in a portable, human-readable format that preserves the same fields as the on-screen presentation, so that the exported file can be examined off the device by an auditor or by the clinical organisation's own information-security review.

    • ARCH-006
      change in progress 6. Audit-trail review-and-export presentation surface ARCH-006

      The audit-trail review-and-export surface realises SRS-005 by extending the Activities overlay (lib/presentation/overlays/activities_overlay.dart) with a tab titled "Audit Log". The pre-existing first tab is renamed from "Recent Activities" to "Recent Recordings" so that its name matches what it actually lists (rows from the recordings table) and does not collide with the new audit-trail tab. The resulting tab set, in order, is: Recent Recordings, Audit Log, Statistics, Archive. The Audit Log tab subscribes to the auditLogListProvider exposed by the application/providers layer, which streams the audit_logs rows from the local persistence subsystem (ARCH-001) via the audit-repository read side (ARCH-004). Entries are rendered as a scrollable list ordered by timestamp descending, each row showing the action, the entity_type/entity_id pair, and the UTC timestamp. The tab carries an Export button whose on-press handler serialises the currently-loaded entries to a file in one of two portable, human-readable formats — CSV (one row per entry, fields: audit_id, timestamp, action, entity_type, entity_id, user_id, details) or JSON (an array of entry objects with the same fields) — and writes the file to a workstation path chosen by the operator through the platform's standard save-file dialog.

      • IT-006
        change in progressPassed 6. Audit-trail export produces a faithful copy of the on-device entries IT-006

        Initialise an AppDatabase against a temporary filesystem path through the production encrypted-connection pathway with a known test key. Construct the audit chain (AuditService + production audit-repository adapter against the same database) and exercise two distinct audited operations through their use cases (a patient creation and a patient deletion) so that at least two distinguishable entries are present in audit_logs. Then, in two scenarios, exercise the SRS-005 / ARCH-006 commitment that the review-and-export surface produces a faithful copy of the trail.

        (a) CSV export: invoke the production export pathway requesting CSV output to a temporary file path; re-open the file with a CSV reader and assert that each row in audit_logs has a corresponding row in the file with the same audit_id, action, entity_type, entity_id, user_id, timestamp, and details content.

        (b) JSON export: invoke the production export pathway requesting JSON output to a temporary file path; re-open the file with a JSON parser and assert that each row in audit_logs has a corresponding object in the resulting array with the same audit_id, action, entity_type, entity_id, user_id, timestamp, and details content.

    • ST-002
      change in progressSkipped 2. Operator can review and export the local audit trail ST-002

      With the application launched, the operator shall be able to perform an audited clinical operation, locate the resulting entry in the on-screen audit-trail review surface, and export the audit trail to a workstation-side file from the same surface. The operator performs a patient creation and a patient deletion (representative audited clinical operations), then opens the Activities overlay and selects the Audit Log tab. The audit entries corresponding to the two operations shall be visible there. The operator then invokes Export CSV, completes the platform save-file dialog, and the application shall confirm a successful export.

SYS-005
reviewed 5. Operator workstation for recording sessions SYS-005

The device shall present the operator a workstation interface for conducting EEG recording sessions in which the live EEG signal display remains continuously visible while the operator accesses recording controls and overlay menus.

  • SRS-006
    reviewed 6. Maximized main window on launch SRS-006

    On application launch, the software shall display the main application window in a maximized state.

    • ARCH-007
      reviewed 7. Application window launch and state subsystem ARCH-007

      The application's top-level desktop window is shown maximised at launch by the native Windows runner: Win32Window::Show() (windows/runner/win32_window.cpp) calls ShowWindow with SW_SHOWMAXIMIZED, invoked from flutter_window.cpp during startup. The Dart application bootstrap additionally calls windowManager.maximize() after the window_manager package initialises, reinforcing the maximised state once the Flutter engine is running. Runtime window state — size, position, and maximised flag — is owned by the WindowStateNotifier (application layer), which applies changes through the window_manager package and persists them to SharedPreferences for restoration on the next launch. The window's minimum size is configured as 1024x768 through the window_manager package's WindowOptions in main.dart, which the package enforces against resize requests so the window cannot be made smaller than that floor.

    • ST-004
      reviewedSkipped 4. Main window maximised on launch ST-004

      With the application launched on the workstation, the main application window shall be displayed maximised, filling the available screen work area, with the EEG signal area occupying the main region.

  • SRS-007
    reviewed 7. Always-visible recording control widget SRS-007

    The software shall display a recording control widget anchored to the top of the main window, and shall keep that widget visible in all interaction states, including signal-control mode, menu mode, and while any overlay is open.

    • ARCH-008
      reviewed 8. Recording control widget ARCH-008

      The recording control widget occupies the fixed top region of the main window. It is a provider-bound presentation container (RecordingWidget) that subscribes to the recording, selected-patient, and signal-engine application providers and renders, through its view (RecordingWidgetView) and the recording sub-components (status badge, action buttons, center content, and menu panel), the recording controls, the current patient name and recording status, and the elapsed-recording-time readout. It dispatches operator actions — start and stop recording — to the recording view-model and holds no recording domain logic itself. The recorded duration shown in that readout is derived from the native recording engine's sample-accurate timing, pushed into the recording view-model as signal is captured rather than from a wall-clock timer, and is formatted as zero-padded HH:MM:SS by the recording widget state (RecordingWidgetState.formattedElapsedTime).

      • IT-008
        reviewed 7. Recording widget renders controls and context IT-008

        Pump the RecordingWidgetView — the rendering view that the provider-bound RecordingWidget container drives — in a flutter_test widget test with a seeded RecordingWidgetState representing a recording in progress for a known patient with a known elapsed time. Render the view and inspect its widget tree.

    • ST-005
      reviewedSkipped 5. Recording widget visible across states ST-005

      With the application launched, the recording control widget at the top of the main window shall remain visible as the operator moves between interaction states: in the default signal mode, after switching the bottom bar to menu mode, and while a navigation overlay is open.

  • SRS-008
    reviewed 8. Bottom-bar signal and menu mode toggle SRS-008

    The software shall provide a bottom bar that switches between a signal-control mode and a menu mode when the operator activates the bottom-bar mode toggle.

    • ARCH-009
      reviewed 9. Bottom-bar mode controller ARCH-009

      The bottom-bar mode controller owns the bottom bar's signal/menu mode. The mode is modelled as a domain value object (BottomBarMode) carrying the two modes and the pure toggle and per-mode content definitions; an application view-model (BottomBarNotifier) holds the current mode — signal mode at launch — and switches it via toggleMode(). The bottom bar widget (BottomBar) watches the mode provider and renders the controls for the current mode: the signal-parameter controls (high-pass, low-pass and notch filters, sensitivity, timebase, and montage) in signal mode, and the Patients/Montages/Settings/Activities navigation buttons in menu mode. A single toggle control invokes toggleMode() when the operator activates it; switching to menu mode opens the corresponding navigation overlay (the last-opened one, or Patients by default) and switching back to signal mode closes it. Dismissing the open overlay — by Escape, a click outside it, or re-selecting the active navigation item — clears the navigation selection, which resets the controller to signal mode.

      • IT-009
        reviewedPassed 8. Bottom bar switches rendered mode on toggle IT-009

        Pump the BottomBar in a flutter_test widget test with the bottom-bar mode provider in its default state. Inspect the rendered controls in signal mode. Activate the mode toggle. Inspect the rendered controls again, then activate the toggle a second time and inspect once more.

      • IT-011
        reviewedPassed 10. Bottom bar renders the per-mode controls IT-011

        Pump the BottomBar in a flutter_test widget test with the bottom-bar mode provider in its default state. Inspect the rendered controls in signal mode. Activate the mode toggle to switch to menu mode. Inspect the rendered controls again.

    • ST-006
      reviewedSkipped 6. Bottom bar toggles signal and menu modes ST-006

      With the application launched, the bottom bar shall begin in signal mode showing the signal-parameter controls; activating the bottom-bar toggle shall switch it to menu mode showing the Patients, Montages, Settings, and Activities navigation items; activating the toggle again shall return it to signal mode.

  • SRS-009
    reviewed 9. Dismiss overlay on ESC or click-outside SRS-009

    The software shall dismiss the active navigation overlay when the operator either presses the ESC key or clicks outside the overlay's content area.

    • ARCH-010
      reviewed 10. Overlay navigation subsystem ARCH-010

      The overlay navigation subsystem manages the application's modal navigation overlays (Patients, Montages, Settings, Activities). The open-overlay state is held as a single selected index in the navigation view-model (NavigationNotifier), which drives which overlay the main page renders. A companion overlay view-model (OverlayNotifier) tracks overlay visibility, the dismissible flag, and per-overlay data. Because the rendered overlay is keyed off one index rather than a stack, at most one overlay is shown at a time and selecting another replaces it: MainPage renders the selected overlay in a single AnimatedSwitcher slot above a dimmed signal area, with the recording widget and bottom bar layered above it. Dismissal is wired two ways: a KeyboardListener closes the active overlay on the ESC key, and a full-screen barrier behind the overlay closes it when the operator taps outside the overlay content. Both paths call clearSelection(), which is gated by a dismissible flag (overlayDismissibleProvider) so an overlay with in-progress edits (the montage editor) intercepts the close attempt instead of discarding unsaved work.

      • IT-010
        reviewedPassed 9. Overlay open/replace/dismiss state machine IT-010

        Construct a ProviderContainer and exercise the overlay navigation subsystem through its view-models without a running UI. (a) From the initial state, call selectItem(0) on the navigation notifier and inspect the navigation and overlay state. (b) With overlay 0 open, call selectItem(1) and inspect the state. (c) With an overlay open and the bottom bar in menu mode, call clearSelection() and inspect the navigation, overlay, and bottom-bar state.

    • ST-007
      reviewedSkipped 7. Overlay dismissed by ESC and click-outside ST-007

      With the application launched, the operator shall open a navigation overlay and dismiss it by pressing ESC, then open it again and dismiss it by clicking outside the overlay content; in each case the overlay closes and the underlying main view is shown again (the signal area may show the "Signal display inactive" placeholder when no signal is streaming).

  • SRS-010
    reviewed 10. At most one overlay open at a time SRS-010

    The software shall display at most one navigation overlay at a time; opening an overlay shall replace any overlay that is currently open.

    • ARCH-010
      reviewed 10. Overlay navigation subsystem ARCH-010

      The overlay navigation subsystem manages the application's modal navigation overlays (Patients, Montages, Settings, Activities). The open-overlay state is held as a single selected index in the navigation view-model (NavigationNotifier), which drives which overlay the main page renders. A companion overlay view-model (OverlayNotifier) tracks overlay visibility, the dismissible flag, and per-overlay data. Because the rendered overlay is keyed off one index rather than a stack, at most one overlay is shown at a time and selecting another replaces it: MainPage renders the selected overlay in a single AnimatedSwitcher slot above a dimmed signal area, with the recording widget and bottom bar layered above it. Dismissal is wired two ways: a KeyboardListener closes the active overlay on the ESC key, and a full-screen barrier behind the overlay closes it when the operator taps outside the overlay content. Both paths call clearSelection(), which is gated by a dismissible flag (overlayDismissibleProvider) so an overlay with in-progress edits (the montage editor) intercepts the close attempt instead of discarding unsaved work.

      • IT-010
        reviewedPassed 9. Overlay open/replace/dismiss state machine IT-010

        Construct a ProviderContainer and exercise the overlay navigation subsystem through its view-models without a running UI. (a) From the initial state, call selectItem(0) on the navigation notifier and inspect the navigation and overlay state. (b) With overlay 0 open, call selectItem(1) and inspect the state. (c) With an overlay open and the bottom bar in menu mode, call clearSelection() and inspect the navigation, overlay, and bottom-bar state.

    • ST-008
      reviewedSkipped 8. Only one overlay shown at a time ST-008

      With the application launched, the operator shall open a navigation overlay and then switch to other overlays in turn; at each switch only the newly-opened overlay shall be shown, with the previously-open overlay no longer visible.

  • SRS-011
    reviewed 11. Signal-mode bottom-bar controls SRS-011

    In signal mode, the bottom bar shall present the signal-parameter controls: a high-pass filter, a low-pass filter, a notch filter, a sensitivity control, a timebase control, and a montage selector.

    • ARCH-009
      reviewed 9. Bottom-bar mode controller ARCH-009

      The bottom-bar mode controller owns the bottom bar's signal/menu mode. The mode is modelled as a domain value object (BottomBarMode) carrying the two modes and the pure toggle and per-mode content definitions; an application view-model (BottomBarNotifier) holds the current mode — signal mode at launch — and switches it via toggleMode(). The bottom bar widget (BottomBar) watches the mode provider and renders the controls for the current mode: the signal-parameter controls (high-pass, low-pass and notch filters, sensitivity, timebase, and montage) in signal mode, and the Patients/Montages/Settings/Activities navigation buttons in menu mode. A single toggle control invokes toggleMode() when the operator activates it; switching to menu mode opens the corresponding navigation overlay (the last-opened one, or Patients by default) and switching back to signal mode closes it. Dismissing the open overlay — by Escape, a click outside it, or re-selecting the active navigation item — clears the navigation selection, which resets the controller to signal mode.

      • IT-009
        reviewedPassed 8. Bottom bar switches rendered mode on toggle IT-009

        Pump the BottomBar in a flutter_test widget test with the bottom-bar mode provider in its default state. Inspect the rendered controls in signal mode. Activate the mode toggle. Inspect the rendered controls again, then activate the toggle a second time and inspect once more.

      • IT-011
        reviewedPassed 10. Bottom bar renders the per-mode controls IT-011

        Pump the BottomBar in a flutter_test widget test with the bottom-bar mode provider in its default state. Inspect the rendered controls in signal mode. Activate the mode toggle to switch to menu mode. Inspect the rendered controls again.

    • ST-006
      reviewedSkipped 6. Bottom bar toggles signal and menu modes ST-006

      With the application launched, the bottom bar shall begin in signal mode showing the signal-parameter controls; activating the bottom-bar toggle shall switch it to menu mode showing the Patients, Montages, Settings, and Activities navigation items; activating the toggle again shall return it to signal mode.

  • SRS-012
    reviewed 12. Menu-mode bottom-bar navigation buttons SRS-012

    In menu mode, the bottom bar shall present the navigation buttons: Patients, Montages, Settings, and Activities.

    • ARCH-009
      reviewed 9. Bottom-bar mode controller ARCH-009

      The bottom-bar mode controller owns the bottom bar's signal/menu mode. The mode is modelled as a domain value object (BottomBarMode) carrying the two modes and the pure toggle and per-mode content definitions; an application view-model (BottomBarNotifier) holds the current mode — signal mode at launch — and switches it via toggleMode(). The bottom bar widget (BottomBar) watches the mode provider and renders the controls for the current mode: the signal-parameter controls (high-pass, low-pass and notch filters, sensitivity, timebase, and montage) in signal mode, and the Patients/Montages/Settings/Activities navigation buttons in menu mode. A single toggle control invokes toggleMode() when the operator activates it; switching to menu mode opens the corresponding navigation overlay (the last-opened one, or Patients by default) and switching back to signal mode closes it. Dismissing the open overlay — by Escape, a click outside it, or re-selecting the active navigation item — clears the navigation selection, which resets the controller to signal mode.

      • IT-009
        reviewedPassed 8. Bottom bar switches rendered mode on toggle IT-009

        Pump the BottomBar in a flutter_test widget test with the bottom-bar mode provider in its default state. Inspect the rendered controls in signal mode. Activate the mode toggle. Inspect the rendered controls again, then activate the toggle a second time and inspect once more.

      • IT-011
        reviewedPassed 10. Bottom bar renders the per-mode controls IT-011

        Pump the BottomBar in a flutter_test widget test with the bottom-bar mode provider in its default state. Inspect the rendered controls in signal mode. Activate the mode toggle to switch to menu mode. Inspect the rendered controls again.

    • ST-006
      reviewedSkipped 6. Bottom bar toggles signal and menu modes ST-006

      With the application launched, the bottom bar shall begin in signal mode showing the signal-parameter controls; activating the bottom-bar toggle shall switch it to menu mode showing the Patients, Montages, Settings, and Activities navigation items; activating the toggle again shall return it to signal mode.

  • SRS-013
    reviewed 13. Recorded-duration readout format SRS-013

    While a recording session is active, the software shall display the recorded duration in HH:MM:SS format.

    • ARCH-008
      reviewed 8. Recording control widget ARCH-008

      The recording control widget occupies the fixed top region of the main window. It is a provider-bound presentation container (RecordingWidget) that subscribes to the recording, selected-patient, and signal-engine application providers and renders, through its view (RecordingWidgetView) and the recording sub-components (status badge, action buttons, center content, and menu panel), the recording controls, the current patient name and recording status, and the elapsed-recording-time readout. It dispatches operator actions — start and stop recording — to the recording view-model and holds no recording domain logic itself. The recorded duration shown in that readout is derived from the native recording engine's sample-accurate timing, pushed into the recording view-model as signal is captured rather than from a wall-clock timer, and is formatted as zero-padded HH:MM:SS by the recording widget state (RecordingWidgetState.formattedElapsedTime).

      • IT-008
        reviewed 7. Recording widget renders controls and context IT-008

        Pump the RecordingWidgetView — the rendering view that the provider-bound RecordingWidget container drives — in a flutter_test widget test with a seeded RecordingWidgetState representing a recording in progress for a known patient with a known elapsed time. Render the view and inspect its widget tree.

    • UT-001
      reviewedPassed 1. Recorded-duration HH:MM:SS formatter UT-001

      Exercise the RecordingWidgetState.formattedElapsedTime getter with a range of recorded-duration values and assert the returned string is the zero-padded HH:MM:SS representation of that duration.

    • ST-009
      reviewedSkipped 9. Recorded duration shown in HH:MM:SS ST-009

      With the application launched and a recording session active, the operator shall see the recorded-duration readout in the recording widget displayed in HH:MM:SS format.

  • SRS-014
    reviewed 14. Patient name in recording widget SRS-014

    While a patient is selected, the software shall display the selected patient's name in the recording control widget.

    • ARCH-008
      reviewed 8. Recording control widget ARCH-008

      The recording control widget occupies the fixed top region of the main window. It is a provider-bound presentation container (RecordingWidget) that subscribes to the recording, selected-patient, and signal-engine application providers and renders, through its view (RecordingWidgetView) and the recording sub-components (status badge, action buttons, center content, and menu panel), the recording controls, the current patient name and recording status, and the elapsed-recording-time readout. It dispatches operator actions — start and stop recording — to the recording view-model and holds no recording domain logic itself. The recorded duration shown in that readout is derived from the native recording engine's sample-accurate timing, pushed into the recording view-model as signal is captured rather than from a wall-clock timer, and is formatted as zero-padded HH:MM:SS by the recording widget state (RecordingWidgetState.formattedElapsedTime).

      • IT-008
        reviewed 7. Recording widget renders controls and context IT-008

        Pump the RecordingWidgetView — the rendering view that the provider-bound RecordingWidget container drives — in a flutter_test widget test with a seeded RecordingWidgetState representing a recording in progress for a known patient with a known elapsed time. Render the view and inspect its widget tree.

    • ST-010
      reviewedSkipped 10. Patient name shown in recording widget ST-010

      With the application launched, the operator shall select a patient and then see that patient's name displayed in the recording control widget.

  • SRS-015
    reviewed 15. Bottom bar default and resting mode SRS-015

    The bottom bar shall be in signal mode when the application launches, and shall return to signal mode whenever the open navigation overlay is dismissed.

    • ARCH-009
      reviewed 9. Bottom-bar mode controller ARCH-009

      The bottom-bar mode controller owns the bottom bar's signal/menu mode. The mode is modelled as a domain value object (BottomBarMode) carrying the two modes and the pure toggle and per-mode content definitions; an application view-model (BottomBarNotifier) holds the current mode — signal mode at launch — and switches it via toggleMode(). The bottom bar widget (BottomBar) watches the mode provider and renders the controls for the current mode: the signal-parameter controls (high-pass, low-pass and notch filters, sensitivity, timebase, and montage) in signal mode, and the Patients/Montages/Settings/Activities navigation buttons in menu mode. A single toggle control invokes toggleMode() when the operator activates it; switching to menu mode opens the corresponding navigation overlay (the last-opened one, or Patients by default) and switching back to signal mode closes it. Dismissing the open overlay — by Escape, a click outside it, or re-selecting the active navigation item — clears the navigation selection, which resets the controller to signal mode.

      • IT-009
        reviewedPassed 8. Bottom bar switches rendered mode on toggle IT-009

        Pump the BottomBar in a flutter_test widget test with the bottom-bar mode provider in its default state. Inspect the rendered controls in signal mode. Activate the mode toggle. Inspect the rendered controls again, then activate the toggle a second time and inspect once more.

      • IT-011
        reviewedPassed 10. Bottom bar renders the per-mode controls IT-011

        Pump the BottomBar in a flutter_test widget test with the bottom-bar mode provider in its default state. Inspect the rendered controls in signal mode. Activate the mode toggle to switch to menu mode. Inspect the rendered controls again.

    • ST-011
      reviewedSkipped 11. Bottom bar rests in signal mode ST-011

      With the application launched, the operator shall see the bottom bar in signal mode, switch it to menu mode, then dismiss the open overlay and see the bottom bar return to signal mode.

  • SRS-016
    reviewed 16. Minimum main window size SRS-016

    The software shall prevent the main application window from being resized smaller than 1024 by 768 pixels.

    • ARCH-007
      reviewed 7. Application window launch and state subsystem ARCH-007

      The application's top-level desktop window is shown maximised at launch by the native Windows runner: Win32Window::Show() (windows/runner/win32_window.cpp) calls ShowWindow with SW_SHOWMAXIMIZED, invoked from flutter_window.cpp during startup. The Dart application bootstrap additionally calls windowManager.maximize() after the window_manager package initialises, reinforcing the maximised state once the Flutter engine is running. Runtime window state — size, position, and maximised flag — is owned by the WindowStateNotifier (application layer), which applies changes through the window_manager package and persists them to SharedPreferences for restoration on the next launch. The window's minimum size is configured as 1024x768 through the window_manager package's WindowOptions in main.dart, which the package enforces against resize requests so the window cannot be made smaller than that floor.

    • ST-012
      reviewedSkipped 12. Minimum window size is enforced ST-012

      With the application launched, attempt to resize the main window below the 1024x768 minimum and confirm the window is clamped to at least 1024x768.

  • SRS-017
    reviewed 17. Selectable application themes SRS-017

    The software shall provide three selectable visual themes — Light, Dark, and Dark Glass — and shall apply the operator's selected theme across the application.

    • ARCH-011
      reviewed 11. Theme subsystem ARCH-011

      The theme subsystem owns the application's visual theming. The available themes are modelled as a domain value object (ThemeType: minimalLight, minimal, and darkGlass — surfaced to the operator as Light, Dark, and Dark Glass). AppTheme.forPreset builds the Flutter ThemeData for a given type, and application providers expose the active selection: the UISettingsNotifier holds the selected ThemeType, currentThemeTypeProvider reads it, and the theme-data and theme-mode providers derive the rendered theme from it, so the whole application re-themes when the selection changes.

      • IT-012
        reviewedPassed 11. Theme selection drives the application theme IT-012

        Construct a ProviderContainer overriding the current-theme-type selection to each of the three theme types in turn, and read the theme-mode and theme-data providers for each.

    • ST-013
      reviewedSkipped 13. Operator selects each visual theme ST-013

      With the application launched, the operator shall open the theme selector, see the three available themes (Light, Dark, Dark Glass), and select each of the three in turn, with the application's appearance changing to match each selected theme.

  • SRS-018
    inactive 18. Live signal display frame rate SRS-018

    The software shall render the live EEG signal display at a sustained rate of at least 60 frames per second.

  • SRS-019
    inactive 19. Multi-monitor window position restore SRS-019

    The software shall remember the main window's position and restore it on the next launch, including when the window was placed on a secondary monitor.

  • SRS-020
    inactive 20. Display-scaling layout integrity SRS-020

    The software shall present the main interface without clipped or overlapping controls under Windows display-scaling settings of 100%, 125%, 150%, and 200%.