791 lines
56 KiB
Markdown
791 lines
56 KiB
Markdown
# Current implementation state
|
||
|
||
## Private installation and isolated Sleep sport overlay
|
||
|
||
The private daily installation runs the explicit ordered Program rescheduling
|
||
rollout from `trainlog-program-recalage-v1` at `3a4ec34`, with desktop schema
|
||
v37, Android schema v34, and the capability-selected `trainlog-programs` V2
|
||
projection. The separate `trainlog-sleep-sport-overlay-v1` worktree starts from
|
||
that deployed source and adds the read-only completed-sport Sleep overlay.
|
||
Local source and isolated-browser validation do not by themselves establish
|
||
that the new overlay is served by the daily installation. Completed-session
|
||
calendar dates continue to come from their linked factual sessions.
|
||
Rollout packaging requires an explicit Meson `-Dweb=enabled` build; the
|
||
embedded frontend and its native Web tests are part of candidate validation.
|
||
|
||
Full-generation synchronization uses one generation/ACK business engine across
|
||
versioned transports. On the paired 0.1.4 deployment, automatic local selection
|
||
is Bluetooth Classic RFCOMM first and direct USB/MTP second. The desktop BlueZ
|
||
service accepts only the configured bonded Android address, Android proves the
|
||
persisted Trainlog peer identity in the first bounded frame, and reconnect noise
|
||
is rate-limited so proximity does not create repeated generations. MTP remains
|
||
the wired recovery path. The separately configured private Drive transport
|
||
remains available to its legacy mirror/fallback workflow. No transport copies
|
||
SQLite.
|
||
|
||
Automatic Bluetooth restart recovery preserves the immutable generation
|
||
identity across conversation-run changes and recognizes the exact original-run
|
||
ACK by immutable generation ID and manifest digest before full SQLite
|
||
validation. An unacknowledged branch is excluded
|
||
from the bounded admission count only when a different sibling has a durable,
|
||
ledger-backed consumed acknowledgement; the superseded bytes and row remain
|
||
retained. The desktop service installer retires the exact obsolete private
|
||
ExecStart override that omitted required runtime paths and installs both
|
||
`trainlog-syncd` and the BlueZ RFCOMM agent.
|
||
|
||
The daemon's durable arrival ledger distinguishes admission from terminal
|
||
consumption. If RFCOMM disappears after an Android request is admitted but
|
||
before the generation conversation can complete, an explicitly transient
|
||
terminal result leaves that same stable request eligible on reconnect. Success
|
||
and non-transient business failures remain consumed, while every retry receives
|
||
a fresh run identity and revalidates the existing idempotent generation/ACK
|
||
evidence.
|
||
|
||
Android synchronization paths share the same concurrency-guarded generation
|
||
coordinator as SyncScreen. That coordinator has one process-wide ownership
|
||
arbiter: explicit foreground intent has priority over automatic background
|
||
resumption, while a foreground service may remain alive without owning the
|
||
conversation. An automatic run yields cooperatively at safe boundaries; its
|
||
durable generation is retained for a correlated handoff rather than deleted.
|
||
A correlated desktop `rejected` ACK is also terminal for the Android producer:
|
||
it is retained as durable rejection evidence, then releases the conversation
|
||
lease immediately because no reciprocal desktop generation can follow that
|
||
rejection. This keeps an explicit Web request or a later automatic Bluetooth
|
||
request from being starved by an impossible post-rejection wait.
|
||
|
||
Direct-MTP publication now has an explicit Android visibility boundary.
|
||
Durable immutable generation files are submitted to MediaProvider and all scan
|
||
callbacks complete before the mutable generation reference is published; the
|
||
reference and Android ACK use the same confirmation. Bounded diagnostic logs
|
||
contain only run/generation identities, artifact names, phases and results.
|
||
The desktop worker performs lightweight local correlation checks continuously
|
||
but opens a full MTP pull no more than once every three seconds. A 90-second
|
||
individual adapter budget is distinct from the overall conversation deadline,
|
||
so an absent correlated generation reports a protocol wait timeout while an
|
||
actually blocked adapter reports `transport_timeout`.
|
||
|
||
The resumed private run `sy_9639f06e-58ba-4bc0-ab1b-1488aefe011c` received its
|
||
desktop request and archive-ACK files, captured generation
|
||
`gen_532da95e-713f-4163-acc1-d838673c33bf`, and published its immutable objects
|
||
and reference with manifest SHA-256
|
||
`170b55ad5bf3e44501fb506107cd26ab807d0179c95a5f1bf73f8251ea1a14e8`.
|
||
Filesystem and MTP exposed the same bytes. It then waited for a desktop ACK
|
||
that could not arrive because the daemon correctly refused to create another
|
||
conversation for the already-consumed request UUID. No causal or business
|
||
mutation occurred. The remaining defect was Android ownership: an automatic
|
||
resume could hold the old run while the explicit foreground attempt failed
|
||
before publishing a fresh request UUID. The arbiter and explicit run baseline
|
||
now prevent that state.
|
||
|
||
Existing preparation rows expose separate editing and Android-delivery states
|
||
and an accessible send action. Draft/local advances the same preparation to a
|
||
ready revision before delivery; ready/local delivers the current revision.
|
||
Completed history hides the active preparation exclusively through the
|
||
delivery's stable execution-session identity and retains all provenance.
|
||
Starting a delivered mixed preparation preserves continuous duration targets
|
||
and set/repetition/load/rest targets in the active singleton without creating
|
||
performed continuous activity, performed sets, MAX results, history, or
|
||
feedback before explicit user capture.
|
||
The active-session editor accepts target-only continuous and set-based
|
||
occurrences: opening an existing stable entry renders its prefilled form,
|
||
keeps the independent target intact on save, and leaves sibling occurrences
|
||
unchanged on save or cancellation. A set-based target-only occurrence can add
|
||
or change equipment without fabricating a performed set.
|
||
Session completion now also turns a completed target-only continuous timeline
|
||
into its measured performed duration when no supplemental field is required.
|
||
Program targets remain independent from facts, so a valid execution may contain
|
||
fewer or more sets and different repetitions or loads. A strictly session-bound
|
||
private recovery build can supply a past factual end without retiming exercise
|
||
markers or immutable heart-rate samples.
|
||
|
||
The full-generation path is operational on the paired daily installation.
|
||
Bluetooth Classic is the primary local byte transport, MTP remains the wired
|
||
recovery transport, and the existing private Drive namespace remains separately
|
||
configured for its supported mirror/fallback path. All carry the same bounded
|
||
generation/ACK artifacts and persistent peer identities; cross-transport replay
|
||
is idempotent and no transport carries SQLite. The Bluetooth transport does not
|
||
yet ingest heart-rate sensor data.
|
||
|
||
The public Android release chain advances from v0.1.1 versionCode 2 through
|
||
v0.1.2 versionCode 3, v0.1.3 versionCode 4, v0.1.4 versionCode 5 and v0.1.5
|
||
versionCode 6 to v0.1.6 versionCode 7 under certificate SHA-256
|
||
`5ef41117da107c9805ae3215c5ec823cff06216610e6e4f7542a754161c71bd2`.
|
||
Higher-numbered private validation builds used a separate Android Debug
|
||
certificate and are not members of the public update-signature chain.
|
||
|
||
`TRAINLOG_NATIVE_BUILD_AND_CANDIDATE_V1` closes the native warning gate. Strict
|
||
GCC 16.2.1 and Clang 22.1.8 builds now pass the same 81-test inventory, and the
|
||
Clang ASan/UBSan build passes it with leak detection. Bounded path assembly and
|
||
the Web sync accepted response reject insufficient capacity without publishing
|
||
partial output. Fresh desktop, current-Android and schema-v17 bridge candidates
|
||
were rebuilt from code commit `dd8c91a946a9c836965818d8cc35b8f8bce74658`.
|
||
The rollout packager now keeps the legacy request daemon and one-shot helper in
|
||
the same relocatable, hashed bundle as the Web generation coordinator, avoiding
|
||
mixed-checkout user-service execution during the controlled rollout.
|
||
|
||
Snapshot date: **2026-09-25**.
|
||
|
||
`TRAINLOG_CODE_READABILITY_V1` normalizes the recent generation-MTP,
|
||
generation/ACK, Android-backup, Web Dashboard serialization, tests, doubles and
|
||
packaging workflows without changing their contracts. The scoped C formatter
|
||
check is canonical for those files only; it does not claim that the historical
|
||
repository has been globally reformatted.
|
||
|
||
This document owns the current implemented state. Historical checkpoints and
|
||
closed incidents belong in [reviews](reviews/) and the
|
||
[changelog](../CHANGELOG.md).
|
||
|
||
## Product boundary
|
||
|
||
Android is the field companion: it captures training and body data, preserves
|
||
the active draft, receives AI proposals, triggers synchronization, and shows
|
||
quick summaries. The C17/Notcurses TUI is the administration, inspection,
|
||
maintenance, import/export, and technical-tooling surface. The local Web
|
||
sibling provides its embedded application shell, Dashboard, Sessions,
|
||
Programmes, Exercises, factual Analyse views, and Sleep Diary
|
||
workflow through Trainlog Core.
|
||
|
||
Trainlog Core owns business truth. The desktop SQLite database remains the
|
||
canonical local source of truth and long-term history; no interface owns a
|
||
parallel implementation of its rules.
|
||
|
||
## Versions and compatibility
|
||
|
||
| Boundary | Current state |
|
||
|---|---|
|
||
| Frozen project exchange | `TRAINLOG_FORMAT_V1=PASS/FROZEN` |
|
||
| Desktop SQLite | schema v36; v36 makes Sleep revision payload tables append-only; v35 gives every Sleep medication intake a structured `quantity` (1..99), defaulting historical rows to 1 |
|
||
| Android SQLite | schema v33; v33 makes Sleep revision payload tables append-only; v32 gives every Sleep medication intake a structured `quantity` (1..99), defaults historical rows to 1, and records a distinct quick-publication tip |
|
||
| Mobile snapshot | V3 active; V1/V2 readable legacy inputs; explicit V4 codec staged, not selected by transport |
|
||
| Desktop terminal backend | Notcurses only |
|
||
| Trainlog product version | `0.1.7` bugfix-only development, synchronized across Android and desktop; latest stable release: `v0.1.6` |
|
||
| Interface language | `TRAINLOG_I18N_V0_1_1=PASS`: French default; English selectable in Settings → Language on both surfaces |
|
||
| AI history export | `TRAINLOG_AI_EXPORT_V1` active |
|
||
| AI session proposals | `TRAINLOG_AI_SESSION_DRAFT_V1=VALIDATION_PENDING` |
|
||
| Training knowledge | `TRAINING_KNOWLEDGE_V1=PASS` |
|
||
| Session generator | `SESSION_GENERATOR_V1=PASS`, hidden pending V2 |
|
||
| Application shell | `APP_SHELL_V1=IMPLEMENTED_AWAITING_VISUAL_REVIEW_2` |
|
||
| Statistics | `STATS_V1=IMPLEMENTED` |
|
||
| Dashboard characterization | `WEB_DASHBOARD_CHARACTERIZATION_V1=PASS/FROZEN` |
|
||
| Dashboard Core read model | `WEB_DASHBOARD_CORE_READ_MODEL_V1=PASS/FROZEN` |
|
||
| TUI Dashboard adoption | `WEB_TUI_READ_MODEL_ADOPTION_V1=PASS/FROZEN` |
|
||
| Web CLI/HTTP infrastructure | `WEB_CLI_HTTP_INFRASTRUCTURE_V1=PASS/FROZEN` |
|
||
| Web frontend shell | `WEB_FRONTEND_SHELL_V1=PASS/FROZEN` |
|
||
| Web Dashboard grid | `WEB_DASHBOARD_GRID_V1=PASS/FROZEN` |
|
||
| Web Dashboard layout | `WEB_DASHBOARD_LAYOUT_V1=PASS/FROZEN` |
|
||
| Web Dashboard tiles | `WEB_DASHBOARD_TILES_V1=PASS/FROZEN` |
|
||
| Web Dashboard visualizations | `WEB_DASHBOARD_VISUALIZATIONS_V1=PASS/FROZEN` |
|
||
| Web Dashboard V1 | `WEB_DASHBOARD_V1=PASS/FROZEN` |
|
||
| Sleep Diary V1/V2 | `SLEEP_DIARY_V1=PASS` remains frozen and readable. The active Sleep exchange requires `sleep-diary-v2` / `sleep-diary-v2.json`: structured per-intake quantity and quick-publishable current revisions supplement the existing causal diary model without changing V1. Distinct concurrent V2 edits with comparable user-edit instants retain the complete latest snapshot and both immutable histories; ambiguous ordering and unsafe deletion/envelope changes still reject. |
|
||
| Web Sessions V1 | `TRAINLOG_WEB_SESSIONS_V1=PASS/FROZEN` (controlled desktop/Android deployment validated) |
|
||
| Web Sessions deletion and Programs V1 | `TRAINLOG_WEB_SESSIONS_DELETE_AND_PROGRAMS_V1=PASS` (private grouped rollout validated) |
|
||
| Programs presentation, Android projection, and deletion | `TRAINLOG_PROGRAMS_PRESENTATION_ANDROID_DELETE_V1=PASS` (private coordinated deployment and restart/replay validated) |
|
||
| Program execution flow | `TRAINLOG_PROGRAM_EXECUTION_FLOW_V1=PASS` (private coordinated deployment, real execution, correlated ACK, restart, replay, and deletion validated) |
|
||
| Web history deletion incident | `TRAINLOG_HISTORY_DELETE_HTTP500_FIX_V1=PASS` (private rollout, correlated full-generation exchanges, restart, retained-generation replay, and non-resurrection validated) |
|
||
| Sync orchestrator/report V1 | `TRAINLOG_SYNC_ORCHESTRATOR_REPORT_V1=PASS/FROZEN` |
|
||
| Web sync API V1 | `TRAINLOG_WEB_SYNC_API_V1=PASS/FROZEN` |
|
||
| Web sync button V1 | `TRAINLOG_WEB_SYNC_BUTTON_V1=PASS/FROZEN` |
|
||
| Sync Web end-to-end wrapper | `TRAINLOG_SYNC_WEB_END_TO_END_V1=PASS/FROZEN` (isolated) |
|
||
| Private full-generation rollout | `TRAINLOG_SYNC_FINALIZATION_AND_ROLLOUT_V1=PASS` (authenticated Bluetooth primary transport with direct MTP recovery on one authorized phone) |
|
||
| Local Web | `TRAINLOG_WEB_V1=CONTRACT_FROZEN / IMPLEMENTATION_STARTED` |
|
||
| Web Exercises V1 | `TRAINLOG_WEB_EXERCISES_V1=PASS` |
|
||
| Cardio persistence/sync | `TRAINLOG_CARDIO_PERSISTENCE_SYNC_V1=PASS`: Android v27 + desktop v30, strict optional `trainlog-heart-rate` V1 Android→desktop companion, generation relation and correlated ACK |
|
||
| Recovered session HR tail | `TRAINLOG_HR_TRUNCATE_AFTER_SESSION_END_V1=PASS`: delayed Android finalization truncates BPM/RR strictly after the inclusive factual end, and the separate `trainlog-heart-rate-corrections` V1 companion reuses causal state without widening frozen causal-deletions V1. The paired-device recovery for `se_fa845379-794d-4bb6-8cd3-48187ebbd134` retained 3,429 BPM/RR facts through 11:16; two successive full MTP exchanges received consumed ACKs, and replay of the original 4,919-sample Heart Rate V1 artifact did not resurrect the tail. |
|
||
| Cardio BLE diagnostics | `TRAINLOG_CARDIO_BLE_DIAGNOSTICS_V1=PASS`: real CYCPLUS H2 validated on Android; `0x180D` + `0x2A37`, Battery Service, approximately 1 Hz notifications and one raw RR interval per observed notification; contact and energy fields absent in the observed payloads |
|
||
| Cardio persistent acquisition | `TRAINLOG_CARDIO_PERSISTENT_ACQUISITION_V1=PASS`: selected sensor persists, dedicated connected-device foreground service reconnects, stale BPM is cleared, and the global Android `♥ BPM` indicator is grey/orange/green by connection state; real CYCPLUS restart and Bluetooth-off/on recovery validated on private vc40 |
|
||
| Training timeline | `TRAINLOG_CARDIO_TRAINING_TIMELINE_V1=PASS`: real session bounds reuse canonical lifecycle timestamps; exercise start/end are explicit user actions; live BPM samples snapshot the active stable `entry_id`; session finalization refuses an unclosed exercise; timeline sync is Android→desktop idempotent |
|
||
| Sleep Android one-tap | `TRAINLOG_SLEEP_ANDROID_ONE_TAP_V1=PASS`: one-tap Couché / medication / Réveil / Levé, guarded 10 s undo, correction route, and sleep-entry-owned cardio capture remain intact. Réveil now creates a structured 30-minute `long_awake` window, repeated taps extend it, and Levé clamps it. The validated V2 development delta additionally creates/reuses one 18:00-to-18:00 pending entry for pre-bed medication, reuses its stable `entry_id` at Couché, and exposes a compact quantity control. |
|
||
| Cardio session type | `TRAINLOG_CARDIO_SESSION_TYPE_V1=PASS`: Android v29 + desktop v32 use canonical `session_kind=cardio`; frozen mobile/history and draft formats keep legacy training/max_test; dedicated `trainlog-cardio-sessions` V1 syncs completed cardio sessions Android→desktop idempotently |
|
||
| Cardio calibration | `TRAINLOG_CARDIO_CALIBRATION_V1=PASS`: reserved system Calibration cardio exercise, real measured observed peak, ≥3 min recovery with bounded +1/+2/+3 min factual samples, immutable profile and Android→desktop calibration companion |
|
||
| Cardio BPM guidance | `TRAINLOG_CARDIO_BPM_GUIDANCE_V1=PASS`: absolute/calibration-derived immutable BPM targets, 2 BPM hysteresis, 3 s confirmation, stale-signal suspension, durable phase/condition history, Android live guidance UI and strict Android→desktop `trainlog-cardio-guidance` V1 sync |
|
||
| Cardio Web visualization | `TRAINLOG_CARDIO_WEB_VISUALIZATION_V1=PASS`: one bounded factual heart-rate timeline read model/HTTP endpoint powers Web History and Sleep with raw BPM/RR, real session/exercise/Sleep markers, guided targets/instructions and calibration facts; no sleep-stage or medical inference |
|
||
| Sleep tonight readiness delta | `TRAINLOG_SLEEP_TONIGHT_CLOSEOUT_V1=PASS`: Meson 111/111, import checks 6/6, Android `testDebugUnitTest` plus `assembleDebug`, Web 191/191 plus typecheck/build, exact V2 quantity replay, private vc47 schema-v32 migration, fresh CYCPLUS BPM and non-mutating Android/Web visual review. |
|
||
| 0.1.5 validation | `TRAINLOG_0_1_5_VALIDATION_V1=PASS`: native 111/111; Android unit-test task and APK build; Web 191/191 plus typecheck/build; JSON/import, C-format and diff checks pass. Private vc47 preserves real data through schema v32, renders pre-bed medication quantity controls, reconnects the CYCPLUS and presents the multi-night Web Sleep agenda. |
|
||
| Tomorrow session readiness | `TRAINLOG_TOMORROW_SESSION_CARDIO_READINESS_V1=PASS`: the private vc46 is clean on schema v31 with fresh CYCPLUS BPM and the next Program session is unambiguous; one combined regression proves two ordered occurrences loaded from the synchronized Program, Program provenance, factual timeline/HR ownership, finalization, full generation, desktop import, replay and correlated ACK |
|
||
| 0.1.6 validation | `TRAINLOG_0_1_6_VALIDATION_V1=PASS`: native Meson 114/114 and Clang ASan/UBSan 114/114; Android 317 tests (312 passed, five documented optional skips), debug APK and signed release APK; Web 32 files / 209 tests plus typecheck/build; JSON/import, changed-C formatting and diff checks pass. |
|
||
| Current operational cursor | 0.1.7 stabilization is open after immutable stable v0.1.6. Only observed bug fixes, regression fixes, directly related small UI corrections, documentation corrections, robustness, and associated tests are in scope. |
|
||
| Sleep awake/selection/PDF refinement | `TRAINLOG_SLEEP_AWAKE_SELECTION_PDF_V1=PASS`: one temporal projection drives agenda and selected HR detail; a separate inclusive night-start date range drives preview/export from the loaded period. The agenda axis and rows share 24 exact hour columns; estimated sleep duration is consistent and remains non-persistent. |
|
||
| Sleep Réveil interval policy | Android Réveil creates a revisioned 30-minute `long_awake` window; repeated taps extend it and Levé clamps it. The real 2026-09-23 night is causally repaired on Android/desktop and renders two orange intervals in agenda, HR and PDF; Android 307 passed plus five optional skips, and Web 208/208 passed. |
|
||
| Sleep revision collision recovery | `TRAINLOG_SLEEP_REVISION_COLLISION_RECOVERY_V1=PASS`: one unpublished divergent desktop tip was re-identified without payload loss, the immutable Android identity was restored exactly, and a fresh successor retained the non-conflicting union. The original generation was consumed with a full SQLite ACK; the real 24/09 session, Sleep facts, cardio samples, timeline, feedback, and Program provenance are visible on Desktop/Web. Schema v36/v33 and importer payload checks prevent recurrence. |
|
||
| Complete synchronization gap contract | Frozen dependency contract; operational USB/Drive slices required by v0.1.2 are delivered |
|
||
| Isolated synchronization test environment | `TRAINLOG_SYNC_TEST_ENV_V1=PASS/FROZEN` |
|
||
| Synchronization characterization | `TRAINLOG_SYNC_CHARACTERIZATION_V1=PASS/FROZEN` |
|
||
| Synchronization data/lifecycle slice | `TRAINLOG_SYNC_DATA_LIFECYCLE_V1=PASS/FROZEN` |
|
||
| Synchronization causal deletion slice | `TRAINLOG_SYNC_CAUSAL_DELETE_V1=PASS/FROZEN` |
|
||
| Synchronization generation/ACK slice | `TRAINLOG_SYNC_GENERATION_ACK_V1=PASS/FROZEN` (explicit staged entry points; active transport remains V3) |
|
||
| Bluetooth synchronization transport | `TRAINLOG_SYNC_BLUETOOTH_TRANSPORT_V1=PASS/FROZEN`; independent from future heart-rate sensor ingestion |
|
||
| Generation MTP and Android backup | Software complete and retained as the authorized wired recovery path |
|
||
|
||
Desktop and Android schema numbers are independent. Neither changes the frozen
|
||
Trainlog JSON V1 contract.
|
||
|
||
The active Sleep V2 delta keeps the Web agenda period-owned: every returned
|
||
night has one row on the shared 18:00-to-18:00 geometry. Selecting a row is
|
||
presentation-only and atomically drives the detail dates, editor content, and
|
||
full-width heart-rate detail; it performs no database mutation. PDF preview
|
||
and download instead share an explicit inclusive range over night start dates,
|
||
initialized to the loaded agenda bounds and independent of detail selection.
|
||
The completed-sport overlay reads factual `sessions.started_at` and `ended_at`
|
||
from desktop history through a read-only, period-bounded paginated Web endpoint.
|
||
Each row uses its one Sleep-owned 18:00 offset and 24 elapsed-hour window for
|
||
Sleep, medication, and sport in both Web and vector PDF. Valid sport intervals
|
||
intersect `[start,end)` and are clipped only for display; missing or invalid
|
||
ends are point markers without invented duration. The Web keeps a separate
|
||
blue lane and accessible session list. Preview and download wait for the
|
||
complete sport read and use the same selected sessions. The PDF prints blue
|
||
outlined bands and paginated names, factual times, and durations, marking
|
||
sessions without a corresponding Sleep row as `sleep not recorded`. Sport
|
||
creates no Sleep event and changes no sleep total, session, schema, or sync
|
||
contract. The HR detail's rise/fall overlays are descriptive relative measured
|
||
variation only (five-minute trailing reference excluding the current 60-second
|
||
window; threshold `max(10 BPM, 15%)`; gaps over three seconds excluded). They
|
||
are not sleep events, sleep stages, causes, readiness, or medical inference.
|
||
The detail shares one normalized time projection with declared sleep and
|
||
awakening intervals, exact Couché/Réveil/Levé markers, medication markers, and
|
||
full-hour ticks between exact visible bounds. Medication presentation retains
|
||
unit dose and quantity as separate facts. A missing declared sleep interval
|
||
uses only a labeled, non-medical Web estimate from Couché plus 45 minutes to
|
||
Levé minus 10 minutes when both bounds exist; it never changes canonical Sleep
|
||
data. Agenda, selected-night graph, and ranged PDF consume the same projection of
|
||
sleep intervals, every structured long-awake interval, point events, medication
|
||
intakes, and bounds. The agenda renders an estimated interval with distinct
|
||
hatching, never substitutes it for a factual interval, and retains the exact
|
||
shared 18:00-to-18:00 geometry, reinforced by 24 equal hour columns shared by
|
||
every row, with each hour label centered inside the cell it names. Displayed
|
||
events use one factual night offset even when revision metadata is UTC, and
|
||
French agenda headings use `DD/MM/YYYY`. Displayed sleep duration is the sum of factual
|
||
sleep intervals or, in their absence, the displayed estimate minus overlapping
|
||
structured awakenings. The overlays remain available without an HR capture and
|
||
do not fabricate BPM.
|
||
|
||
Language is device-local presentation state. Android keeps it in dedicated
|
||
SharedPreferences; desktop keeps it in the XDG configuration file
|
||
`trainlog/presentation.conf`. A successful selection updates the UI
|
||
immediately. It does not enter either SQLite database, training data, stable
|
||
IDs, schemas, exchange artifacts, synchronization protocols, AI proposals,
|
||
MAX, or feedback. User-defined exercise and catalogue names are never
|
||
automatically translated. Where a recognized stable body-zone ID has a local
|
||
presentation label, only that label is localized; unknown IDs retain their
|
||
catalogue label. Numbers, dates, and other visible formatting are owned by the
|
||
selected presentation language.
|
||
|
||
Synchronization remains a protocol boundary: core, receipt, and persisted
|
||
history summaries stay opaque operational bytes. Android and the TUI render
|
||
their current synchronization state from local typed status and counters; they
|
||
never inject a raw cross-device French summary into the visible interface. No
|
||
raw exchange or protocol format changed for interface language support.
|
||
|
||
Schema v19/v18 now persists enriched history (`ended_at`, causal session,
|
||
occurrence and observation notes, and observation-to-session links), stable
|
||
execution-draft identities, bounded pending drafts and finalization ledgers.
|
||
The explicit `trainlog-mobile-export` V4 and `trainlog-execution-drafts` V1
|
||
codecs are validation-stage entry points only. The active sync engine and
|
||
Android inbox/outbox still select V3 and do not move the draft artifact.
|
||
|
||
## Storage and synchronization
|
||
|
||
- Android stores capture data in its private SQLite database.
|
||
- Android reads and writes exchange artifacts only in
|
||
`/storage/emulated/0/Documents/Trainlog` after the user grants all-files
|
||
access through system settings.
|
||
- The old SAF/tree-URI path and `Download/Trainlog` are historical recovery
|
||
boundaries, not active storage choices.
|
||
- Desktop synchronization uses direct libudev/libmtp discovery and object
|
||
access. No GVFS/FUSE mount is required.
|
||
- `trainlog_sync_run()` remains the strict legacy V3 engine shared with the
|
||
TUI and compatibility tooling. When the trusted full-generation
|
||
configuration is present, `trainlog-syncd` admits the Android request into
|
||
the existing `sync_orchestrator.py`/`sync_peer_worker.py` conversation;
|
||
configuration absence retains the legacy fallback.
|
||
- The desktop imports Android snapshots, publishes catalog/profile/feedback/AI
|
||
companions, and returns a receipt. Stable IDs, artifact idempotence and the
|
||
few domain-specific explicit states make defined replay paths idempotent.
|
||
Explicit staged generation services now provide coherent V4/companion
|
||
capture, immutable manifests, whole-generation SQLite consumption and
|
||
durable peer ACKs. The Android foreground trigger now publishes a complete
|
||
generation by default. A trusted desktop configuration selects the bounded
|
||
production generation MTP adapter; absent configuration leaves V3 as the
|
||
compatibility fallback.
|
||
- SQLite database files are never synchronized.
|
||
- The desktop AI flow uses external `rclone` for Drive inbox/archive and
|
||
read-only history export. Android owns no Drive credentials.
|
||
|
||
The frozen [complete synchronization gap contract](design/sync_gap_contract_v1.md)
|
||
remains the authority for the later orchestrator. Mobile V3 still omits
|
||
`ended_at`, session/occurrence/body-observation notes, the body-observation to
|
||
session link and the active Android draft. Legacy imports remain transactional
|
||
per artifact. Explicit generation consumption instead validates every listed
|
||
artifact, applies all domains and its consumption record in one transaction,
|
||
and emits `consumed` only after commit. A legacy receipt remains distinct from
|
||
the correlated generation ACK.
|
||
Characterization now also freezes the observed preservation of local-only
|
||
session/body values on an identical replay, loss of an occurrence-local note
|
||
when V3 correction reconstructs that occurrence, delete-before-send exposure,
|
||
request replay/receipt marking, and real inter-process lock contention. These
|
||
are current-behavior proofs, not lifecycle repairs.
|
||
|
||
## Android
|
||
|
||
Android currently provides:
|
||
|
||
- metadata-driven SETS+REPS, SETS+DURATION, and CONTINUOUS+DURATION capture;
|
||
- actual per-set values, optional occurrence equipment, and explicit MAX rows;
|
||
- exactly one durable active session draft with identity-preserving continuous
|
||
multi-position drag reorder,
|
||
resume, confirmed discard, and atomic completion;
|
||
- complete scrollable equipment selection and caller-aware creation from an
|
||
active occurrence editor, with the explicit catalogue-created ID selected on
|
||
return and the occurrence identity, plan, raw form and siblings retained for
|
||
in-place save;
|
||
- completed-session consultation, transient drag reorder, and atomic factual or
|
||
order correction while retaining session/occurrence/feedback identities for
|
||
synchronization;
|
||
- exercise catalogue editing with stable `exercise_id` and referenced-profile
|
||
protection;
|
||
- supplied/custom equipment and machine-exercise metadata;
|
||
- body measurements and recent BODY ZONES summaries;
|
||
- immediate exercise feedback, immutable wording revisions, and J+1/session
|
||
follow-ups;
|
||
- pending AI proposal collection, explicit start/delete, and durable tombstones;
|
||
- 7/30/90-day, year, and all-history quick statistics;
|
||
- direct-storage snapshot publication, sync triggering, locally rendered typed
|
||
receipt status/counters, and companion application.
|
||
- French-default/English-selectable presentation from **Settings → Language**,
|
||
applied immediately without changing repository or synchronization state.
|
||
|
||
Android does not own canonical long-term analytics. The V1 session generator is
|
||
implemented but intentionally hidden from normal navigation pending V2.
|
||
|
||
## Desktop TUI
|
||
|
||
The desktop is a strict C17 application using Notcurses. It currently provides:
|
||
|
||
- a keyboard-first seven-section shell, semantic true-color roles, UTF-8 input,
|
||
resize recovery, and a 72x20 minimum-terminal fallback;
|
||
- session creation, history, detail, and transactional correction;
|
||
- exercise and equipment catalogue management;
|
||
- explicit measured-MAX consultation and working-load calculations;
|
||
- body observation history, correction, derived analytics, and longitudinal
|
||
graphs;
|
||
- statistics derived from canonical occurrence-owned actual work, including
|
||
exact calendar-week/month buckets, performance, body, frequency, exercise,
|
||
and BODY ZONES views;
|
||
- detailed locally rendered typed synchronization status/history through the
|
||
shared engine.
|
||
- French-default/English-selectable presentation from **Settings → Language**,
|
||
including selected-language formatting without changing canonical data.
|
||
|
||
Rendering is not permitted to own SQL or business rules. Statistics are
|
||
read-only projections; they are not persisted as facts. The former Dashboard
|
||
exception is resolved: its typed Core read model owns the SQLite projection and
|
||
all characterized calculations, while the TUI supplies query state and renders
|
||
the result. The Web aggregate now exposes the distinct factual snapshot through
|
||
`GET /api/v1/dashboard` without changing the frozen TUI projection.
|
||
|
||
## Local Web
|
||
|
||
The 0.1.6 refinement adds a persistent French/English preference and a dedicated
|
||
Settings route without moving the global synchronization action. Dashboard
|
||
activity is grouped by calendar month/ISO week, its progression tile is now a
|
||
global factual synthesis, and Analyse progresses from primary BODY ZONE to
|
||
exercise to individual detail. Exercise work time is derived exclusively from
|
||
`session_exercise_timeline` start/end facts; missing timelines remain unknown.
|
||
Measurements first expose unit-safe relative change cards before real-unit
|
||
detail. Dashboard and Analyse reuse the same human-proportioned BODY ZONES SVG.
|
||
|
||
The Dashboard layout keeps a 12-column canonical grid but permits every card
|
||
to reach full width and several useful heights; per-card minima, overlap
|
||
validation, exact save/cancel/reset semantics, and keyboard resizing remain
|
||
authoritative. Settings and synchronization form the rightmost Header action
|
||
cluster. Program progress uses the shared success color while retaining its
|
||
factual completed/total text. Responsive charts preserve their viewBox aspect
|
||
ratio, and Analyse explicitly distinguishes unavailable exercise timing from
|
||
partial measured coverage instead of deriving time from sets or repetitions.
|
||
The shared Sleep agenda owns one label/track/facts grid. Its 25 exact hourly
|
||
positions and every event, interval and medication marker use the same
|
||
coordinates: 18:00 at 0%, 00:00 at 25%, 06:00 at 50%, 12:00 at 75%, and 18:00
|
||
at 100%. Ordinary desktop widths show the 13 two-hour labels, wide layouts show
|
||
all hours, and narrow layouts retain the five six-hour anchors without changing
|
||
the underlying geometry.
|
||
|
||
The Equipment route projects supplied and custom definitions with exercise,
|
||
history, preparation and Program reference counts. Its guarded merge accepts a
|
||
custom duplicate only, transfers relational references in one SQLite IMMEDIATE
|
||
transaction, preserves occurrence/MAX facts indirectly through occurrence
|
||
identity, and writes the existing causal deletion boundary. An immutable
|
||
execution-draft JSON reference rejects the operation rather than being edited.
|
||
Browser mutation identities use the shared UUIDv4 helper backed by
|
||
`crypto.getRandomValues()`; no Web mutation depends on `crypto.randomUUID()`,
|
||
so `http://trainlog.perf` remains supported.
|
||
|
||
An `execution_drafts` row marked active but containing no `exercises` or
|
||
legacy `entries` is an orphaned projection, not a resumable workout. Web may
|
||
delete that exact row through the existing revision-guarded causal transaction;
|
||
the durable tombstone prevents synchronization replay from resurrecting it.
|
||
An active draft with any occurrence remains protected from Web deletion.
|
||
|
||
Generation admission archives only rows backed by an exact correlated consumed
|
||
ACK and retains the newest two acknowledged lineage members. Legacy terminal
|
||
Android rows that predate the durable ACK ledger are neither fabricated nor
|
||
deleted, but no longer consume the active admission window. Captured,
|
||
published, waiting-ACK, and current acknowledged rows continue to count until
|
||
safe archival, so capacity recovery never prunes unacknowledged evidence.
|
||
|
||
`TRAINLOG_WEB_V1` architecture, API independence, local-network boundary,
|
||
browser shell, Dashboard, layout ownership, build/runtime separation, and
|
||
security invariants are canonical. The CLI/local HTTP infrastructure and
|
||
embedded frontend are implemented: `-w`/`--web`, optional `--port`, exact IPv4
|
||
loopback binding, `/api/v1/health`, React navigation, and the persistent
|
||
Header/Body/Footer shell. Production assets are generated from the npm lockfile
|
||
and linked into the binary. The read-only Dashboard uses one typed Core
|
||
snapshot, a seven-tile responsive grid, factual size-adaptive tiles, an ECharts
|
||
progression series, an original BODY ZONES SVG, and private versioned layout
|
||
persistence with optimistic conflict and CSRF/Origin protection. It provides no
|
||
automatic browser launch. Sessions provides complete paged preparation,
|
||
resume and history views, stable details, optimistic manual-preparation writes,
|
||
explicit proposal derivation and generation-backed Android delivery. The
|
||
top-level `/programmes` route is an operational daily active-Program calendar;
|
||
Sessions → Programmes remains the technical administration/import, list,
|
||
detail, archive, and delete surface. `/exercices` is the operational desktop
|
||
catalogue administration surface. `/analyse` is implemented with Overview,
|
||
Exercises, Distribution, and Measurements sections over one bounded Core read
|
||
model. Its global 7/30/90-day/all filter is applied in the backend; Dashboard
|
||
uses the same 30-day calculations for activity, active-Program progress,
|
||
exercise progression, measurements, and BODY ZONE links.
|
||
|
||
The Analyse API validates period, exercise identity, measurement metric and
|
||
page, rejects unknown or duplicate parameters, and bounds exercises and chart
|
||
points. Missing facts remain null. Exercise output respects persisted
|
||
tracking/load modes, explicit MAX rows are never estimated, and duration,
|
||
distance, speed, external load, and external volume appear only when their
|
||
source observations support them. BODY ZONES are labelled as recorded primary
|
||
zone exposures/associated sets rather than physiological percentages. All
|
||
fourteen stored body metrics expose period-first, period-last, and an absolute
|
||
delta only with at least two points. The route and Dashboard deep links have
|
||
safe invalid-query fallbacks, French/English labels, keyboard controls, and
|
||
responsive no-overflow layouts. Visual inspection remains pending before any
|
||
freeze marker.
|
||
|
||
Exercises uses bounded Core-owned list/detail reads, canonical name
|
||
normalization, BODY ZONES taxonomy, profiled creation/update commands and
|
||
causal retirement. Its deterministic optimistic token covers all editable
|
||
catalogue state. Built-in identities cannot be retired, equipment associations
|
||
are presented read-only, and profile edits affect future use only: occurrence
|
||
snapshots, performed sets, feedback and MAX history are never rewritten. A
|
||
successful mutation changes only the canonical desktop database; the existing
|
||
USB-priority, Drive mirror/fallback synchronization later carries the existing
|
||
catalogue/profile/BODY ZONES/causal artifacts. No Web mutation invokes sync.
|
||
|
||
The calendar uses the existing typed Core services `fetchAllPrograms(active)`,
|
||
`fetchProgram`, and `createPreparationFromProgram`; it derives no local program
|
||
or execution state. With no active Program it shows an empty state and
|
||
navigation to Sessions administration; with one it selects it automatically;
|
||
with several it offers a non-persisted selector. Its continuous Monday–Sunday
|
||
weeks derive from Program bounds, imported `planned_for` dates, and the actual
|
||
`sessions.started_at` instant for linked completed executions, presented on the
|
||
same browser-local civil day as Web session history. The Program detail Web API
|
||
adds the nullable read-only `execution_started_at` projection field; the
|
||
Programs synchronization artifact and its strict fields are unchanged. A completed
|
||
session moved from its imported date shows both dates without changing history
|
||
or Program identity. Several cards may share a day; rest days contain no
|
||
invented session. A completed execution with no valid history timestamp stays
|
||
undated rather than appearing to have occurred on its planned day. Unfinished
|
||
sessions still show their imported dates because no durable current-plan or
|
||
ceded-optional-slot state exists in the stabilized Programs exchange. It shows
|
||
all five Core execution states; only `todo` offers Prepare. Each Prepare result
|
||
is followed by an authoritative Core reread and may expose the returned
|
||
preparation link. This calendar adds no Android delivery, start, or execution
|
||
behavior.
|
||
|
||
Desktop schema v26 owns immutable manual-preparation revisions, stable ordered
|
||
occurrences, persistent HTTP idempotency keys, delivery-to-execution identity,
|
||
and durable revision-bound withdrawal records. Android schema v24 stores
|
||
received preparations and permanent withdrawal results separately from AI
|
||
proposals and the active singleton. Pending deliveries are cancelled by an
|
||
exact V2 withdrawal; started executions are preserved. An exact correlated ACK
|
||
is required before delivery or withdrawal is shown as acknowledged.
|
||
|
||
Sessions and the prepared-items Dashboard projection now share descending
|
||
planned-date presentation with timestamp and stable-identity tie-breaks. Web
|
||
dates default to French presentation, with an installation-local versioned ISO
|
||
preference outside business SQLite and synchronization. Contextual French state
|
||
labels and persisted proposal provenance replace raw enums and digest-only
|
||
presentation. The preparation detail exposes a protected, idempotent **Delete
|
||
preparation** action; withdrawn rows remain technically inspectable but cannot
|
||
be edited or delivered.
|
||
|
||
Sessions rows expose independent right-edge trash actions and an accessible
|
||
destructive confirmation dialog. Proposal withdrawal preserves derived
|
||
preparations and is propagated by `trainlog-ai-session-drafts` V2; inactive
|
||
execution drafts and completed sessions use revision-guarded causal deletion.
|
||
Active or concurrently changed drafts conflict instead of being erased.
|
||
|
||
Program V1 is desktop-owned planning data in schema v26. The responsive
|
||
Sessions Programs subtab provides cards and timeline detail backed by real
|
||
imported definitions, sessions, usage, and provenance; it keeps strict bounded
|
||
preview/commit import and deterministic replay/conflict behavior. A discrete,
|
||
accessible trash action uses confirmation, focus restoration, and Escape-safe
|
||
cancel semantics. Active and archived programs may be terminally logically
|
||
deleted; sources and derived preparations remain, while deleted programs cannot
|
||
resurrect or reprepare and archived programs cannot prepare.
|
||
|
||
Android schema v24 contains a read-only synchronized Programs projection with
|
||
program, session, entry, and deletion tables. Its Sessions list/detail has no
|
||
program mutation actions. The optional staged controlled-generation
|
||
`trainlog-programs` V1 companion publishes a PC-to-Android full nondeleted
|
||
snapshot plus unacknowledged tombstones under capability `programs-v1`; a
|
||
durable correlated ACK records acknowledgment. It changes neither
|
||
`TRAINLOG_FORMAT_V1`, mobile V3, catalog exchange, nor preparations.
|
||
|
||
Android schema v25 makes those projected sessions executable through the
|
||
existing durable singleton. Stable paired Program provenance survives draft
|
||
restart and completion. The optional Android-to-desktop
|
||
`trainlog-program-executions` V1 companion records `in_progress` or `completed`
|
||
against desktop schema v27 without transferring ownership of Program
|
||
definitions. Web and Android derive their session state from these persisted
|
||
identities; no display-field heuristic is used.
|
||
|
||
Android is capture-first: its drawer no longer exposes Statistics, and Home is
|
||
limited to capture, recent BODY ZONES exposure, synchronization, and body
|
||
measurements. A shared compact card/button system covers Sessions, Programs,
|
||
drafts, exercise catalog/detail/editing, synchronization, Settings, and MAX
|
||
presentation. Program list/detail surfaces show completed counts and explicit
|
||
todo/in-progress/completed states; the active editor uses exclusive side-by-
|
||
side session-type controls and compact two-column set cards. These are
|
||
presentation changes only and do not alter schemas or exchange artifacts.
|
||
Web Program administration retains revision-guarded deletion and now presents
|
||
adaptive cards plus an explicit completed-session progress indicator.
|
||
The full Android lint comparison against an isolated clean `main` worktree is
|
||
unchanged at ten pre-existing errors: one `NewApi`, one
|
||
`LocalContextConfigurationRead`, and eight bilingual `StringFormatMatches`
|
||
diagnostics. This UI tranche adds no lint error and changes no lint policy.
|
||
The stable Android v0.1.6 tag uses `versionName=0.1.6` and public versionCode 7.
|
||
The v0.1.7 stabilization source starts at `versionName=0.1.7` and versionCode 8.
|
||
Private validation builds may still supply only a strictly increasing
|
||
`versionCode` as a build-time override; that does not create a public release
|
||
or change schemas and protocols.
|
||
|
||
The private Program execution rollout installed desktop schema v27 and the
|
||
signed nondebuggable Android versionCode 17/schema v25 update without
|
||
uninstalling or clearing application data. A strictly disposable Program was
|
||
synchronized to Android, started, left and resumed through the durable
|
||
singleton, and completed as session
|
||
`se_6bb66f59-a6fb-4759-a75d-c1a79ea107e8`. Android displayed `Effectuée` and
|
||
desktop/Web received the same completed provenance through the real
|
||
`program-executions-v1` producer and consumer. A second exchange created no
|
||
duplicate and no second active draft. The disposable Program was then deleted
|
||
through the revision-guarded Web command; Android retained exactly one
|
||
tombstone and desktop retained exactly one acknowledged deletion ledger row.
|
||
After both stores and the Trainlog components were reopened, exact replay of
|
||
the preserved pre-deletion Android generation returned its production durable
|
||
ACK without duplicating the execution or resurrecting the Program. A final
|
||
correlated exchange remained completed with no missing capability. The real
|
||
Program remains active and unique with 24 sessions and 164 ordered entries.
|
||
|
||
Program-owned manual preparations now carry their stable Program provenance to
|
||
Android. Starting and finalizing such a delivery retains both identities, and a
|
||
strict same-delivery enrichment repairs completed sessions produced from the
|
||
earlier omission without changing workout facts. Android and Web therefore
|
||
derive the completed count and per-session state from the same execution
|
||
identity after synchronization and restart.
|
||
The historical S1 A delivery was recovered onto its original completed
|
||
`session_id`; Android and Web now both report one completed session out of 24.
|
||
Generation capture also accepts its valid local continuous duration plans
|
||
without projecting them as unsupported set targets.
|
||
|
||
The private Programs rollout migrated desktop schema v25 to v26 and Android
|
||
schema v23 to v24 after verified backups, then installed the signed
|
||
non-debuggable versionCode 16 APK without uninstalling or clearing data. The
|
||
real 24-session Program remained unique and readable on Web and Android. A
|
||
clearly disposable Program was imported, synchronized, cancelled once without
|
||
mutation, then logically deleted through the Web confirmation. Android consumed
|
||
the real tombstone and desktop recorded its correlated generation ACK. A first
|
||
post-restart attempt failed before generation with `device_unavailable` and
|
||
`PTP_ERROR_IO`; no success state was recorded. Releasing the stale bounded MTP
|
||
worker and restarting the local ADB server restored the same physical peer. The
|
||
subsequent correlated exchange completed in both directions, and store reopen
|
||
confirmed one durable Android tombstone, one acknowledged desktop deletion,
|
||
no duplicate Program, and no resurrection.
|
||
|
||
The controlled deployment preserved the installed Android signing identity and
|
||
advanced its private `versionCode` from 13 to 14 without uninstalling or
|
||
clearing data. The requested six-occurrence validation preparation was already
|
||
`started` on Android at preflight, so withdrawal retained its immutable
|
||
delivery and returned `execution_preserved`; it did not delete or complete the
|
||
reserved execution. The source proposal retained its identity, `archived`
|
||
state, content and publication timestamp. Two correlated post-withdrawal
|
||
generation conversations, separated by component restart, completed without
|
||
resurrection, duplicate withdrawal, performed fact, or draft mutation.
|
||
|
||
The Next Session tile no longer treats the frozen Dashboard
|
||
`next_session.available` placeholder as a durable inventory. A separate bounded
|
||
`GET /api/v1/prepared-items` Core projection distinguishes manual preparations,
|
||
imported AI proposals, and active or pending execution drafts and exposes stable
|
||
identity, state, title/date, occurrence count, and provenance. Reload and later sync
|
||
failure do not erase the last successfully read projection. Proposal targets
|
||
remain planning data and never alter Dashboard actual-work metrics.
|
||
|
||
Full-generation MTP publication now uses a private phase-owned outbox. The
|
||
request, desktop consumption ACK, and current desktop generation are published
|
||
only when their phase requires them; retained staging, inbound objects, legacy
|
||
files, prior ACKs, and causal evidence are not recursively re-uploaded. Adapter
|
||
timeouts are persisted as the stable `transport_timeout` status instead of a
|
||
truncated Python traceback. The browser polls through one abortable scheduler,
|
||
orders revisions per `run_id`, and rejects stale responses from older runs.
|
||
The reciprocal MTP read is bounded to the Android peer advertisement, current
|
||
generation reference and consumption ACK, plus the single Android generation
|
||
named by that reference. Historical generations and other retained evidence
|
||
are neither traversed nor deleted during polling. Before peer validation, MTP
|
||
mode now requires one successful current-device pull; a retained pre-upgrade
|
||
advertisement can no longer cause a false missing-capability failure.
|
||
|
||
## Data semantics
|
||
|
||
- `exercise_id` identifies a catalogue exercise; `entry_id` identifies one
|
||
ordered occurrence in a session.
|
||
- Plans and performed work are distinct. A continuous activity is not a fake
|
||
performed set, and actual repetitions may be heterogeneous.
|
||
- Explicit MAX results are occurrence-owned and distinct from ordinary best
|
||
sets. Equipment/resistance/execution context remains relevant.
|
||
- BODY ZONES are a UX/business projection over explicit scientific knowledge,
|
||
not a complete anatomical taxonomy.
|
||
- Feedback roots retain identity while append-only revisions preserve wording
|
||
history. J+1/session follow-ups are session-owned.
|
||
- AI proposals contain target-only plans and never create performed work,
|
||
history, MAX, or feedback until the user explicitly starts and completes a
|
||
normal capture draft.
|
||
|
||
## Validation status
|
||
|
||
The durable commands are owned by [tests.md](tests.md). At commit
|
||
`14b863eb0f159d46891f4ad5cf076b3aa0a11bba` on 2026-09-17, isolated validation
|
||
passed desktop compilation, **75/75 normal Meson tests** and
|
||
**75/75 ASan/UBSan Meson tests**, Android debug assembly and **203 Android
|
||
tests (199 passed, 4 skipped, 0 failed)**, the JSON validator, and the
|
||
import-contract validator. The source-derived TUI
|
||
`TRANSLATABLE_UI=0` check and Android resource parity also passed. Link and
|
||
diff safety checks passed in that checkpoint evidence. These are historical
|
||
results for that commit, not executions performed by later documentation work.
|
||
The four Android skips were the optional historical v9, v12, v13, and v14
|
||
database-fixture migration cases. Hardware MTP, real Drive, and Android
|
||
instrumented tests were not part of that isolated run.
|
||
|
||
For `TRAINLOG_SYNC_CHARACTERIZATION_V1` on 2026-09-17, the isolated JDK 17
|
||
preflight, smoke, and full modes passed. The final full run passed desktop
|
||
compilation, **75/75 normal Meson tests**, **75/75 ASan/UBSan Meson tests**, the
|
||
explicit production-path sync-gap characterization, both JSON/import contract
|
||
validators, the targeted Android draft lifecycle test, Android debug assembly,
|
||
and **204 Android tests (200 passed, 4 skipped, 0 failed/error)**. The added
|
||
Android↔desktop↔fresh-Android V3 round trip runs inside that Android total. The
|
||
four skips remain the documented optional historical v9, v12, v13, and v14
|
||
database fixtures. No hardware MTP, real Drive, adb, instrumented device test,
|
||
user database, exchange directory, or user service participated.
|
||
|
||
For the `TRAINLOG_SYNC_DATA_LIFECYCLE_V1` closeout on 2026-09-17, real staged
|
||
V4 history and execution-draft entry points completed both Android/desktop
|
||
producer directions through fresh destination databases. The closeout fixed
|
||
one Android V4 import defect that validated but failed to persist occurrence
|
||
plan fields. Isolated validation passed **75/75 normal Meson tests**,
|
||
**75/75 ASan/UBSan Meson tests**, Android debug assembly, and **207 Android
|
||
tests (203 passed, 4 skipped, 0 failed/error)**. The four skips remain the
|
||
optional real historical v9, v12, v13, and v14 database fixtures. The active
|
||
transport remains V3; no phone, MTP, Drive, service, user database, deployment,
|
||
or instrumented device test participated.
|
||
|
||
The Dashboard Core extraction tranche passes **62/62 normal Meson tests** and
|
||
**62/62 ASan/UBSan Meson tests**, including the standalone public-header and
|
||
Core characterization targets, plus both canonical JSON validators.
|
||
|
||
The CLI/HTTP infrastructure tranche passes **67/67 normal Meson tests**, adding
|
||
pure CLI parsing, process-level help/version checks and an isolated loopback
|
||
HTTP lifecycle/security suite. The same suite is required under ASan/UBSan.
|
||
|
||
The frontend shell tranche passes **70/70 normal and ASan/UBSan Meson tests**,
|
||
including TypeScript typecheck, 10 Vitest component tests, deterministic asset
|
||
generation bounds, real embedded-asset HTTP coverage, SPA/API separation and
|
||
runtime execution without Node/npm or `web/dist`.
|
||
|
||
The Dashboard data-contract tranche passes **71/71 normal and ASan/UBSan
|
||
Meson tests**, including the bounded Core/JSON target, HTTP endpoint coverage
|
||
and 12 Vitest tests for parsing and factual Footer integration.
|
||
|
||
`WEB_DASHBOARD_GRID_V1=PASS/FROZEN` uses `react-grid-layout` 2.2.4 behind an
|
||
independent Trainlog model/validator. Its 12-column desktop layout has seven
|
||
stable tile IDs, bounded per-tile dimensions, vertical collision compaction,
|
||
explicit edit/cancel/reset behavior, mouse drag/two-axis
|
||
resize, an announced keyboard alternative, and derived six/one-column
|
||
responsive projections. Persistence is supplied by the following frozen slice.
|
||
|
||
`WEB_DASHBOARD_LAYOUT_V1=PASS/FROZEN` adds a strict versioned seven-tile file
|
||
under the private XDG configuration directory, backend revalidation, atomic
|
||
0600 replacement, ETag/If-Match conflict control, Origin plus ephemeral CSRF
|
||
protection, and durable React load/save/reset behavior. Only the desktop
|
||
12-column canon persists.
|
||
|
||
`WEB_DASHBOARD_TILES_V1=PASS/FROZEN` renders all seven domains from the one
|
||
validated `/api/v1/dashboard` snapshot. Compact, medium and large densities
|
||
only reveal existing facts. Activity uses a factual CSS day map, MAX lists are
|
||
bounded 1/3/8, and muscle bars encode `session_count` only. Unavailable,
|
||
partial, invalid and transport-error states never synthesize values.
|
||
|
||
`WEB_DASHBOARD_VISUALIZATIONS_V1=PASS/FROZEN` uses a lazy, modular Apache
|
||
ECharts 6.1.0 SVG line chart for medium/large Progression tiles. Every real
|
||
point remains visible, `smooth=false`, legacy zeroes are preserved, and only
|
||
Core-owned `improved` flags alter markers. An original React SVG supplies
|
||
front/back BODY ZONES regions whose discrete colour level depends solely on
|
||
`session_count`; the factual text list remains authoritative and accessible.
|
||
|
||
`TRAINLOG_I18N_V0_1_1=PASS` is covered by desktop presentation, persistence,
|
||
formatting, layout-invariance and source-derived text-boundary tests, plus
|
||
Android resource-parity, language-owner, typed sync-presentation, and
|
||
stable-data presentation tests. A real-device/manual visual language-switch
|
||
smoke remains a manual validation; it does not change the validated status.
|
||
|
||
Hardware-dependent MTP is not automated. The controlled real-device rollout
|
||
validated two consecutive post-withdrawal full-generation exchanges around a
|
||
Trainlog component restart. Android retained exactly one withdrawal result as
|
||
`execution_preserved`, retained the linked delivery as `started`, and exposed
|
||
no active preparation or start action after replay. Desktop retained exactly
|
||
one acknowledged withdrawal and no active preparation. The source AI proposal
|
||
was verified with its original identity, state, entry content and publication
|
||
timestamp; no proposal-to-execution-draft conversion was performed.
|
||
|
||
## Active limitations
|
||
|
||
`TRAINLOG_SYNC_CAUSAL_DELETE_V1=PASS/FROZEN` provides isolated Android and
|
||
desktop producers/consumers for `trainlog-causal-deletions` V1. All contracted
|
||
domains are durably protected; exact replay is idempotent and unprovable or
|
||
concurrent ancestry conflicts before mutation. Protected legacy snapshots are
|
||
refused before resurrection. The default automatic daemon path remains V3,
|
||
while the trusted local Web opt-in now runs the deployed full-generation path.
|
||
Its acknowledged generations can leave the active admission window only
|
||
through the verified schema-v22/schema-v21 archive ledger; pending, rejected
|
||
and ambiguous generations remain protected.
|
||
|
||
The focused causal closeout additionally proves child-data delete/update
|
||
conflicts including change-and-revert, imported built-in and alias protection,
|
||
active/pending/desktop draft-revision equivalence, protected companion refusal,
|
||
current-view retirement and bounded pre-effect admission. The later generation
|
||
slice additively advances schemas to desktop v22/Android v21 without changing
|
||
default automatic V3 selection.
|
||
|
||
The residual causal evidence also proves legitimate Android and desktop
|
||
producer directions separately for exercise retirement, populated body
|
||
observation deletion, custom-equipment retirement, multi-revision feedback
|
||
withdrawal and BODY ZONE relation removal. Production exchanges establish each
|
||
replica before deletion; stored effects, current readers, stale-companion
|
||
refusal and exact replay are checked after reopening the receiving store.
|
||
|
||
The isolated closeout passed 76/76 normal Meson tests, 76/76 ASan/UBSan
|
||
tests, Android debug assembly, and 211 Android tests (207 passed, four optional
|
||
historical-fixture skips). Strict desktop builds use Clang 22.1.8; the compared
|
||
pre-existing GCC 16.2.1 Web warning gate remains documented rather than green.
|
||
|
||
- `TRAINLOG_WEB_EXERCISES_V1=PASS`. `TRAINLOG_WEB_ANALYSIS_V1` is implemented
|
||
on the 0.1.4 branch and awaits the requested Dashboard/Analyse visual review.
|
||
- `APP_SHELL_V1` retains a recorded legacy visual/accessibility review gate; its
|
||
observations are inputs to, not decisions for, the new 0.1.3 Android contract.
|
||
- Session Generator V1 is hidden while V2 planning semantics are developed.
|
||
- Hardware MTP validation requires a connected unlocked Android device.
|
||
- Scientific knowledge is bounded to reviewed catalog entries; unknown custom
|
||
exercises remain unclassified rather than inferred from names.
|