The /api/oneliners time window is now read per-user from users.toml
(window_hours, default 72h). Clients no longer carry a window:
client.toml oneliner_window_hours, SyncConfig.window_hours, the
?hours=N query param, and the render_case_list cutoff filter are
gone. ETag suffix keeps the effective hours so an admin edit to
users.toml invalidates client caches on the next request.
OnelinersResponse.window_hours stays in the wire format, but now
exists solely to anchor client reconciliation.
Introduce `TranscriptState { Pending, Silent, Content }` in
doctate-common as the canonical three-way state of a recording's
`.transcript.txt` sidecar. Previously each call-site projected the
raw `Option<String>` / `.exists()` onto its own 2-state view and the
projections disagreed: the UI treated a 0-byte silent transcript like
`Content` while the oneliner worker treated it like `Pending`,
leaving silent-only cases stuck on "generiere Titel …" forever with
no persisted oneliner.json.
`update_oneliner` now settles a silent-only case to
`OnelinerState::Empty` when no `Content` transcript exists and no
recording is still `Pending`, so the UI resolves to "unbenannt" and
recovery treats it as terminal.
Single reader: `paths::read_transcript_state`. All call-sites
(scan_recordings, compute_oneliner_display, has_pending_recordings,
all_transcripts_joined, read_recordings, enqueue_pending_for_user,
scan_m4as, cases_needing_oneliner_in, case_recordings.html) go
through the same typed abstraction and must handle `Silent` via
exhaustive match.
Regression tests:
- silent_case_empty_test: heal path settles silent-only case to
Empty without calling Ollama
- case_page_silent_only_shows_empty_not_generating: UI renders
"unbenannt", not "generiere Titel …"
The oneliner is now stored as `OnelinerState` enum which can represent
three states: `Ready`, `Empty` or `Error`. This allows the client to
differentiate between a case with no medical content and a case where
the oneliner generation failed.
The `OnelinerState` enum is serialized to JSON and stored in
`oneliner.json` file. The `OnelinerEntry` struct has been updated to
reflect this change.
The client-desktop application has been updated to handle the new
`OnelinerState` enum and display appropriate UI elements for each state.
The server-side code has also been updated to read and write the
`OnelinerState` enum, and to handle the new file format.
The `reset_case_artefacts` function in
`server/src/routes/case_actions.rs` has been updated to remove
`oneliner.txt` and create `oneliner.json` instead.
The tests have been updated to reflect these changes.
The TOML dependency is added to `Cargo.lock` to support configuration
file parsing.
New modules `config` and `startup` are introduced in
`doctate-client-core`:
- `config`: Handles client configuration loading and saving, including
defaults for polling intervals and window hours.
- `startup`: Contains routines for initial cleanup tasks, such as
removing stale markers and orphan audio files based on defined
retention periods.
Introduce `last_recording_at` to `OnelinerEntry` and use it as the
primary sort key for cases. This ensures that cases with recent
dictation activity are prioritized, even if their initial creation date
is older.
The logic for determining a case's activity has been updated to consider
the most recent `.m4a` file's modification time (`last_recording_at`),
falling back to `updated_at` or `created_at` if necessary.
This change also refactors the timestamp formatting and handling within
the web interface to correctly display and sort cases based on their
actual last activity time, improving user experience and data relevance.
The `now_rfc3339` function is updated to strip sub-second precision for
consistent filename generation.
Introduces a new crate `doctate-client-core` to house shared client-side
logic. This includes:
- `case_store`: Manages local case marker files and merging with server
snapshots.
- `snapshot_cache`: Placeholder for caching server responses.
- `oneliner_poller`: Placeholder for the background polling task.
The workspace configuration and `Cargo.lock` have been updated to
include the new crate.
This commit introduces a new `doctate-common` crate to the workspace.
This crate will house shared data structures and constants used by both
the `doctate-server` and any future clients.
This refactoring helps to:
- Reduce code duplication.
- Improve maintainability by centralizing common logic.
- Define a clear API contract for server-client communication.
The following modules have been added:
- `ack`: Contains `AckResponse` and `AckStatus` for server responses.
- `constants`: Defines shared constants like API endpoints and multipart
field names.
- `timestamp`: Provides utilities for handling RFC3339 timestamps,
including conversions to and from filesystem-safe strings.