2134 lines
122 KiB
Markdown
2134 lines
122 KiB
Markdown
# Tests and validation
|
||
|
||
## Reading the evidence
|
||
|
||
Counts below belong to the stated checkpoint, source revision and build
|
||
configuration. They are historical evidence, not a claim that the present
|
||
documentation checkout ran those tests. A native build with `-Dweb=enabled`
|
||
registers embedded Web asset tests in Meson; `npm test` separately runs Vitest
|
||
against Web source. A default `web=auto` build can omit embedded assets, so a
|
||
green source test alone does not validate the packaged Web application.
|
||
Skipped Android fixture tests, unrun instrumented tests and physical Bluetooth,
|
||
MTP or Drive evidence are distinct outcomes. The primary personal phone is
|
||
reserved for authorized manual smoke checks, not host automation.
|
||
|
||
## Sleep V2 concurrent-edit selection in 0.1.7
|
||
|
||
Synthetic Desktop and Android regressions cover both edit-time orderings,
|
||
including a later user edit that records an earlier sleep event. They check
|
||
the complete winning snapshot, retained sibling revisions, no event/intake
|
||
union or duplication, causal successor import on the other peer, exact replay,
|
||
and rejection when edit instants are equal. The real paired-device validation
|
||
completed separate automatic, Web-with-automatic-service, and post-reconnection
|
||
Bluetooth generation/ACK cycles without changing an old rejected ACK.
|
||
|
||
## Sleep revision collision recovery
|
||
|
||
Collision regressions distinguish exact replay from same-ID parent or payload
|
||
reuse, validate known historical ancestors before stale-snapshot handling, and
|
||
exercise safe re-identification of an unpublished branch. They preserve
|
||
quantity one and two, distinct same-time medication occurrences, event and
|
||
intake unions, rollback, immutable-generation references, exact ledger rearm,
|
||
and idempotent replay. SQLite migration tests prove that Desktop v36 and
|
||
Android v33 reject direct update/delete of all revision-owned Sleep payload
|
||
tables. Android also creates a 65-revision edit chain and verifies unique UUIDs,
|
||
exact parentage, and unchanged historical payloads.
|
||
|
||
The retained real-data simulation replays the original immutable full
|
||
generation against isolated copies and verifies one session ending at 11:16,
|
||
7 exercises, 15 sets, 5 feedbacks, 7 exercise intervals, 4,919 BPM samples,
|
||
4,919 RR intervals, Program provenance, `consumed` / `sqlite-commit-full`, a
|
||
byte-identical ACK replay, and clean integrity/foreign-key checks. A later
|
||
normal generation was consumed and acknowledged, proving continued pipeline
|
||
use. Sensitive medication names and dose values remain outside repository
|
||
evidence.
|
||
|
||
## Web UI refinement 0.1.6
|
||
|
||
Web regressions cover calendar-week activity grouping, global rather than
|
||
single-exercise Dashboard progression, exercises-per-date overview data,
|
||
measured-only group drill-down, normalized measurement selection, the shared
|
||
BODY ZONES figure, persistent language/settings navigation, multi-night Sleep
|
||
selection invariants, and Equipment response validation. Native equipment
|
||
coverage exercises a synthetic custom duplicate and proves that a transactional
|
||
merge repoints completed history to the retained canonical identity without
|
||
touching real user equipment. Strict C compilation, the full Meson inventory,
|
||
Web typecheck/build/Vitest, JSON/import validation, changed-C formatting and
|
||
diff checks remain the closeout gates.
|
||
|
||
Post-review regressions cover a validated Sleep Diary revision plus nocturnal
|
||
BPM/RR capture through the production full-generation transport, desktop
|
||
import, correlated ACK, exact ACK replay, a second Sleep-only revision and a
|
||
second generation. The 24-conversation deployed-capacity regression removes
|
||
legacy ACK-ledger rows deliberately, recovers capacity without deleting
|
||
generation evidence, and continues through archive/replay. Native Web tests
|
||
prove that an active zero-occurrence execution draft is deleted causally and
|
||
that repeating its request is byte-identical, while a populated active draft
|
||
still conflicts. The equipment client test removes `crypto.randomUUID`, uses
|
||
deterministic `getRandomValues` bytes, and completes the protected merge HTTP
|
||
request; native merge tests retain their transaction/rollback coverage.
|
||
|
||
The real Sleep Bluetooth recovery regression starts with no Android session
|
||
and an empty pending Sleep revision, records two pre-bed medication intakes
|
||
(including `quantity=2`), reopens the repository after each meaningful
|
||
lifecycle transition, then records Couché, two BPM/RR samples
|
||
separated by Réveil, and Levé. It captures and publishes the production V2
|
||
Sleep and V1 heart-rate artifacts, imports them with the desktop consumer,
|
||
accepts and replays the ACK without duplication, then admits a second
|
||
Sleep-only revision. It also applies the original intake-free ancestor after
|
||
ACK and proves that causal reconciliation cannot erase the current intakes.
|
||
Separate tests prove connection-arrival auto-request
|
||
policy with its cooldown, cross-run reuse of an unacknowledged immutable
|
||
generation and recognition of its original-run exact generation/digest ACK,
|
||
ledger-backed superseded-branch capacity, fresh-peer non-root Sleep ancestry,
|
||
service-installer upgrade behavior, and ACK-recovery progress frames over the
|
||
Bluetooth-selected orchestrator path.
|
||
|
||
UI regressions assert the Sleep agenda's 25 exact hourly positions, 13 visible
|
||
two-hour desktop labels and five narrow six-hour anchors, including the exact
|
||
0/25/50/75/100% coordinates. They also prove that both Sleep views reuse the
|
||
same presentation-only Couché +45 minutes to Levé -10 minutes estimate, that a
|
||
factual sleep interval wins, and that a missing bound produces no estimate.
|
||
Other UI regressions cover persisted
|
||
Dashboard sizing and reset behavior, English preference persistence across a
|
||
remount, centralized Dashboard English labels, non-stretched chart aspect
|
||
ratios, and distinct missing/partial exercise-timing messages.
|
||
The current closeout run passes Web **32 files / 209 tests**, TypeScript
|
||
typecheck and the production build; native Meson **113/113**; Android **315
|
||
tests** with five optional skips plus `assembleDebug`; JSON/import validators;
|
||
changed-C formatting; and diff checks.
|
||
The restarted private service serves the rebuilt executable byte-for-byte from
|
||
the development checkout and reports healthy Trainlog `0.1.6`. A non-mutating
|
||
Firefox smoke on the real 2026-09-23 night confirms the agenda and detail both
|
||
show 22:50–04:04 as estimated, with 13 ordinary-desktop labels, 25 wide labels
|
||
and five narrow anchors.
|
||
|
||
Sleep Diary coverage includes schema creation/migration, midnight and DST
|
||
offsets, revision conflict, logical deletion/reopen, companion replay and
|
||
rollback, Web periods/deep links, Android persistence, and vector PDF
|
||
construction/pagination for 7, 14, 21 and 30 synthetic days.
|
||
Lifecycle regressions additionally cover durable drafts excluded from export,
|
||
guarded validation, reopen, post-validation correction, exact
|
||
generation-to-revision capture and ACK-only acknowledgement.
|
||
Web component regressions add each point/interval capture type, verify that a
|
||
successful mutation rerenders without losing the workspace, preserve one entry
|
||
identity while advancing revisions, and exercise medication creation, immediate
|
||
intake availability, event/intake editing and deletion. The UUID regression
|
||
removes `crypto.randomUUID()` and proves the HTTP-origin-compatible UUIDv4
|
||
path. A real Firefox run through the production C server and `trainlog.perf`
|
||
creates events, medications and intakes, reloads their durable state, checks a
|
||
responsive viewport and captures console errors and unhandled rejections.
|
||
Program recovery coverage starts from an acknowledged legacy preparation,
|
||
repairs the same completed Android `session_id`, publishes one completed
|
||
execution and verifies Web `1 / 24` presentation. Related regressions cover
|
||
planned continuous activity capture and fresh-peer Sleep Diary bootstrap from
|
||
non-root current revisions.
|
||
|
||
Android session-finalization coverage includes a Program-derived mixed session
|
||
whose continuous target is completed from its measured timeline, several prior
|
||
occurrences, feedback, an active heart-rate capture, and a planned `2 x 10`
|
||
occurrence performed as three valid `10 @ 23 kg` sets. It recreates the legacy
|
||
state with a closed continuous timeline but no performed row, finalizes at an
|
||
explicit factual timestamp, and proves that plan, actual sets, feedback,
|
||
timeline, BPM samples, and RR intervals remain distinct and intact. Desktop
|
||
Sleep exchange coverage also verifies that replay of an identical durable but
|
||
unpublished revision acknowledges it without `StopIteration`, while different
|
||
content under the same revision identity remains an explicit conflict.
|
||
|
||
`web_analysis` covers empty history, every 7/30/90/all period selector,
|
||
loaded and unloaded repetitions, continuous duration/distance/speed, explicit
|
||
persisted MAX, multiple primary BODY ZONES, partial measurements, one- and
|
||
multiple-point measurement semantics, and active-Program projection. The HTTP
|
||
suite exercises the real bounded endpoint plus invalid and duplicate query
|
||
parameters. Vitest validates the typed parser, null preservation, route and
|
||
section changes, period/exercise/metric selection, invalid deep-link fallback,
|
||
critical French/English labels, and Dashboard layout regressions.
|
||
|
||
`test_sync_drive_transport` proves Drive artifact-before-manifest and
|
||
manifest-before-reference publication, rejects unsafe/AI namespaces, and keeps
|
||
a partial generation invisible. Orchestrator coverage proves that Drive mirror
|
||
failure cannot undo a completed primary transport. `web_sessions` additionally
|
||
proves duplicate-delivery rejection and stable-identity active-list removal
|
||
after the reserved execution session appears in completed history. Android's
|
||
normal unit suite builds the shared generation coordinator, foreground service
|
||
and WorkManager transport rather than a test-only protocol copy.
|
||
|
||
`web_sessions` exercises production preparation creation, durable request
|
||
replay, immutable successor revisions, stale-revision conflict, duplicate
|
||
exercise occurrences, list search, persisted detail serialization, revision-
|
||
guarded withdrawal, conflicting idempotency-key reuse, active-list exclusion,
|
||
and preservation of proposal/revision/delivery evidence. The HTTP test covers
|
||
the real protected DELETE route and the versioned local preference GET/PUT,
|
||
including CSRF, ETag, persistence and invalid-file fallback. The Web Vitest
|
||
suite additionally covers cross-type global ordering beyond 32 rows, timestamp
|
||
offsets, stable ties, invalid/absent dates, contextual states, confirmation
|
||
cancel, the DELETE request and preference conflict behavior. Date tests run
|
||
under `Pacific/Honolulu` and `Pacific/Kiritimati`.
|
||
|
||
`SessionPreparationExchangeTest` covers Android import replay, occupied
|
||
singleton refusal, explicit start, reserved execution identity, schema-v22 to
|
||
v23 migration, permanent withdrawal replay, pending cancellation, started
|
||
execution preservation, multi-delivery targeting, witness preservation and
|
||
absence of performed facts. `test_session_preparation_export.py` proves that a
|
||
withdrawn preparation is not redelivered, its exact evidence is exported in V2,
|
||
and an acknowledged withdrawal is not republished. Generation tests bind the
|
||
withdrawal to one immutable generation and acknowledge it only after the exact
|
||
durable ACK. `SyncGenerationServiceTest` and
|
||
`SyncDeploymentConversationTest` exercise the real optional artifact through
|
||
generation validation and 24 bidirectional conversations. It also asserts that
|
||
every foreground conversation emits exactly one fresh daemon request signal;
|
||
the coordinator's production resume predicate preserves an interrupted
|
||
generation identity until the counterpart generation has a durable terminal
|
||
consumption row.
|
||
|
||
`SyncDeploymentConversationTest` executes both production generation services
|
||
and reopens Android storage. `test_web_sync_browser.py` adds a real Firefox click
|
||
through the C adapter, observes the desktop commit at an Android test barrier,
|
||
then releases the real ACK. This proves the directory transport double, not
|
||
physical MTP. Orchestrator tests cover bounded streams and descendant cleanup;
|
||
deployment-tool tests cover consistent SQLite backup and candidate inventory.
|
||
The incident regressions also prove that a phase-owned MTP outbox excludes
|
||
retained staging and unrelated legacy files, adapter expiry produces the stable
|
||
`transport_timeout` code, and operational failure classes remain distinct.
|
||
The C MTP regression keeps a malformed retained generation beside the current
|
||
reference and proves that pull downloads only the referenced generation while
|
||
still rejecting a truncated object inside that current generation.
|
||
|
||
`web_session_deletions` exercises proposal withdrawal replay, preservation of a
|
||
derived preparation, active-draft conflict, causal draft/session deletion and
|
||
completed-session finalization. It also builds a Program-origin completed
|
||
session, deletes it through the production command, verifies one causal
|
||
operation/tombstone and terminal execution provenance, then replays the exact
|
||
request. The HTTP suite covers real history detail, stale and successful
|
||
session DELETE requests, invalid identity, post-delete deep-link absence and an
|
||
injected SQLite failure with complete rollback. Vitest covers the
|
||
focus-managed confirmation, Cancel and Escape zero-mutation paths, the real
|
||
DELETE call, server-confirmed refresh, useful typed errors and focus
|
||
restoration. AI companion tests prove that V2 exports a
|
||
withdrawal without the proposal and that Android's tombstone rejects later V1
|
||
replay.
|
||
|
||
`web_programs` exercises validation preview without writes, transactional
|
||
import, exact replay, responsive detail serialization, archive replay,
|
||
revision-guarded terminal deletion, response replay, preparation creation,
|
||
usage/provenance and absence of delivery. Mixed-profile preparation coverage
|
||
combines continuous duration, externally loaded reps with an integer JSON
|
||
weight, and unloaded reps; it verifies preserved order and targets, generated
|
||
entry identities, readable detail, request replay, and zero execution or
|
||
delivery creation. A separate compatibility fixture retains an affected source
|
||
Program's impossible zero weight while deriving an editable preparation with an
|
||
unspecified target. Schema migration coverage checks all v26 tables and
|
||
columns. `schema_v26_migration` additionally migrates a
|
||
deterministically populated v25 fixture containing 24 sessions and 48 entries,
|
||
with preparation provenance and history, and verifies exact typed projections
|
||
after migration. Programs Vitest covers preview before the only importing
|
||
mutation, explicit preparation creation/navigation, discrete trash confirmation,
|
||
focus restoration, and Escape/Cancel zero-mutation behavior. The full HTTP,
|
||
pagination, strict-parser and Core suites retain malformed, duplicate-key,
|
||
bounds, catalog/profile/equipment, conflict and rollback coverage.
|
||
|
||
Android Programs projection tests cover schema-v23 to v24 migration, full
|
||
nondeleted `trainlog-programs` V1 snapshot import, durable deletion tombstones,
|
||
read-only Sessions list/detail data, exact replay, capability gating, and
|
||
correlated ACK behavior. The integrated `SyncGenerationServiceTest` invokes the
|
||
real production chain from a live Program through capture, publication, Android
|
||
consumption, deletion/tombstone publication, real ACK, desktop acceptance and
|
||
reopen; an exact old generation is idempotently recognized and ACKed while the
|
||
durable tombstone prevents Program resurrection, and a second exchange creates
|
||
no duplication. Python Programs/synchronization suites cover the staged
|
||
companion's full snapshot, unacknowledged tombstone publication, and ACK-driven
|
||
acknowledgment without changing mobile V3, catalog, or preparation artifacts.
|
||
|
||
Program execution regressions additionally cover Android schema v24 to v25,
|
||
start/replay/singleton protection, target-only draft creation, completion,
|
||
restart persistence and companion serialization. The integrated generation
|
||
test uses the production desktop and Android producers/consumers to import a
|
||
Program, execute one session, import completed history and its V1 provenance,
|
||
acknowledge the correlated generation, expose Web `completed`, then continue
|
||
through Program deletion, restart and old-generation replay without duplication
|
||
or resurrection. The desktop populated migration test now continues through
|
||
schema v27 and proves the new execution ledger starts empty.
|
||
|
||
The delivered-Program regression covers the previously omitted provenance
|
||
pair end to end: legacy preparation receipt, start, performed capture,
|
||
finalization, exact-payload enrichment, completed Android projection, execution
|
||
companion output and restart. Export tests assert both stable IDs, the C Web API
|
||
asserts `completed`, and the Web component verifies `1/24`, `Effectuée` and the
|
||
absence of a preparation action on the completed session.
|
||
|
||
`schema_v28_migration` migrates a populated v27 fixture, preserves Program,
|
||
session and execution identities byte-for-byte, admits the terminal deleted
|
||
state, reports `integrity_check=ok` and an empty foreign-key check, and proves
|
||
that migration invents no causal operation or deletion. The mobile V3 importer
|
||
regression proves deterministic live causal revision seeding and exact replay.
|
||
The Program execution exchange regression proves an old retained completed
|
||
fact cannot override a session tombstone or duplicate terminal provenance.
|
||
|
||
`test_web_sessions_browser.py` runs headless Firefox against the embedded
|
||
production assets and C HTTP server with private temporary XDG roots. It creates
|
||
only a synthetic ready preparation and delivery, verifies French then persisted
|
||
ISO presentation, captures desktop and an exact BiDi-emulated 390x844 viewport,
|
||
performs the real confirmation click, checks the schema-v24 withdrawal ledger
|
||
after reload, and validates the resulting V2 export evidence. Android repository
|
||
tests exercise the actual V2 consumer rather than a permissive protocol mock.
|
||
|
||
`web_prepared_items` exercises the production SQLite projection and JSON
|
||
serializer with a proposal and an execution draft, then proves finalized and
|
||
causally deleted execution drafts are absent. The HTTP test covers the
|
||
read-only route and method rejection. Vitest distinguishes proposal, active
|
||
draft, loading, empty and failed-refresh states; it retains an already rendered
|
||
durable value across a reread failure. Sync-control tests cover a single
|
||
abortable polling scheduler, per-run revisions, rejection of an older run's
|
||
late terminal response, stable error/action rendering, and cleanup on unmount.
|
||
|
||
The `ai_history_export` test covers an empty database, a simple session,
|
||
distinct multi-occurrences of one exercise, ordered sets, exact occurrence
|
||
feedback, session-owned follow-up feedback, JSON escaping/UTF-8, MAX history,
|
||
body measurements, deterministic content, and byte-for-byte database
|
||
immutability.
|
||
|
||
## 1. Principle
|
||
|
||
A Trainlog feature is not complete without relevant validation.
|
||
|
||
Frozen formats, persistence migrations, synchronization semantics, and user-data
|
||
mutations require executable coverage where practical.
|
||
|
||
`WEB_CLI_HTTP_INFRASTRUCTURE_V1` adds pure CLI tests for the default TUI mode,
|
||
both Web switches, help, version, unknown and repeated options, positional
|
||
arguments, Web-only `--port`, missing/non-numeric/negative/zero/out-of-range
|
||
ports, 65535, and option ordering. Process-level tests execute `--help` and
|
||
`--version` without initializing the database, TUI or server.
|
||
|
||
The isolated HTTP test opens one in-memory database, starts the production
|
||
single-threaded adapter on an explicitly reserved loopback port, and verifies
|
||
health JSON/version, content type, `nosniff`, CSP, absence of CORS, 404, 405
|
||
with `Allow: GET`, invalid Host, oversized body, oversized-header rejection,
|
||
SIGTERM shutdown and explicit failure on an already occupied port. It never
|
||
uses the Internet. A separate real-launch smoke uses an isolated XDG directory,
|
||
performs the health request and stops the production binary with SIGINT.
|
||
|
||
`WEB_FRONTEND_SHELL_V1` adds TypeScript typechecking and 10 Vitest/jsdom
|
||
component tests for the persistent shell, five client destinations, active
|
||
navigation, browser history, unavailable factual fields, successful/failed/
|
||
invalid health responses and accessible landmarks. Production Vite output is
|
||
then generated and embedded for the C HTTP test, which additionally verifies
|
||
HTML root and SPA routes, JS/CSS MIME, HTML and immutable cache policies,
|
||
SHA-256 ETag, strict CSP, HEAD, unknown assets, traversal rejection and the
|
||
invariant that unknown `/api/` paths never receive `index.html`.
|
||
|
||
The generator contract test proves byte-for-byte deterministic output, stable
|
||
path ordering and the 128-asset bound. Runtime smoke coverage starts the linked
|
||
binary with Node/npm absent from `PATH`, removes any dependency on `web/dist`,
|
||
and checks root, health and SIGINT. A separate `-Dweb=disabled` build verifies
|
||
the explicit diagnostic instead of pretending that a frontend exists.
|
||
|
||
Frontend preparation and direct validation are:
|
||
|
||
```bash
|
||
cd web
|
||
npm ci
|
||
npm run typecheck
|
||
npm test
|
||
npm run build
|
||
```
|
||
|
||
Release-oriented Meson builds must use `-Dweb=enabled`; Meson never provisions
|
||
or downloads npm dependencies.
|
||
|
||
The history-deletion regression suite also exercises a real Program-origin
|
||
completed session through the Core and HTTP command paths, including stale
|
||
revision, cancellation, injected transaction failure, exact request replay and
|
||
terminal execution provenance. Full-generation tests apply causal deletions
|
||
before older history and equipment-association companions, prove that
|
||
standalone imports remain strict, recover recent consumed and rejected ACKs
|
||
without letting an older bounded window starve active producer capacity, and
|
||
replay an already consumed generation without mutation.
|
||
|
||
`WEB_DASHBOARD_DATA_CONTRACT_V1` adds a standalone bounded Core test for empty
|
||
history, explicit unavailable domains, two observable sessions on one local
|
||
day, an unfinished session, continuous activity, MAX-only sessions, primary
|
||
zone deduplication, catalogue-without-history exclusion, 90/30-day windows,
|
||
recent-MAX truncation/`partial`, Footer facts and JSON escaping/overflow. The
|
||
HTTP lifecycle test additionally proves `GET /api/v1/dashboard`, JSON content
|
||
type/version, unavailable reasons, corrupt-timestamp `invalid_data`, Core-error
|
||
translation to bounded HTTP 500 and `POST` rejection without weakening Host,
|
||
CSP, CORS or unknown-route behavior. Vitest validates strict parsing, real
|
||
Footer population, empty fallback, malformed snapshots and absence of invented
|
||
tile metrics; the seven tile bodies remain placeholders.
|
||
|
||
`WEB_DASHBOARD_GRID_V1` adds Vitest coverage for the seven stable identities,
|
||
default validity/non-overlap, every min/max constraint, independent rejection
|
||
of unknown/duplicate/out-of-grid/non-integral layouts, lossless library adapter
|
||
round trips, deterministic collision compaction, horizontal/vertical keyboard
|
||
resize, responsive six/one-column projections, normal-mode locking, absent
|
||
normal-mode handles, edit actions, cancel/reset/session-only save, visible
|
||
keyboard focus and live announcements. Existing shell tests continue to prove
|
||
the factual Footer, navigation and absence of invented data.
|
||
|
||
`WEB_DASHBOARD_LAYOUT_V1` adds Core-independent filesystem tests for default,
|
||
strict parsing, bounds, corruption, oversized/non-regular/symlink inputs,
|
||
private permissions, atomic replacement, revision conflicts, preservation on
|
||
invalid writes and reset. A canonical JSON parity test prevents C/TypeScript
|
||
constraint drift. HTTP tests cover GET/PUT/DELETE, ETag/If-Match, two stale
|
||
clients, CSRF/Origin rejection and `trainlog.perf` Origin acceptance. Vitest
|
||
covers default/persisted/invalid sources, ETag/token parsing, canonical-only
|
||
PUT, conflict translation, loading, local cancel/reset and durable Save.
|
||
|
||
Sleep Diary Web tests cover canonical snapshot adoption after every successful
|
||
autosave, ordered revision chaining, rejection of stale asynchronous reads,
|
||
and live Agenda updates for bedtime, final get-up, split sleep, time in bed,
|
||
long awakenings, naps, sleepiness and grouped medication intakes. They also
|
||
cover exact 18:00-to-18:00 timestamp geometry, active-night isolation, empty
|
||
night state, return navigation and rejection of an earlier date response that
|
||
finishes late. Localized dose/unit labels, same-name medications with different
|
||
usual doses and editable intake-dose prefill remain covered.
|
||
Selection regressions additionally prove that one agenda click updates both
|
||
detail dates and HR identity without a write, and that preview and download
|
||
receive the same one-night subset with recomputed observations. Projection
|
||
unit tests cover exact factual duration, fallback duration, multiple structured
|
||
awakenings, and subtraction of only their overlap from an estimated range.
|
||
The heart-rate panel suite additionally covers a complete 22:05-to-04:14 night,
|
||
declared and fallback-estimated sleep ranges, multiple close awakenings, final
|
||
get-up, pre-bed and close medication intakes with exact dose/unit/quantity,
|
||
every intervening hour tick, and a no-HR rendering that creates no measured
|
||
curve. Geometry tests prove exact factual bounds and the common normalized
|
||
projection used by chart samples, overlays, events, intakes, and ticks; narrow
|
||
viewports preserve that geometry in a horizontally scrollable chart.
|
||
`test_web_sleep_browser.py` exercises the embedded production bundle in real
|
||
Firefox against an isolated synthetic database, observes every mutation
|
||
without reload, verifies HTTP success and browser error collectors, measures
|
||
ticks, point markers and intervals within one rendered pixel, switches to an
|
||
empty night and back, then reloads and compares the persisted Agenda.
|
||
The PDF suite separately asserts complete French and English vocabularies,
|
||
accent-preserving WinAnsi text, human-readable duration and plural forms,
|
||
localized dates, medication dose snapshots, pagination and the exact ordered
|
||
geometry of sleep / 45-minute long-awakening / sleep intervals. PDF rows reuse
|
||
the same projected sleep/awake/point/medication primitives as agenda and HR;
|
||
subset summaries cannot inherit facts from unselected nights. The same real
|
||
Firefox scenario exports a seven-night French PDF from the embedded production
|
||
bundle, extracts its text with `pdftotext` and rasterizes its A4 landscape page
|
||
with `pdftoppm` for retained visual inspection.
|
||
The 0.1.7 PDF polish tests also assert newest-first night and sport day order,
|
||
chronological sessions within each day, unchanged chronological intervals and
|
||
medication intakes within a night, 7/14/21/28-night pagination, and absence of
|
||
global validation warnings. Web
|
||
tests keep the exact-revision publication action and editing while excluding
|
||
publication labels from agenda rows.
|
||
|
||
The completed-sport Sleep overlay is covered by C read-only history
|
||
intersection/pagination checks, Web pagination and failure-state tests, and
|
||
pure geometry tests for 05:00 placement, midnight, 18:00 clipping, missing and
|
||
invalid ends, and the Europe/Paris fall offset. PDF tests assert vector blue
|
||
bands, black outlines, factual labels, and unchanged Sleep totals. Synthetic
|
||
Firefox validation uses an isolated schema-v37 database and the embedded
|
||
`-Dweb=enabled` binary, then inspects a downloaded PDF rendered by `pdftoppm`.
|
||
Synthetic French and English reports of 7, 14, 21, and 30 nights are rasterized
|
||
for page, margin, and monochrome inspection. No personal health artifact is
|
||
stored in the repository.
|
||
The private PC/Web deployment was checked separately after the rollout switch:
|
||
the running executable and the served JavaScript/CSS hashes matched the
|
||
`e2c07f1` bundle. Read-only Firefox inspection found 9 blue segments across
|
||
11 existing Sleep rows; a downloaded three-page PDF contained the same sampled
|
||
factual start time and a printable sport listing. Its private images and PDF
|
||
remain outside the repository.
|
||
|
||
`WEB_DASHBOARD_TILES_V1` adds strict TypeScript parsing for every consumed
|
||
field and Vitest fixtures confined to test sources. Coverage proves the 90-day
|
||
activity map and direct totals, all compact/medium/large disclosure levels,
|
||
legacy 0 kg visibility, `improved` use, unfinished-session duration absence,
|
||
1/3/8 MAX bounds, factual muscle bars, unavailable cardio/next-session states,
|
||
global loading/error/invalid signals, accessible textual equivalents and live
|
||
density changes during keyboard resize.
|
||
|
||
`WEB_DASHBOARD_VISUALIZATIONS_V1` adds Vitest coverage for lazy chart presence
|
||
by tile size, factual progression tooltips, one/multiple/legacy-zero points,
|
||
accessible series descriptions, the complete canonical BODY ZONES SVG mapping,
|
||
unmapped aggregate preservation, deterministic session-only intensity levels,
|
||
front/back views, keyboard-focusable regions and factual muscle tooltips. The
|
||
existing Activity, MAX, session, unavailable, Footer and Grid/Layout tests
|
||
remain unchanged and passing.
|
||
|
||
`WEB_DASHBOARD_V1_FINAL_REVIEW` adds explicit value-axis coverage for an empty
|
||
defensive domain, one point, identical points, low amplitude, normal amplitude
|
||
and a legacy zero. Every observation must remain within the computed bounds and
|
||
a positive constant series must have visible space on both sides. Tile-density,
|
||
BODY ZONES text preservation, edit handles, keyboard editing and responsive
|
||
non-persistence remain covered by the existing Grid and tile suites.
|
||
|
||
## 2. Frozen Trainlog JSON v1
|
||
|
||
Run:
|
||
|
||
```bash
|
||
python tools/validate_json.py
|
||
```
|
||
|
||
It validates:
|
||
|
||
- `examples/session-v1.json`;
|
||
- all positive fixtures;
|
||
- all negative fixtures.
|
||
|
||
A negative fixture passes only when Trainlog rejects it.
|
||
|
||
Android field-workflow regression coverage additionally permutes distinct and
|
||
duplicate active occurrences, checks stable IDs/equipment/targets/actuals and raw
|
||
form state, recreates the repository, injects a transactional write failure, and
|
||
finalizes the reordered draft. Completed-session tests edit reps, loads, set and
|
||
continuous durations, distance, and speed; preserve feedback/follow-up roots;
|
||
exercise atomic rollback; and inspect the corrected V3 export identity.
|
||
Completed-order coverage also exercises duplicate exercises, SETS, CONTINUOUS,
|
||
MAX, planning, equipment, feedback revisions, J+1 follow-up, Cancel/no-write,
|
||
successful position persistence, injected Save rollback, and desktop V3
|
||
reconciliation without a duplicate canonical session.
|
||
Field-input coverage maps one accumulated drag directly from index 5 to 1 and
|
||
between both list extremes, restores the origin permutation on cancellation,
|
||
and checks decimal editing/final parsing for both separators. It covers `0.3`,
|
||
`0,3`, quarter-unit values, whole values, incomplete separator states, malformed
|
||
text, non-finite spellings, negative final distance, stored numeric equality,
|
||
and locale-independent V3 JSON publication.
|
||
|
||
Semantic validation includes:
|
||
|
||
```text
|
||
stable-ID uniqueness
|
||
normalized exercise-name uniqueness
|
||
timestamp offsets
|
||
end > start
|
||
catalog/reference equality
|
||
tracking-mode consistency
|
||
load-mode/weight consistency
|
||
unknown-field rejection
|
||
non-blank notes
|
||
zero actual repetitions allowed
|
||
```
|
||
|
||
## 3. Import reconciliation contract
|
||
|
||
Run:
|
||
|
||
```bash
|
||
python tools/validate_import_contract.py
|
||
```
|
||
|
||
Coverage includes:
|
||
|
||
```text
|
||
same ID + same name/profile -> reuse
|
||
same ID + renamed display text -> controlled reuse/warning
|
||
same ID + incompatible mode -> reject
|
||
different ID + equivalent normalized name -> reject
|
||
new unique identity -> create
|
||
```
|
||
|
||
That command validates the frozen Trainlog JSON V1 importer contract. Active
|
||
mobile V3 synchronization has a separate, stricter safe-reconciliation policy
|
||
covered below; it does not modify the frozen V1 expectation.
|
||
|
||
## 4. Desktop Meson suite
|
||
|
||
Current registered set (Meson executes tests in parallel, so displayed order is
|
||
not contractual):
|
||
|
||
```text
|
||
database
|
||
catalog
|
||
equipment_catalog
|
||
body_zones
|
||
training_knowledge
|
||
session_generation
|
||
session_generation_policy_validation
|
||
training_context
|
||
custom_equipment
|
||
session_detail
|
||
duration
|
||
body_metrics
|
||
bodyviz
|
||
exercise_performance
|
||
session_type_schema
|
||
session_edit
|
||
body_observation_edit
|
||
usb
|
||
mtp
|
||
exercise_profile_schema
|
||
continuous_session
|
||
continuous_detail
|
||
reps
|
||
variable_sets
|
||
schema_v5_migration
|
||
schema_v9_migration
|
||
schema_v7_migration
|
||
timestamp_validation
|
||
mobile_import_variable_sets
|
||
mobile_import_multi_occurrence
|
||
session_exchange_v3
|
||
equipment_associations_exchange
|
||
equipment_definitions_exchange
|
||
exercise_reconciliation
|
||
body_zone_sync
|
||
body_zone_catalog_validation
|
||
sync_direction
|
||
sync_history
|
||
sync_screen_action
|
||
measured_max
|
||
max_results
|
||
max_sync
|
||
body_analytics
|
||
terminal_input_event_type_policy
|
||
tui_workflows
|
||
app_shell
|
||
presentation_i18n
|
||
```
|
||
|
||
The desktop suite includes APP_SHELL_V1 production-transition coverage and the
|
||
AI-draft exchange regressions. The configured suite size and latest result are
|
||
recorded once in `current_state.md`; the following generator-specific checkpoint counts remain
|
||
historical evidence:
|
||
|
||
```text
|
||
45/45 Meson tests PASS
|
||
```
|
||
|
||
`SESSION_GENERATOR_V1` validation covered policy shape and shared fixtures,
|
||
full-history exposure/recency boundaries, selection and observed-load anchors,
|
||
Android v10 -> v11 planning migration, V3 round trips and malformed-V3
|
||
priority, and ordinary draft/editor acceptance. The checkpoint also passed 4/4
|
||
selected ASan/UBSan tests and Android 73 tests with zero failures/errors; one
|
||
known historical real-v9 fixture skipped while the structural v10 migration
|
||
test executed and passed. No hardware MTP or real app-upgrade/install validation
|
||
is claimed for this checkpoint.
|
||
|
||
The completed post-repair matrix passed 45/45 Meson tests, named ASan/UBSan
|
||
4/4, and Android 75 tests with zero failures/errors and one unavailable
|
||
external Android-v9 fixture skip. The structural Android v10 planning migration
|
||
executed and passed. Validators, strict C17 headers, deterministic policy/
|
||
fixture/knowledge regeneration, and APK asset byte comparisons passed. The
|
||
earlier sanitizer invocation that selected no tests is not used as evidence.
|
||
|
||
The desktop executable is additionally smoke-checked in isolated tmux PTYs at
|
||
100x30, the exact 72x20 minimum, and the 60x15 fallback; a resize down/up must
|
||
recover before a clean keyboard quit. Notcurses is verified as the executable's
|
||
direct terminal dependency with `readelf -d`.
|
||
|
||
`presentation_i18n` covers the deterministic French default, French/English
|
||
catalogue lookup, immediate in-process language switching, selected-language
|
||
decimal/date formatting even under a contrary host numeric locale, unchanged
|
||
layout at the 72x20 minimum, and local XDG configuration persistence/failure
|
||
semantics. It also proves that persisted catalogue names are not translation
|
||
keys, the source-derived `TRANSLATABLE_UI=0` boundary, and that synchronization
|
||
presentation uses local typed status/counters rather than raw opaque
|
||
core/receipt/history summaries.
|
||
|
||
Android Robolectric coverage verifies both resource locales, Settings language
|
||
ownership and dedicated SharedPreferences persistence, French fallback for an
|
||
absent/invalid preference, and localized presentation of known stable BODY ZONE
|
||
IDs without changing unknown catalogue labels or repository values. Resource
|
||
parity and typed synchronization-receipt presentation are also covered; raw
|
||
protocol/operational summaries remain opaque. Final i18n validation passed
|
||
60/60 normal desktop tests, 60/60 ASan/UBSan desktop tests, and 202 Android
|
||
tests (198 passed, 4 skipped, 0 failed), with both JSON validators passing.
|
||
Automated coverage does not replace a real device/manual visual language-switch
|
||
smoke; that remains explicit manual validation.
|
||
|
||
`WEB_DASHBOARD_CHARACTERIZATION_V1` now exercises the production Core service
|
||
directly through a real in-memory SQLite database, without including `tui.c` or
|
||
linking Notcurses. Its explicit fixed query instant
|
||
covers an empty training history, exact rolling-window inclusion immediately
|
||
before/at/after the 30-day boundary, valid actual history without `ended_at`,
|
||
and plan-only exclusion. It covers repeated occurrences, heterogeneous REPS
|
||
sets, SETS/DURATION, CONTINUOUS/DURATION without fake sets, assistance,
|
||
unweighted actuals, ordinary heavy sets versus explicit MAX, equal-instant
|
||
tie-breaks (including equal same-session performance points), the legacy
|
||
unweighted `0.0 kg` prior baseline, primary-only catalogue zones,
|
||
secondary-zone exclusion, and seeded catalogue behavior. Exact and exceeded
|
||
bounds are asserted for 128 observable
|
||
sessions and 4096 Dashboard facts, including the independently truncated totals
|
||
and `partial` flag. Existing regressions continue to cover malformed legacy
|
||
timestamps, body-metric selection, responsive rendering, all-history buckets,
|
||
strictly earlier exact-dose improvement, and explicit-MAX separation.
|
||
The standalone `dashboard_header` target also proves that
|
||
`trainlog/dashboard.h` compiles independently as C17. TUI workflow regressions
|
||
separately verify service adoption and snapshot presentation.
|
||
|
||
Notable regression coverage:
|
||
|
||
- transactional persisted-session replacement;
|
||
- exercise removal from a session;
|
||
- body-observation stable-identity editing;
|
||
- profile-aware exercise constraints;
|
||
- continuous activity without fake sets;
|
||
- table-only desktop SETS workflow: planning does not create actual rows,
|
||
explicit add requires an actual metric, and normal zero-row completion is
|
||
rejected; Android legacy compact-draft decoding remains separately covered;
|
||
- direct v4 -> current database migration and v7 -> v8 custom-equipment migration;
|
||
- bounded v8 -> v9 explicit-max migration, including ambiguous-attempt preservation;
|
||
- lossless v9 -> v10 `performed_sets` rebuild: historic NULL and positive
|
||
weights/IDs/owners/positions/metrics survive, explicit zero is accepted, and
|
||
injected failure rolls back with integrity, foreign-key and enforcement
|
||
checks;
|
||
- additive desktop v10 -> v11 and Android v9 -> v10 body-zone migrations:
|
||
exact stable row identities and history remain unchanged while only proven
|
||
manifest mappings are seeded; the Android fixture also preserves an active
|
||
draft, per-set loads, explicit MAX, a body observation and a custom equipment
|
||
definition;
|
||
- canonical body-zone validation rejects duplicate IDs/order, missing parents,
|
||
cycles, group/leaf disagreement, a taxonomy other than the exact V1 manifest,
|
||
invalid `full_body`, boolean versions, malformed stable IDs, and direct group
|
||
assignment;
|
||
- one primary/multiple secondary persistence, duplicate/unknown/group/orphan-
|
||
secondary rejection, primary/secondary exclusivity, edit, unclassified and
|
||
reopen;
|
||
- exact and parent-descendant zone filters, lower-body leaf coverage,
|
||
primary-only participation, normalized-prefix composition and custom rows;
|
||
- body-zone companion export/import, empty mapping, identical replay,
|
||
one-sided update, simultaneous conflict rollback and source-V2-proven
|
||
exercise-ID reconciliation without name-only inference; custom-exercise
|
||
publication establishes both peer baselines before a reverse one-sided edit;
|
||
malformed `ex_<uuid-v4>` identities and timestamps without offsets are
|
||
rejected;
|
||
- heterogeneous mobile-set import;
|
||
- per-set load persistence and correction: ordered rows retain mixed actual
|
||
repetitions, nullable loads, positions and assistance semantics through
|
||
desktop replacement and detail retrieval;
|
||
- V2 mobile round-trip and idempotent replay preserve each ordered set's own
|
||
nullable `weight_kg`, without collapsing it to an occurrence target;
|
||
- V2 explicit-max Android -> desktop -> Android replay and resumed same-session update;
|
||
- Notcurses input lifecycle translation: PRESS/REPEAT are actionable while a
|
||
RELEASE event is consumed without creating a second navigation action.
|
||
- targetless mobile SETS persistence;
|
||
- mobile-import idempotence.
|
||
- V2 mobile import with repeated exercise occurrences and stable `entry_id`;
|
||
- companion equipment import after session import, including idempotent
|
||
reimport and independent associations for repeated occurrences;
|
||
- rejection of the historical equipment-import invocation without its required
|
||
`--database` target, followed by the corrected complete V2 export chain.
|
||
- stable-ID mobile-to-desktop rename reconciliation without duplicate catalog
|
||
rows or historical-reference replacement.
|
||
- supplied catalogue search/detail, local custom-equipment creation and
|
||
selection, resolution of supplied/local occurrence links, and explicit
|
||
visibility of an unknown historic equipment reference;
|
||
- strict custom-equipment definitions V1 import/export: reserved supplied IDs,
|
||
additive omission, equal reimport, and divergent same-ID conflict;
|
||
- three-mode shared-engine plans and their directional receive/publish bounds;
|
||
- Sync-page action dispatch: direct `a`/`p`/`b` confirmation, one `Enter` run,
|
||
`Esc` cancellation without a run, refresh-only `r`, inert retired `s`, and
|
||
equivalent full/compact footer direction labels;
|
||
- definition-first ordering before V2 artifacts, so associations can resolve
|
||
custom IDs without changing V2 shapes.
|
||
- exercise reconciliation with identical profiles, compatible subset/superset
|
||
masks, explicit incomparable-profile rejection, current Marche identities,
|
||
preservation of two historic occurrences and their `entry_id`/continuous
|
||
values, ordered custom-equipment definitions/catalog/mobile/association
|
||
replay, complete definitions/mobile/associations/body import, outbound
|
||
publication, and stable second replay;
|
||
- a production PC-exporter to Android-importer regression over definitions,
|
||
catalog, mobile V3, and association V2 artifacts: the fixture includes
|
||
Marche, Leg press, two Marche occurrences, per-set loads, body data, a
|
||
durable draft, and custom equipment; after the first import, both the second
|
||
and third imports report zero additions and an exact snapshot of every
|
||
Android business table remains unchanged;
|
||
- synchronization summary reporting where a reconciliation-only result keeps
|
||
`+0 exercice(s)` and only an actual inserted row produces `+1`;
|
||
- all fourteen body metrics through the same complete reconciliation import;
|
||
- desktop-created occurrence identities use `sxe_<uuid-v4>`, not the `sy_`
|
||
synchronization-run prefix.
|
||
|
||
## 5. Build
|
||
|
||
### Isolated synchronization validation environment
|
||
|
||
`TRAINLOG_SYNC_TEST_ENV_V1=PASS/FROZEN` provides one repository-owned entry
|
||
point for the desktop and Android validation needed by synchronization work:
|
||
|
||
```bash
|
||
python3 tools/validate_sync_isolated.py --suite preflight
|
||
python3 tools/validate_sync_isolated.py --suite smoke
|
||
python3 tools/validate_sync_isolated.py --suite full --jdk /usr/lib/jvm/java-17-openjdk
|
||
```
|
||
|
||
`TRAINLOG_SYNC_DATA_LIFECYCLE_V1` adds schema v19/v18 migration assertions and
|
||
Android lifecycle coverage for confirmed-only serialization, persistent
|
||
pending state, occupied-singleton refusal, explicit activation, identity-
|
||
preserving idempotent finalization and stale replay. The closeout adds
|
||
`v4HistoryRoundTripsAcrossRealAndroidAndDesktopImplementations` and
|
||
`executionDraftRoundTripsAcrossRealAndroidAndDesktopImplementations`. Their
|
||
bridge provisions only synthetic schema and invokes the real Android and
|
||
desktop staged entry points; it does not reconstruct wire data. The final
|
||
isolated run passed 75/75 normal native tests, 75/75 ASan/UBSan native tests
|
||
and 207 Android tests (203 passed, four unchanged historical-fixture skips),
|
||
plus both Python validators and `assembleDebug`. These are artifact/repository
|
||
proofs; they do not claim transport activation or global synchronization
|
||
atomicity.
|
||
|
||
The 75-versus-73 inventory contains no removed, renamed, merged, or disabled
|
||
native tests. With `web=auto` and no `web/node_modules`, Meson selects 73 tests
|
||
and omits exactly `web_frontend_typecheck` and `web_frontend`. After `npm ci`
|
||
from the locked package file and Meson reconfiguration, both the normal and
|
||
ASan/UBSan selections contain and pass all 75. Clang 22.1.8 is the compiler
|
||
used for those green builds. GCC 16.2.1 fails both baseline `77c1517` and the
|
||
lifecycle branch under the unchanged strict `-Werror` policy on
|
||
`-Wmisleading-indentation` in unchanged `json_writer.c`,
|
||
`web_dashboard_json.c`, and `web_dashboard.c`; this is a pre-existing
|
||
compiler-version limitation, not lifecycle evidence and not a relaxed gate.
|
||
|
||
Use `--jdk /absolute/jdk17/home` when auto-detection is unsuitable and
|
||
`--run-parent /absolute/private/parent` to select another parent outside the
|
||
problematic system `/tmp`. `--keep-run-dir` retains a successful run for
|
||
diagnosis; failures and interruptions are always retained. Without that flag, a
|
||
successful run removes only its own validated `run-*` directory.
|
||
|
||
Each invocation creates a mode-0700 unique root under
|
||
`${XDG_CACHE_HOME:-$HOME/.cache}/trainlog/test-runs/`, then gives its children
|
||
private HOME, XDG data/config/cache/runtime, native/JVM tmp, exchange, database
|
||
and report directories. Test-sensitive files and logs are mode 0600. The
|
||
already-provisioned `${GRADLE_USER_HOME:-$original_HOME/.gradle}` dependency
|
||
cache is deliberately shared; no Trainlog application data, rclone credential,
|
||
keystore or production XDG path is shared. This is path/environment isolation,
|
||
not a system sandbox.
|
||
|
||
The harness verifies Java 17 with `java -version`, then verifies both Gradle's
|
||
launcher and daemon JVM. `JAVA_TOOL_OPTIONS=-Djava.io.tmpdir=<run>/tmp` reaches
|
||
the forked Robolectric JVM; `IsolatedTestEnvironmentTest` checks the effective
|
||
property and every private path from inside that JVM. Android test tasks use
|
||
`--rerun-tasks`, so an up-to-date or cached task cannot masquerade as a new
|
||
execution. No default mode invokes rclone, adb, libmtp synchronization,
|
||
`trainlog-sync-once`, `connectedDebugAndroidTest`, a user service or a
|
||
production lock/database.
|
||
|
||
The modes are:
|
||
|
||
- `preflight`: tools, writable parent, JDK 17, Gradle launcher/daemon and paths;
|
||
- `smoke`: preflight, production-path sync-gap characterization and the Android
|
||
JVM isolation test;
|
||
- `full`: normal and ASan/UBSan desktop suites, explicit characterization,
|
||
JSON/import validators, targeted draft lifecycle/export test, complete
|
||
Android unit suite and `assembleDebug`.
|
||
|
||
Logs are private and capped at 8 MiB per command. Exit codes distinguish `0`
|
||
success, `2` blocked preflight, `3` missing dependency, `4` environment failure,
|
||
`5` business-test/command failure and `130` interruption. Commands, exit codes,
|
||
JDK, non-secret paths and Android test/pass/fail/skip counts are recorded in
|
||
`reports/`; the full environment and credentials are never printed. The
|
||
harness terminates only process groups that it started, waits before forced
|
||
termination, never stops a pre-existing Gradle daemon or Trainlog service, and
|
||
refuses cleanup outside its exact root or across a symlink.
|
||
|
||
At the 2026-09-17 reference checkpoint, the Android total retained four skips
|
||
because optional historical database fixtures were unavailable: one v9, one
|
||
v12, one v13, and one v14 migration-fixture case. These are explicit fixture
|
||
coverage gaps, not passing migration executions. The isolated harness does not
|
||
run hardware MTP, real Drive, or Android instrumented tests; those remain
|
||
separate validation classes.
|
||
|
||
Stable-release validation additionally builds the signed release variant and
|
||
checks the packaged product versions and binary linkage:
|
||
|
||
The release machine supplies all four `TRAINLOG_RELEASE_*` environment
|
||
variables, or a mode-0600 private file at
|
||
`~/.config/trainlog/release-signing.properties` with this shape:
|
||
|
||
```text
|
||
storeFile=/absolute/private/path/trainlog-release.jks
|
||
storePassword=<local secret>
|
||
keyAlias=<local alias>
|
||
keyPassword=<local secret>
|
||
```
|
||
|
||
`TRAINLOG_RELEASE_CREDENTIALS_FILE` may select another private properties file.
|
||
Environment variables take precedence over file values. The keystore and
|
||
credentials are the durable Android update identity: back them up through an
|
||
approved encrypted external mechanism, never Git or release assets. Missing
|
||
configuration makes `packageRelease` fail explicitly rather than producing an
|
||
apparently publishable unsigned APK.
|
||
|
||
```bash
|
||
meson compile -C build
|
||
meson test -C build --print-errorlogs
|
||
|
||
cd android
|
||
JAVA_HOME=/usr/lib/jvm/java-17-openjdk ./gradlew test
|
||
JAVA_HOME=/usr/lib/jvm/java-17-openjdk ./gradlew assembleRelease
|
||
|
||
apksigner verify --verbose --print-certs \
|
||
app/build/outputs/apk/release/app-release.apk
|
||
|
||
file trainlog-tui-linux-x86_64-v<version>
|
||
ldd trainlog-tui-linux-x86_64-v<version>
|
||
sha256sum trainlog-android-v<version>.apk \
|
||
trainlog-tui-linux-x86_64-v<version>
|
||
```
|
||
|
||
An unsigned release APK is build evidence only and must not be published as the
|
||
canonical stable Android artifact. Release assets must come from the exact
|
||
tagged source state, and the tag, Android `versionName`, Meson project version,
|
||
TUI product version, asset names, and release title must agree.
|
||
|
||
Private update rollout may set `TRAINLOG_ANDROID_VERSION_CODE` to a positive
|
||
integer above the installed package while retaining the tagged product
|
||
`versionName`. The version-code override is build-only and does not change any
|
||
schema or exchange-format version. Private daily candidates must never
|
||
downgrade the logical product version.
|
||
|
||
For `TRAINLOG_UI_ANDROID_WEB_UNIFICATION_V1`, `lintDebug` was also run from a
|
||
detached clean worktree at the exact `main` baseline and from the mission
|
||
branch. Both runs report the same ten errors: one `NewApi` diagnostic for
|
||
`windowLightNavigationBar`, one `LocalContextConfigurationRead`, and eight
|
||
`StringFormatMatches` diagnostics (four bilingual call sites). Comparison by
|
||
rule and complete diagnostic message reports zero added and zero removed
|
||
errors. No lint rule or severity was changed.
|
||
|
||
```bash
|
||
meson setup --reconfigure build
|
||
meson compile -C build
|
||
meson test -C build --print-errorlogs
|
||
```
|
||
|
||
Strict warning flags remain active. Do not weaken warnings to make a change pass.
|
||
|
||
## APP_SHELL_V1 production-transition coverage
|
||
|
||
The active desktop suite has 58 tests. `app_shell` verifies layout thresholds,
|
||
route/history bounds, overlays and focus restoration, bounded UTF-8 search and
|
||
form input, stable-ID list selection, shared action ordering, and leave guards.
|
||
`tui_workflows` covers the production single event-loop routes and controller
|
||
handoffs: sessions/generator, exercises, equipment, statistics/body/MAX,
|
||
settings, and confirmed synchronization. `custom_equipment` covers the
|
||
bounded deterministic read-only equipment page reader, including pagination,
|
||
invalid arguments, offsets and corrupt values. This coverage replaces former
|
||
nested-screen-loop workflow claims.
|
||
|
||
The Mensurations workflow regression separates the selected observation's
|
||
profile from its metric history: singleton circumference bars, independent
|
||
kg/cm rendering, missing-field omission, a real two-date high-resolution
|
||
Unicode series, historical selection updates, availability-filtered metric
|
||
navigation, and bounded 120x35, 100x30, 80x24 and 72x20 geometry without ASCII
|
||
chart glyphs. The terminal-free `chart` regression exercises zero, one and two
|
||
points; constant, increasing and decreasing series; irregular elapsed-time X
|
||
mapping; Y headroom; clipping and compact rectangles; Braille 2x4 dot encoding;
|
||
interpolated Braille output; and distinct stored-measurement markers.
|
||
|
||
The terminal-free `statistics` regression covers empty, 7-day, 30-day and
|
||
complete windows; actual-session and occurrence counts; sets, repetitions and
|
||
duration; strict external-load volume; same-occurrence planned/actual totals;
|
||
canonical alias reconciliation; primary and secondary persisted zones;
|
||
immediate and session-global feedback scopes; and zero/one/multiple explicit
|
||
MAX histories. `tui_workflows` additionally exercises the five-entry statistics
|
||
hub, period and graph cycling, compact zone bars, canonical exercise detail,
|
||
MAX facts, and bounded common-chart rendering without an interactive terminal.
|
||
|
||
Statistics-refinement regressions distinguish identical observations plotted
|
||
inside 7-day and 30-day domains, clamp non-negative scales to zero, render two
|
||
weekly totals as independent bars, retain empty local-calendar weeks, compare
|
||
unequal zone counts through one shared scale, and verify that the default MAX
|
||
list excludes never-measured exercises until its explicit toggle is used.
|
||
|
||
Period-bucketing regressions cover four consecutive 7-day comparison buckets,
|
||
Monday/Sunday assignment, localized consecutive Gregorian months, retained
|
||
empty middle periods, and boundary assignment without double counting.
|
||
Ordinary February, leap-year February and exact first/last dates verify month
|
||
length without fixed-day arithmetic. Offset-bearing fixtures verify persisted
|
||
civil-date membership independently of the machine timezone. Current partial
|
||
weeks/months, stable zero history, and duration-based adaptive full-history
|
||
aggregation are also covered.
|
||
|
||
The APP_SHELL PTY validation exercises six TUI sizes—72x20, 80x24, 100x25,
|
||
100x30, 120x31 and 120x35—plus help, search clear/close, F6/F7, compact focus,
|
||
resize/overlay restoration, clean exit, and navigation with no temporary
|
||
database write. Normal and ASan/UBSan Meson suites each passed 48/48; the
|
||
current normal and sanitizer real-PTY runs each passed 100/100 checks. The
|
||
sanitizer run used the upstream-prescribed Notcurses compatibility setting
|
||
`ASAN_OPTIONS=use_sigaltstack=0:detect_leaks=1:halt_on_error=1` and reported
|
||
no ASan/UBSan diagnostics.
|
||
|
||
Android unit/assembly evidence records 84 tests, 0 failures, 0 errors and one
|
||
external `TRAINLOG_ANDROID_V9_FIXTURE` skip. Required human checks remain:
|
||
the six TUI sizes above; Android widths 320, 360, 393 and 412 dp; large system
|
||
font; IME forms; durable-draft leave guards; and TalkBack. No emulator was
|
||
available; the daily installed application was not installed over or exercised
|
||
by instrumentation, so these are not marked visually passed.
|
||
|
||
## 6. Android build
|
||
|
||
When Android code changes:
|
||
|
||
```bash
|
||
cd android
|
||
|
||
printf 'sdk.dir=%s\n' "$HOME/Android/Sdk" > local.properties
|
||
|
||
JAVA_HOME=/usr/lib/jvm/java-17-openjdk \
|
||
./gradlew testDebugUnitTest assembleDebug
|
||
```
|
||
|
||
Install to the connected device when hardware behavior changes:
|
||
|
||
```bash
|
||
adb install -r app/build/outputs/apk/debug/app-debug.apk
|
||
```
|
||
|
||
Android repository host tests additionally cover exercise editing:
|
||
|
||
- trimmed rename preserves `exercise_id` and recalculates `normalized_name`;
|
||
- duplicate and invalid names are rejected;
|
||
- completed history and active-draft references resolve the renamed catalog row;
|
||
- a referenced profile change is explicitly rejected;
|
||
- same-ID PC-catalog rename reconciles in place without a duplicate.
|
||
- canonical body-zone taxonomy, create/edit/reopen, parent filters,
|
||
search+filter, unclassified rows, companion replay/update/conflict and
|
||
zone-safe exercise-identity merging.
|
||
|
||
`BodyZoneHomeOverviewRepositoryTest` exercises the production
|
||
`getBodyZoneHomeOverview(now)` query with a caller-supplied deterministic
|
||
instant. It covers the settled chest/back/shoulders/thighs example,
|
||
secondary-only exposure remaining weaker than primary, stable ranking,
|
||
unsupported calves exclusion, parent-zone non-participation, retired alias A
|
||
to canonical B counted once, active/resolved availability, honest empty
|
||
history, inclusive exact 7/30-day instant boundaries, and target-only exclusion.
|
||
`BodyZoneHomePresentationTest` covers concise evidence and last-primary age
|
||
wording without recovery/fatigue/prescription language.
|
||
`BodyZoneHomeSemanticsTest` renders the production section at 288 dp content
|
||
width (the constrained 320 dp-screen case) with 1.6x font scale and at a
|
||
practical 380 dp content width. It verifies every view-specific Face/Dos zone
|
||
and textual state, Button actions, selected/not-selected transitions, leaf
|
||
canvases with no anatomical text children, exact normalized-path containment,
|
||
a real Canvas tap selecting core, and both existing-route callbacks. The Home
|
||
implementation keeps side-by-side maps with separate region-aligned, at-least
|
||
48 dp semantic controls; 320/360/393/412 dp and TalkBack visual checks remain
|
||
explicit device/emulator validation.
|
||
|
||
Focused host command:
|
||
|
||
```bash
|
||
cd android
|
||
JAVA_HOME=/usr/lib/jvm/java-17-openjdk \
|
||
./gradlew :app:testDebugUnitTest \
|
||
--tests com.labfytools.trainlog.data.BodyZoneHomeOverviewRepositoryTest \
|
||
--tests com.labfytools.trainlog.ui.BodyZoneHomePresentationTest \
|
||
--tests com.labfytools.trainlog.ui.BodyZoneHomeSemanticsTest
|
||
```
|
||
|
||
## 7. Hardware MTP validation
|
||
|
||
Hardware probes and real synchronization are separate from the normal automated
|
||
suite because a test runner cannot assume an unlocked MTP phone.
|
||
|
||
Available probe binaries include:
|
||
|
||
```text
|
||
trainlog-usb-probe
|
||
trainlog-mtp-probe
|
||
trainlog-mtp-exchange-probe
|
||
trainlog-mtp-roundtrip-probe
|
||
trainlog-mtp-mobile-export-probe
|
||
```
|
||
|
||
Previously established physical baseline:
|
||
|
||
```text
|
||
USB_MTP_DETECTION=PASS
|
||
MTP_STORAGE_ACCESS=PASS
|
||
MTP_WRITE=PASS
|
||
MTP_LIST_FOLDER=PASS
|
||
MTP_READ=PASS
|
||
MTP_ROUNDTRIP=PASS
|
||
```
|
||
|
||
The automated `sync_direction` regression also covers selection of the newest
|
||
Android V2 export, equipment-definitions V1 artifact, and equipment-association
|
||
V2 artifact when scoped storage has retained collision-suffixed sibling files.
|
||
It rejects non-versioned/
|
||
non-numeric names and uses deterministic ties; JSON validation remains part of
|
||
the normal import path. `mobile_import_multi_occurrence` exercises the real
|
||
definition importer before the V2 session importer, including idempotent
|
||
reimport and rejection of a missing custom definition.
|
||
|
||
## 8. Bidirectional synchronization validation
|
||
|
||
`sync_gap_characterization` uses the production mobile V3 exporter/importer and
|
||
temporary SQLite databases to prove the current gaps without blessing them: V3
|
||
omits `ended_at`, three distinct note owners and the body-observation session
|
||
link; the imported values are `NULL`; deleting a local session and replaying an
|
||
old snapshot resurrects it; an invalid artifact rolls back while an earlier
|
||
artifact remains independently committed. The existing Android test
|
||
`TrainlogRepositoryDraftTest.finalizeIsAtomicAndDraftNeverExportsBeforeCompletion`
|
||
proves that the active draft is absent before finalization and the completed
|
||
session appears afterward. These tests do not implement the future contract.
|
||
|
||
`TRAINLOG_SYNC_CHARACTERIZATION_V1=PASS/FROZEN` completes the missing current-
|
||
behavior evidence without changing production code, schemas, or formats. The
|
||
coverage matrix is assertion-based; “future” rows remain criteria rather than
|
||
passing tests:
|
||
|
||
| Current behavior | Test and production boundary | Significant assertions | Coverage |
|
||
|---|---|---|---|
|
||
| V3 omissions, new-row loss, replay resurrection, and per-artifact rollback | `tests/test_sync_gap_characterization.py` through the real desktop V3 importer/exporter | Wire fields are absent; newly imported columns are `NULL`; an identical replay preserves local-only values; a V3 correction rebuilds its occurrence and loses its local-only occurrence note; replay after deletion resurrects the session; malformed input rolls back only its artifact | Existing proof extended |
|
||
| Active draft lifecycle | `TrainlogRepositoryDraftTest.finalizeIsAtomicAndDraftNeverExportsBeforeCompletion` through the real Android repository/exporter | No draft rows export; atomic finalization removes the draft and makes the completed session exportable | Existing proof reused |
|
||
| Cross-implementation V3 round trip | `TrainlogRepositoryDraftTest.androidV3RoundTripUsesRealDesktopImporterExporterAndFreshAndroidDatabase`, with `tests/support/roundtrip_desktop_v3.py` invoking the real desktop tools | A fresh Android database reconstructs stable session/entry/exercise/equipment identities, repeated occurrences, occurrence and set order, heterogeneous sets, targets, continuous values, and explicit MAX; exact replay adds nothing | Added |
|
||
| V3 correction and forbidden rebinding | Existing desktop `session_exchange_v3` and Android repository cases | Accepted same-identity correction is reported separately from insertion; conflicting `entry_id` rebinding is rejected | Existing proof reused |
|
||
| Companion identity and revision chains | Existing aliases, equipment, BODY ZONES, causal profile-state, and feedback suites | Stable foreign references, immediate-feedback roots, immutable revisions, session follow-ups, and replay behavior remain exact | Existing proof reused |
|
||
| Inter-artifact partial failure | Existing Android inbox ordering tests and `sync_gap_characterization` | Earlier valid catalogue/components remain committed when a later session/companion is invalid; intra-artifact invalid content rolls back | Existing proof reused |
|
||
| Desktop delete-before-send publication | `sync_body_zone_wiring` through `trainlog_sync_run()` and the MTP double | The old remote catalogue is deleted, the injected send fails, the run returns an error, and no replacement exists | Added |
|
||
| Selected-invalid V3 priority | `TrainlogRepositoryDraftTest.inboxPresentMalformedV3DoesNotFallBackToValidV2` through the Android inbox importer | A present malformed V3 fails and is not masked by a valid V2 | Existing proof reused |
|
||
| Request, receipt, and processed marker | `sync_body_zone_wiring` through `trainlog_sync_run()` | Receipt and report carry the exact `request_id`; identical request replay is refused; receipt-send failure leaves the prior processed marker unchanged | Added |
|
||
| Android post-receipt consumption | Desktop request/receipt proof plus Android typed receipt/inbox component tests | Desktop processing success and Android artifact-import success are separate component results; V1 has no durable peer-consumption acknowledgement | Component-scoped only |
|
||
| Inter-process lock | `sync_body_zone_wiring` against the real `sync.lock` acquisition with private `XDG_DATA_HOME` | A pipe-coordinated child holds the lock; the second trigger returns conflict before any MTP probe; every child is joined | Added |
|
||
| Enriched V4 completed history | `TrainlogRepositoryDraftTest.v4HistoryRoundTripsAcrossRealAndroidAndDesktopImplementations` through Android V4 repository methods and the real desktop V4 importer/exporter | Android → desktop → fresh Android and desktop → Android → fresh desktop preserve stable identities, order, heterogeneous sets, plans, equipment, causal null/empty/escaped UTF-8 notes and idempotent replay | Added cross-implementation proof |
|
||
| Execution-draft V1 | `TrainlogRepositoryDraftTest.executionDraftRoundTripsAcrossRealAndroidAndDesktopImplementations` through Android repository methods and `execution_draft_exchange.py` | Both implementation directions preserve stable session/entry identity and confirmed facts; raw unfinished form text remains local; replay is unchanged and pre-finalization replay is stale after reopen/finalization | Added cross-implementation proof |
|
||
| General tombstones, coherent generations, causal deletion, and consumption acknowledgement | Causal-deletion suites plus `sync_generation_exchange` and `SyncGenerationServiceTest` | Explicit staged production entry points pass cross-implementation, rollback, replay, bounds and restart assertions; automatic V3 remains unchanged | PASS/FROZEN staged evidence |
|
||
|
||
The cross-implementation helpers only provision minimal production-compatible
|
||
desktop schemas and launch production Python tools; they do not duplicate
|
||
their business rules. Kotlin tests use real Android exporters and importers on
|
||
distinct databases. Comparisons remove only volatile `generated_at`,
|
||
canonicalize object-key and unordered top-level collection order, and retain
|
||
occurrence/set array order as business data.
|
||
|
||
Request/receipt evidence remains deliberately bounded: the native test proves
|
||
desktop correlation, publication, replay handling, and marking order, while
|
||
Android tests prove local receipt presentation and inbox failure behavior. A
|
||
receipt still proves desktop processing only, not durable Android consumption.
|
||
README, `architecture.md`, and `sync_exchange.md` were reviewed for this lot and
|
||
need no correction; their current ownership and V1 limitations remain accurate.
|
||
|
||
The 2026-09-17 closing run used all three documented harness modes with the
|
||
explicit JDK 17 path. The final `full` mode passed 75/75 normal Meson tests,
|
||
75/75 ASan/UBSan Meson tests, both validators, the targeted draft lifecycle
|
||
test, Android debug assembly, and 204 Android tests (200 passed, 4 historical
|
||
fixture skips, 0 failed/error). No hardware, Drive, adb, instrumented test, or
|
||
user service was exercised.
|
||
|
||
Validated workflow:
|
||
|
||
```text
|
||
Android
|
||
-> Synchroniser maintenant
|
||
-> unique sr_ request
|
||
-> trainlog-syncd
|
||
-> shared engine
|
||
-> Android -> PC import
|
||
-> PC -> Android catalog
|
||
-> sy_ structured run
|
||
-> matching receipt
|
||
-> Android final status
|
||
```
|
||
|
||
Multiple distinct Android request IDs were processed successfully without
|
||
reprocessing one request as a new one.
|
||
|
||
The TUI also invokes the same engine manually and exposes structured run
|
||
details.
|
||
|
||
`post_sync_ai_drive` validates strict export-before-upload ordering, skips
|
||
upload after export failure, and reports unavailable/failing rclone. The
|
||
production sync wiring test exercises successful TUI and Android triggers
|
||
through `trainlog_sync_run()`, uses an isolated fake rclone, and proves that
|
||
failed sync runs do not invoke Drive publication. Post-sync failures are
|
||
non-fatal to the already successful Trainlog sync by contract.
|
||
|
||
`ai_session_draft` validates the strict desktop source parser and schema v18
|
||
transaction: exact/unknown-key rejection, `aid_` identity, alias resolution,
|
||
target-only SETS compatibility, 64-entry/99-set/999-rep bounds, exact replay
|
||
skip, changed-payload conflict, deterministic 256-at-a-time publication across
|
||
257 drafts, failed-publication retry, permanent import ledger, and full rollback on
|
||
an injected child failure. Its fake-rclone seam also verifies that the exact
|
||
fetched bytes are archive-copied only after import, same-name inbox replacement
|
||
is preserved, stale temporary content cannot import, and archive/status failure
|
||
converges through idempotent logical replay. Production orchestration
|
||
tests retain `DRIVE_FAIL`, fetched-invalid `REJECTED`, and `ARCHIVE_FAIL` in a
|
||
successful report/history. Android repository tests cover the 256-proposal
|
||
companion limit, full validation before replay skip, strict optional-text
|
||
trimming, replay idempotence, start/delete
|
||
tombstones, target-only copy to the unchanged singleton active draft, and
|
||
catalog/profile/equipment prerequisite ordering. Focused Android UI wiring tests
|
||
also cover pending-only deterministic listing, started/deleted exclusion, the
|
||
always-visible **Brouillons** count without losing manual/history actions,
|
||
proposal title/target details, inert display with no automatic active draft,
|
||
external-revision refresh after companion import, and the existing transactional
|
||
start path. These are automated fake
|
||
transport tests; no real Drive automated test exists. The final real Drive plus
|
||
Android-triggered bidirectional smoke test remains manual and
|
||
`TRAINLOG_AI_SESSION_DRAFT_V1=VALIDATION_PENDING`.
|
||
|
||
A September 2026 connected-device inspection established that the previously
|
||
installed 2026-09-13 APK still owned a schema-v16 database without
|
||
`ai_session_drafts`; it could not import the companion. Installing the current
|
||
APK and synchronizing once is therefore a prerequisite for the remaining real
|
||
acceptance test, not evidence that the acceptance test has passed.
|
||
|
||
## 9. Sanitizers
|
||
|
||
For meaningful C checkpoints:
|
||
|
||
```bash
|
||
CC=clang meson setup build-asan \
|
||
-Db_sanitize=address,undefined \
|
||
-Db_lundef=false
|
||
|
||
meson compile -C build-asan
|
||
meson test -C build-asan --print-errorlogs
|
||
```
|
||
|
||
## 10. Pre-push checklist
|
||
|
||
```bash
|
||
meson compile -C build
|
||
meson test -C build --print-errorlogs
|
||
|
||
python tools/validate_json.py
|
||
python tools/validate_import_contract.py
|
||
|
||
git diff --check
|
||
git status --short
|
||
```
|
||
|
||
When Android changed, add:
|
||
|
||
```bash
|
||
cd android
|
||
JAVA_HOME=/usr/lib/jvm/java-17-openjdk ./gradlew testDebugUnitTest assembleDebug
|
||
```
|
||
|
||
Documentation must describe the resulting state, not retain contradictory old
|
||
`NEXT` checkpoints.
|
||
|
||
## 11. Measured-max regression
|
||
|
||
The normal Meson suite contains:
|
||
|
||
```text
|
||
measured_max
|
||
```
|
||
|
||
Coverage proves:
|
||
|
||
- stronger ordinary training is ignored by measured-max classification;
|
||
- only `max_test` sessions participate;
|
||
- zero-repetition failed attempts are not promoted;
|
||
- newest successful explicit test is the current measurement;
|
||
- historical external-load record can remain older than current;
|
||
- lower assistance is better;
|
||
- no-load max tests compare actual reps/duration;
|
||
- external working loads round to the configured increment;
|
||
- working-load percentages reject assistance.
|
||
- `%MAX` target calculators use the exact unrounded formula, enforce 1..100,
|
||
exact equipment identity and external resistance, and reject assistance;
|
||
- Android manual target-plan tests prove that calculation is read-only, actual
|
||
set weights remain unchanged, existing target dose/rest survive direct-kg
|
||
edits, and none removes the plan target;
|
||
- the real TUI `/` path resolves through the registered action, focuses search,
|
||
filters live, then follows clear-before-close Escape semantics;
|
||
- action stable identifiers are unique, preventing footer/F7 duplication.
|
||
- Statistics-dashboard rendering covers global summary/event facts, primary-zone
|
||
and unclassified active-catalogue projection without merged-alias double
|
||
counts, zero/one-point without-graph and multi-point Unicode detail-chart
|
||
states; readable selected-period frequency with non-duplicating MAX markers;
|
||
daily/multi-day period projection and independent catalogue bars with
|
||
right-side counts; exact-dose per-set parity, equal-instant
|
||
working/MAX non-events, malformed-timestamp availability, distinct wide versus
|
||
stacked compact geometry, and Unicode blocks used only as chart geometry;
|
||
and footer F6/F7 single-occurrence output at 120x35, 100x30, 80x24, and
|
||
72x20.
|
||
|
||
Historical validation checkpoint (not the current suite size):
|
||
|
||
```text
|
||
39/39 Meson tests PASS
|
||
```
|
||
|
||
## 12. Body analytics regression
|
||
|
||
The Meson suite adds:
|
||
|
||
```text
|
||
body_analytics
|
||
```
|
||
|
||
Coverage includes:
|
||
|
||
- male circumference-formula branch;
|
||
- female circumference-formula branch;
|
||
- estimated fat and lean mass from real body weight;
|
||
- waist/hip, shoulder/waist, and chest/waist ratios;
|
||
- left/right asymmetry;
|
||
- profile-independent analytics without a configured estimation profile;
|
||
- missing required circumference handling;
|
||
- invalid estimation-profile rejection.
|
||
|
||
Historical validation checkpoint (the current desktop suite is 48/48):
|
||
|
||
```text
|
||
39/39 Meson tests PASS
|
||
```
|
||
|
||
## 13. Android session draft v1
|
||
|
||
Android schema v4 introduced one durable active draft; the current additive
|
||
chain reaches schema v17 without clearing completed history or the draft. The
|
||
explicit v10 -> v11 migration adds optional planning metadata while preserving
|
||
existing rows with `load_mode=none`, zero rest and NULL targets. The
|
||
v11 -> v12 migration adds the durable flattened exercise-alias table. Its exact
|
||
physical-v11 fixture compares every pre-existing table cell before and after
|
||
migration, requires the new alias table to be empty, and checks foreign keys.
|
||
The dedicated physical v16 -> v17 fixture preserves representative completed
|
||
history and the active draft, adds empty AI proposal tables, and checks SQLite
|
||
integrity and foreign keys. AI companion parser coverage accepts title/notes
|
||
at their 120/2,000 Unicode-code-point limits (including non-BMP characters)
|
||
and rejects 121/2,001 code points atomically.
|
||
Host coverage includes exercise shapes and raw partial text, fresh repository
|
||
restore, remove/discard, atomic
|
||
finalization and repeated-finalize rejection, rollback, catalog reconciliation,
|
||
missing-selection recovery, explicit DB-open failure, historical migration,
|
||
equipment selection and occurrence identity.
|
||
|
||
Schema-v9 host coverage additionally proves French raw max-text persistence,
|
||
explicit max creation/edit/finalization without sets, distinct movement values
|
||
on the same equipment, latest-per-exercise history, V2 replay, stable-ID resume
|
||
and bounded conversion that leaves multiple legacy attempts untouched.
|
||
|
||
Schema-v10 host coverage adds the canonical body-zone asset, stable-ID mapping,
|
||
relation constraints, filters, transactional editing and companion conflict
|
||
policy without changing explicit MAX or per-set tables.
|
||
|
||
The current set-row-editor coverage additionally proves that row edits,
|
||
deletion and append preserve neighbouring rows; French-comma loads, blank
|
||
loads and explicit zero loads remain distinct; reopening preserves aligned raw
|
||
row fields; and completed history keeps the ordered per-set values. The
|
||
instrumentation source exercises row edit, deletion, append and Activity
|
||
recreation, but this document does not claim that instrumentation was executed
|
||
for the current documentation checkpoint.
|
||
|
||
For the settled implementation, the validation inventory is:
|
||
|
||
```text
|
||
Android JVM: ./gradlew testDebugUnitTest
|
||
Android compilation: ./gradlew assembleDebug
|
||
Android instrumentation: adb shell am instrument ... (execution is explicit;
|
||
no device execution is asserted here)
|
||
Desktop: meson compile -C build
|
||
meson test -C build --print-errorlogs
|
||
Frozen JSON/import: python tools/validate_json.py
|
||
python tools/validate_import_contract.py
|
||
V2 regression: mobile_import_variable_sets and the desktop/Android
|
||
V2 round-trip/idempotent-replay coverage
|
||
Sanitizers: clang ASan/UBSan Meson build and test invocation
|
||
```
|
||
|
||
Executed Body Zones V1 evidence is recorded after each closeout run: Android
|
||
`testDebugUnitTest` 44/44 with the retained real v9 fixture enabled and
|
||
`assembleDebug`, a historical 39/39 Meson checkpoint, valid and invalid JSON checks,
|
||
import-contract checks, and the ASan/UBSan Meson suite. Device installation and
|
||
installed Android-store migration remain explicit hardware steps and are never
|
||
inferred from host tests.
|
||
|
||
The 2026-09-09 desktop closeout additionally backed up the real v10 database,
|
||
opened it through the production Notcurses binary, and verified v11 integrity,
|
||
foreign keys, all historical row counts and bidirectional row equality against
|
||
the backup. A real Kitty terminal validation exercised the zone list, parent
|
||
filter, classified/unclassified details and edit preloading/cancellation. The
|
||
Samsung SM-G990B then passed certificate matching, `adb install -r`, the real
|
||
v9 -> v10 migration, SQLite integrity/FK checks, row equality for every
|
||
pre-existing application table, the Android zone UI matrix and two live MTP
|
||
round trips. The second run reported zero additions/reconciliations and left
|
||
the Android application tables and semantic companion exercise states equal to
|
||
the first run. A regression also covers a current
|
||
`trainlog-sync-request-v1 (N).json` beside an older canonical request.
|
||
|
||
The optional `RealAndroidV9BodyZonesMigrationTest` is enabled by setting
|
||
`TRAINLOG_ANDROID_V9_FIXTURE` to a coherent copied v9 database. It makes two
|
||
test-owned copies before opening the production repository, then compares all
|
||
16 pre-existing application tables in both directions; it never opens or
|
||
modifies the supplied fixture through the migration helper.
|
||
|
||
```bash
|
||
cd android
|
||
JAVA_HOME=/usr/lib/jvm/java-17-openjdk ./gradlew test
|
||
JAVA_HOME=/usr/lib/jvm/java-17-openjdk ./gradlew assembleDebug assembleDebugAndroidTest
|
||
adb install -r app/build/outputs/apk/debug/app-debug.apk
|
||
adb install -r app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk
|
||
adb shell am instrument -w -e package com.labfytools.trainlog \
|
||
com.labfytools.trainlog.test/androidx.test.runner.AndroidJUnitRunner
|
||
```
|
||
|
||
The following device instrumentation evidence is a prior baseline, not a
|
||
blanket device-validation claim for schema-v8 definitions or three-mode sync.
|
||
That suite has **5 tests**: two real-SQLite repository
|
||
checks and three production-screen Compose UI checks. Coverage includes exact
|
||
raw form restoration through Activity recreation, cancellation and confirmation
|
||
of discard, final save with no stale Resume after recreation, and refusal of a
|
||
second completion. All tests use isolated databases; the debug-only,
|
||
non-exported Activity renders production Home/Session screens without exporting
|
||
synthetic data. The phone must be unlocked and interactive. Do not interpret a
|
||
locked-screen `No compose hierarchies found` failure as a passing UI check.
|
||
|
||
The Samsung SM_G990B additionally passed the normal `MainActivity` matrix:
|
||
|
||
- two real catalog exercises with distinct continuous values survived Home,
|
||
another app, background `am kill` with verified PID exit, and force-stop;
|
||
- rotation recreated the Activity and Home Resume restored both exercises;
|
||
- removal of one exercise survived process death, with the other intact;
|
||
- raw duration/speed text survived process death and force-stop exactly;
|
||
- Back and the normal new-session action preserved the existing draft;
|
||
- discard cancellation preserved the DB exactly; confirmation and relaunch
|
||
left all draft tables empty and no Resume action;
|
||
- completed History and mobile export excluded the populated draft;
|
||
- every original domain row survived migration and the entire device matrix.
|
||
|
||
Final-save UI and repeated-finalization tests ran on-device with isolated data;
|
||
no fictitious completed session was added to the user's history. Real migration
|
||
and final state passed SQLite integrity/foreign-key checks. The pre-upgrade DB
|
||
and preferences backup is outside the repository. No uninstall, package-data
|
||
clear, desktop schema change or frozen artifact change is part of this repair.
|
||
|
||
## 14. Current real-data reconciliation validation
|
||
|
||
Before mutation, coherent desktop and Android v8 copies were created and passed
|
||
`PRAGMA integrity_check` plus `PRAGMA foreign_key_check`. The newest real
|
||
Android V2 artifacts were selected, including scoped-storage `(N)` siblings.
|
||
The production tools then ran in protocol order on the desktop copy:
|
||
|
||
```text
|
||
equipment definitions -> exercise/session/body V2 -> equipment associations
|
||
-> PC definitions/catalog/mobile/associations export -> second import
|
||
```
|
||
|
||
The result retained the desktop Marche identity with catalog
|
||
`data_fields = 3`, both older speed-only Marche occurrences, the Android
|
||
speed-and-distance occurrence, all original `entry_id`/positions, custom
|
||
equipment, set weights and body observations. The retired Android Marche ID had
|
||
no remaining desktop catalog/reference owner. The second replay changed no row
|
||
counts and both integrity checks remained clean.
|
||
|
||
This exercises real artifacts and production import/export code on copies. It
|
||
does not substitute for a final direct-MTP run when USB ownership or sandbox
|
||
permissions prevent libmtp access.
|
||
|
||
`tests/test_equipment_associations_exchange.py` additionally covers the
|
||
bounded v13 stale-companion bridge for the five frozen machine-exercise splits.
|
||
Plate Loaded Leg Press and Treadmill pass only with matching current mobile V3
|
||
proof; generic Marche, the durable Marche+treadmill draft, Seated Leg, and Rear
|
||
Delt NULL provenance remain unchanged; an unrelated exercise mismatch still
|
||
fails; and replay is idempotent.
|
||
|
||
For this checkpoint, the USB probe discovered the connected Samsung interface
|
||
but libmtp failed at `libusb_open()`. The canonical desktop DB was also
|
||
read-only to the sandbox; the first attempted definitions import failed before
|
||
mutation, and a repeated dump hash plus integrity/FK checks proved it unchanged.
|
||
The Android package remained force-stopped and ADB was used only for read-only
|
||
inspection/pull. Applying the validated state to both real stores and running
|
||
the two transport directions remains an out-of-sandbox hardware validation.
|
||
|
||
Detailed retained evidence: [Android draft execution record](reviews/android_session_draft_v1_resume.md).
|
||
|
||
## 15. Training knowledge V1 validation checkpoint
|
||
|
||
The current implementation passes the training-knowledge catalog
|
||
validator, eight Python generation/catalog regressions, 42 desktop Meson tests,
|
||
and standalone C17 public-header checks for `training_knowledge.h`,
|
||
`training_context.h` and `database.h`. A targeted AddressSanitizer/Undefined-
|
||
Behavior-Sanitizer build passed the two new desktop API tests. Android
|
||
`testDebugUnitTest assembleDebug` passed with 56 tests, zero failures/errors,
|
||
and one skipped real-v9 fixture test because `TRAINLOG_ANDROID_V9_FIXTURE` was
|
||
not available. The TUI knowledge screen's UTF-8 cell-aware scrolling was tested
|
||
at the 72x20 minimum terminal.
|
||
|
||
The catalog validator, JSON validator and import-contract validator also pass.
|
||
`git diff --check` passes. Scientific-review metadata and
|
||
catalog hashes were verified separately.
|
||
|
||
Temporal regressions now cover the original `+14:30`, `+15:00` and lowercase-`t`
|
||
failure, lowercase `z`, omitted seconds, high/negative offsets, exact fractions,
|
||
stable identity ties, cursor reuse, traversal through exhaustion and malformed
|
||
stored values. C tests also cover selected/unselected values beyond its fixed
|
||
output capacity. Four Python tests cover the explicit grammar and exact
|
||
chronology, including actual document admission with missing, strict and
|
||
permissive optional JSON Schema format checkers. An independent twelve-form
|
||
production C probe passes normally and under ASan/UBSan.
|
||
|
||
Fresh final validation passed: strict build; 42 Meson tests; eight Python
|
||
knowledge tests; four Python temporal tests; knowledge, JSON, and import
|
||
validators; three strict C17 headers; affected C knowledge/context tests under
|
||
ASan/UBSan plus Python timestamp validation; normal and sanitized independent
|
||
12-form temporal probes; Android 56 tests with zero failures/errors and one
|
||
known missing real-v9 fixture skip; Java 17 debug assembly; skill validation;
|
||
and `git diff --check`. Generated C is byte-identical with SHA-256
|
||
`e8c099f67eb111d61621b5d76592c049823af5508d43e73ec646f22e4c377fca`; all six
|
||
Android assets are byte-identical.
|
||
|
||
`TRAINING_KNOWLEDGE_V1=PASS`. The independent temporal review passed with no
|
||
findings; its coverage included parser grammar and limits, exact chronology,
|
||
ties, cursor semantics, source capacity, snapshots, and Android/Python parity.
|
||
The initial full audit's stale temporal documentation, Android loader, Meson
|
||
dependency, and C role-only query findings were repaired and independently
|
||
verified. See the [temporal contract](reviews/training_knowledge_v1_temporal_contract.md)
|
||
for the established contract. No real Android install,
|
||
manual TUI visual exercise, or manual MTP hardware validation was performed
|
||
for Training Knowledge V1.
|
||
|
||
Shell/naming closeout coverage includes real-PTY shell behavior, canonical
|
||
name export/import replay on temporary databases, and preservation of sets,
|
||
MAX, equipment, BODY ZONES, aliases, and distinct unmapped exercises.
|
||
EQUIPMENT_KNOWLEDGE_V2 validation covers relation envelopes, canonical UUID and
|
||
equipment references, enum/source integrity, uniqueness and stable ordering,
|
||
the unresolved Seated Leg invariant, generic/custom non-inference, and generated
|
||
C/Android query parity for leg press, combo equipment, and chest-zone derivation.
|
||
`training_feedback_sync` covers first import, identical replay, post-sync
|
||
follow-up addition, PC export convergence, and immutable-ID conflict. Android
|
||
unit tests cover partial/final recognition, correction, resume/append, stop,
|
||
save/cancel, failure preservation, permission/unavailable fallback, lifecycle
|
||
destroy, and H+ flooring/unknown anchors. Draft-feedback coverage saves before
|
||
completion, reopens the repository, preserves multiple stable IDs and ordering
|
||
across ordinary draft rewrites, verifies active/completed Compose readback,
|
||
excludes the draft rows from sync, transfers them atomically on finalization,
|
||
rolls back on an injected insert failure, and checks discard cascading. The
|
||
real-v13-copy production migration test also requires the existing active draft
|
||
and all its occurrences to remain identical while the new draft-feedback table
|
||
starts empty and a second production open is logically idempotent.
|
||
`feedback_correction` reconstructs completed occurrence rowids while retaining,
|
||
reordering and changing the exercise context of stable entries; it verifies
|
||
exact feedback preservation, selective cascade on removal, no inheritance by a
|
||
new entry, unchanged follow-ups, and full rollback on injected reattachment
|
||
failure. Android additionally covers its bounded resumed-MAX replacement path.
|
||
`tests/test_profile_state_sync.py` covers the captured Planche droite sol
|
||
legacy `SETS+REPS`/`SETS+DURATION` repair, idempotent replay, direct-descendant
|
||
and multi-generation stale-ancestor convergence, sibling conflict, the explicit
|
||
32-record resource bound, exact JSON scalar types, immutable machine conflict,
|
||
and preservation of the old occurrence snapshot. Android
|
||
`ExerciseProfileStateSyncTest` covers the same transitive/type contract in the
|
||
reverse PC→Android path and future-occurrence snapshot.
|
||
`HistoricalSessionProfileImportTest` exercises the production V2/V3 repository
|
||
import with equal and evolved tracking/recording/data-field profiles, strict
|
||
snapshot-local payload rejection, unknown identity rejection, unchanged
|
||
historical values, unchanged current profile history, and idempotent replay.
|
||
`SyncBundlePublicationTest` exercises the production request coordinator and a
|
||
synthetic direct exchange directory. The exact BODY ZONES file is rewritten
|
||
beside 31 preserved historical copies, no `(32)` copy occurs, all seven
|
||
Android-origin companions precede the request signal, and replay reuses
|
||
canonical names without stable-feedback churn.
|
||
It also requires one direct-child enumeration for the entire export-plus-request
|
||
transaction, one additional enumeration for each later synchronization, index
|
||
update after a create without rescan, one write per canonical artifact, and
|
||
non-main dispatcher ownership for snapshot and stream operations. Focused
|
||
publisher cases additionally cover an empty directory, exact rewrite,
|
||
ten successive publications without numbered copies, historical numbered
|
||
copies alone and beside a canonical object, exact final content, and an
|
||
explicit canonical-name conflict without writing a wrongly named target.
|
||
`DirectExchangeStorageTest` verifies the manifest permission and settings
|
||
action, refusal without an authorized backend, directory creation, real
|
||
temporary-file replacement and truncation, ten publications without suffixes,
|
||
and preservation of legacy Download and recovery fixtures. It also reconstructs
|
||
a truly fresh Android database from catalog, alias, profile-state and body-zone
|
||
companions, including an old zone `exercise_id` resolved through the durable
|
||
alias. A deliberately malformed downstream session companion proves zones are
|
||
already durable before the error; the repaired pass imports the one-sync AI
|
||
draft, and replay preserves three canonical identities and exactly seven zone
|
||
rows without duplicates.
|
||
`AiSessionDraftUiWiringTest` additionally requires the delete action to expose
|
||
the titled confirmation before mutation, verifies Cancel and Back dismissal,
|
||
then proves that only confirmation removes the pending proposal while retaining
|
||
its replay-blocking deleted tombstone. Its existing Start coverage remains.
|
||
Native `sync_body_zone_wiring` verifies the
|
||
profile pre-pass before mobile import, strict post-pass gating, replay, legacy
|
||
peer compatibility, and the explicit missing-companion diagnostic even when a
|
||
stale local temporary profile artifact exists. It also receives a valid alias
|
||
V1 companion through the production MTP orchestration, imports it into an
|
||
isolated database through the production Python argv path, and proves that the
|
||
sync continues. Negative cases cover an unresolved helper, argparse failure,
|
||
Python exception/stderr propagation, sync-history visibility, and rejection of
|
||
a stale result-file `PASS`; the successful flow exercises the other shared
|
||
Python helper callers as well.
|
||
Every Android outbound artifact and the request use the shared direct-storage
|
||
exact-name resolver; app-scoped MediaStore visibility is irrelevant.
|
||
`profile_state_sync` includes the production persisted update-trigger dependency
|
||
on `trainlog_profile_revision`, imports through the same Python connection path
|
||
used by the PC sync worker, and requires first import plus identical replay to
|
||
pass without revision-state or history-count churn. It also checks that the
|
||
central factory installs both profile-revision schema functions.
|
||
|
||
`TRAINLOG_ANDROID_DIRECT_STORAGE_V1` coverage fixes Android storage at
|
||
`/storage/emulated/0/Documents/Trainlog`, preserves canonical rewrite/no-suffix
|
||
behavior, ignores legacy Download trees, and gives the MTP fixture both
|
||
Documents and Download roots while failing on any legacy access.
|
||
Android unit tests and any instrumented tests run on an emulator; the primary
|
||
personal phone is reserved for manual, non-destructive install-and-sync smoke
|
||
validation and must not run `connectedDebugAndroidTest`.
|
||
|
||
## Generation, transaction, and acknowledgement V1
|
||
|
||
The normal Meson inventory includes `sync_generation_exchange` and
|
||
`schema_v21_migration`. The service suite exercises deterministic desktop
|
||
snapshot capture against a controlled writer, immutable publication/replay,
|
||
partial and substituted input, strict paths/capabilities/bounds, wrong ACK
|
||
context, durable ACK replay after reopen, late-domain rollback, rejected ACKs,
|
||
and unchanged causal-operation IDs/digests across first emission and
|
||
retransmission for all seven causal target kinds. The v20 fixture migration
|
||
preserves populated business state and creates no historical generation/ACK.
|
||
|
||
Android `SyncGenerationServiceTest` uses production repository codecs and the
|
||
filesystem object adapter. It proves a transaction-held capture against a
|
||
deterministically blocked writer, v19→v20 preservation, manifest-last immutable
|
||
publication, whole-generation rollback on a late semantic failure, committed
|
||
ACK replay after close/reopen, and causal-operation byte stability. Its real
|
||
cross-implementation test runs both chains: an Android-created session is
|
||
captured and transactionally consumed by the desktop Python service, and a
|
||
separate desktop-created session written through the public C17 database API is
|
||
captured by the desktop service and consumed by Android. Both ACKs return to
|
||
the original producer. The bridge moves bytes and invokes real entry points; it
|
||
does not author expected business JSON or repair artifacts.
|
||
|
||
After these additions the direct normal suites report 78/78 Meson tests and
|
||
216 Android tests: 212 passed, four historical-fixture skips, zero failures.
|
||
|
||
The grouped Web end-to-end tranche raises the direct normal and sanitizer
|
||
inventories to 79/79 and adds `sync_orchestrator`, protected sync API
|
||
coverage, frontend sync assertions and a real headless Firefox scenario in
|
||
`tests/test_web_sync_browser.py`. Its detailed matrix is
|
||
[`sync_web_end_to_end_v1_evidence.md`](design/sync_web_end_to_end_v1_evidence.md).
|
||
The browser uses the production C server and worker with an isolated directory
|
||
transport fixture; physical MTP and Drive are not claimed.
|
||
The publication adapter is an isolated directory/object-store double. No
|
||
physical MTP, phone, Drive, active service, or user database participates.
|
||
## Causal deletion V1
|
||
|
||
The normal Meson inventory includes `causal_delete_exchange`, which invokes
|
||
the production desktop service and proves all target kinds, relationship
|
||
safety, exact replay, conflicts, finalization protection, and rollback. Android
|
||
`causalSessionDeletionRoundTripsWithBothRealProducersAndBlocksReplay` uses the
|
||
real Android repository and desktop service in both producer directions with
|
||
fresh destinations, reopen/replay, and legacy non-resurrection.
|
||
|
||
`causalFiveDomainDeletionsRoundTripWithAndroidAndDesktopProducers` closes the
|
||
remaining producer/consumer matrix. The Android repository creates exercises,
|
||
populated observations, custom equipment, multi-revision feedback and BODY
|
||
ZONE relations; production V4 and companion tools establish the desktop
|
||
replica. Android-local deletion is consumed and replayed by the desktop
|
||
service. A second production-provisioned desktop replica creates each deletion
|
||
locally, exports it to Android, and receives Android's exact replay.
|
||
|
||
| Kind | Producer -> consumer | Stored/read-model assertion | Result |
|
||
|---|---|---|---|
|
||
| `exercise` | Android -> desktop | Historical row/reference retained; new-work predicate excludes it; unrelated exercise and alias protection remain | PASS |
|
||
| `exercise` | desktop -> Android | Repository list excludes it after reopen; history row remains; stale V4/catalog state cannot restore it | PASS |
|
||
| `body_observation` | Android -> desktop | Populated target row is absent; session and unrelated populated observation remain | PASS |
|
||
| `body_observation` | desktop -> Android | Repository list excludes the target after reopen; unrelated observation and session remain | PASS |
|
||
| `custom_equipment` | Android -> desktop | Custom definition/history remains but new-work predicate excludes it; built-in occurrence equipment remains | PASS |
|
||
| `custom_equipment` | desktop -> Android | Historical equipment row/reference remains; repository list excludes it; built-in equipment remains | PASS |
|
||
| `feedback` | Android -> desktop | Root and two immutable revisions remain; current predicate excludes it; unrelated feedback remains | PASS |
|
||
| `feedback` | desktop -> Android | Root/revisions remain after reopen; repository current view excludes it; unrelated feedback remains | PASS |
|
||
| `body_zone_relation` | Android -> desktop | Target secondary relation is absent; primary relation and exercise remain | PASS |
|
||
| `body_zone_relation` | desktop -> Android | Target secondary relation is absent after reopen; primary relation and exercise remain | PASS |
|
||
|
||
Every row uses a legitimate replica established through production exchange
|
||
entry points. Repeated local deletion returns the original immutable operation;
|
||
the opposite exporter preserves its operation ID, target, predecessor and
|
||
payload; replay after a new database connection is unchanged. Protected V4,
|
||
equipment, feedback and BODY ZONES legacy imports refuse stale restoration and
|
||
leave all accepted rows and causal state unchanged. Generic codec reuse is not
|
||
counted as a domain result.
|
||
|
||
Strict compilation uses Clang 22.1.8. GCC 16.2.1 retains the compared
|
||
pre-existing `-Werror=misleading-indentation` failure in unchanged Web baseline
|
||
sources; it is not reported as green and warning policy is unchanged.
|
||
|
||
The causal closeout full harness result is 76/76 normal Meson, 76/76
|
||
ASan/UBSan, and 211 Android tests: 207 passed and four optional historical
|
||
database fixtures skipped. `assembleDebug`, JSON validation, import-contract
|
||
validation, both frontend tests, and the isolated characterization all pass.
|
||
|
||
Focused closeout assertions are `test_child_mutations_invalidate_session_observation_and_feedback_deletes`,
|
||
`test_imported_builtin_and_alias_retirement_are_refused_without_writes`,
|
||
`test_draft_identity_is_semantic_revision_not_json_serialization`, and
|
||
`test_operation_count_and_input_byte_bounds_precede_effects` in the desktop
|
||
production-service suite. Android adds
|
||
`causalDraftDeletionUsesTheSameRevisionForActivePendingAndDesktopReplicas` and
|
||
`importedCausalOperationsCannotRetireBuiltInIdentities`; the existing session
|
||
round trip now also changes and reverts a confirmed set before proving the
|
||
remote deletion conflicts without losing business or causal state.
|
||
|
||
## Generation MTP and Android backup
|
||
|
||
`generation_mtp` drives the production transport algorithm through typed fake
|
||
libudev/libmtp callbacks. It covers exact peer selection, bounded download,
|
||
manifest-last publication, ambiguity and truncated-object rejection. The
|
||
Firefox end-to-end scenario also runs in `mtp` mode: only the callback table is
|
||
replaced by a filesystem object store while the C transport, Python worker,
|
||
Android generation service, desktop consumers and durable ACK path remain real.
|
||
|
||
`AndroidBackupServiceTest` creates and restores a populated current-schema
|
||
backup including an active draft, raw partial input, stable peer identity and
|
||
preferences, and rejects corrupt and traversal entries. The bridge build runs
|
||
the analogous test on the exact schema-v17 source and feeds its archive to
|
||
`BridgeBackupMigrationTest`, which verifies normal migration to schema v21.
|
||
These are software proofs. They do not claim physical USB/MTP behavior,
|
||
installation over the signed release, a real user backup, or a real-device
|
||
restore.
|
||
|
||
The production Android coordinator and desktop peer worker also complete 24
|
||
successive bidirectional conversations against their real SQLite services with
|
||
no cleanup between runs. The test crosses multiple archive cycles and checks
|
||
that the active draft survives. After the eighth exchange it also reproduces
|
||
the deployed legacy evidence gap by retaining acknowledged generations while
|
||
removing only their producer-side ACK rows; the next production conversation
|
||
must restore the desktop consumer's exact correlated ACKs, archive eligible
|
||
generations, and complete without deleting or duplicating any generation.
|
||
Focused archive tests cover interrupted-copy resume, invalid archives,
|
||
simulated insufficient space, missing and late ACKs, and idempotent ACK replay
|
||
after archival.
|
||
|
||
The controlled Samsung SM-G990B rollout migrated the authentic Android backup
|
||
from schema v20 to v21 and an authentic desktop copy from v21 to v22 before
|
||
installation. The installed release retained its existing certificate and
|
||
advanced from versionCode 6 through 7 to 8 without uninstalling or clearing
|
||
data. Real MTP validation preserved the failed-run evidence that exposed an
|
||
Android external-storage directory-fsync incompatibility and an early desktop
|
||
generation reference. After their bounded repairs, run
|
||
`sy_06200dd7-0d51-4f33-b80d-9976822ba87d` completed, the two Trainlog services
|
||
were restarted, and run `sy_783014b0-f3f0-4f1e-8f4f-723acfc02dbc` completed.
|
||
Both exchanged distinct generations and durable ACKs with zero session
|
||
reconciliation. The final Android backup passed integrity and foreign-key
|
||
checks at schema v21 with 13 generations, five verified archives, 13 ACKs,
|
||
five consumed generations, and six active generations. The expected AI
|
||
proposal remained `pending` with its original identity and six entries.
|
||
|
||
## Readability validation
|
||
|
||
The normalized C scope is checked non-mutatively with the explicit
|
||
`clang-format --dry-run --Werror` command in `docs/coding_style.md`. Clang C17,
|
||
the normal Meson inventory and the ASan/UBSan inventory preserve the existing
|
||
behavioral assertions. Python syntax/orchestrator tests, Android generation and
|
||
backup tests, the I/O-boundary MTP browser proof, JSON/import validators and
|
||
the isolated harness cover the other normalized sources.
|
||
|
||
GCC 16.2.1 and Clang 22.1.8 both pass separate strict C17 builds and the full
|
||
**81/81** Meson inventory with `werror=true` and warning level 3. The former GCC
|
||
`format-truncation` findings are closed by exact checked path construction in
|
||
the generation-MTP tests/double and by a production accepted-response
|
||
serializer that reports its byte count. Boundary tests cover exact fit,
|
||
one-byte insufficiency and multibyte path components; failure leaves no partial
|
||
path or response available to a caller. Clang ASan/UBSan passes the same
|
||
**81/81** inventory with leak detection.
|
||
|
||
The readability closeout preserves the **81/81** normal Meson and **81/81**
|
||
ASan/UBSan inventories. The isolated Android run reports **220 tests: 215
|
||
passed, five skipped, zero failures/errors**, and `assembleDebug` succeeds on
|
||
JDK 17. The real Firefox/object-I/O-boundary MTP conversation also passes after
|
||
formatting. A source comparison against the pre-readability commit confirms
|
||
that the scoped test names and assertion counts are unchanged.
|
||
|
||
The deployment-tools regression also constructs the relocatable candidate and
|
||
requires its inventory to include the packaged `trainlog-sync-once` binary,
|
||
the `trainlog-syncd` launcher and daemon source, and the generation worker.
|
||
This prevents a user unit from silently executing a helper from a different
|
||
checkout than the installed desktop candidate.
|
||
|
||
`syncd_routing` proves that both Android-labelled and daemon-labelled
|
||
admissions invoke the packaged `sync_orchestrator.py` full-generation owner
|
||
when trusted configuration is present, and never invoke the legacy
|
||
`trainlog-sync-once` command in that mode. The generation-MTP boundary test
|
||
proves that polling copies the Android request signal without acknowledging or
|
||
deleting it. Session-exchange characterization keeps standalone V3 rejection
|
||
strict while its complete-envelope case filters a tombstone-dominated live
|
||
fact; the causal Android/Desktop round-trip and generation replay/ACK tests
|
||
cover restart, idempotence and non-resurrection. `AndroidBackupServiceTest`
|
||
injects a product version and verifies that exact value in the ZIP manifest,
|
||
preventing a return to a hard-coded release string.
|
||
|
||
The isolated JDK 17 harness reports **220 Android tests: 215 passed, five
|
||
skipped, zero failures/errors**. The skips are optional, externally supplied
|
||
copy gates: `BridgeBackupMigrationTest.verifiedSchemaSeventeenBridgeBackupMigratesThroughCurrentOwner`,
|
||
`RealAndroidV9BodyZonesMigrationTest.realVersionNineCopyMigratesWithoutChangingExistingTables`,
|
||
`RealAndroidV14FeedbackRevisionMigrationTest.freshRealCopyMigratesLosslesslyToVersionFifteen`,
|
||
and the version-thirteen and version-twelve methods in
|
||
`RealAndroidV12MachineExerciseMigrationTest`. They require respectively the
|
||
bridge backup input, a real v9 fixture, a real v14 fixture/output directory,
|
||
and real v13/v12 fixtures with explicit output directories. Synthetic bridge
|
||
and current-schema migration proofs run separately; no user database is read.
|
||
|
||
The bridge migration test accepts both legitimate backup states: an active
|
||
draft may be present or absent. It snapshots every original table, column,
|
||
SQLite value type and value before restore, migrates through the production
|
||
repository owner, and compares that complete historical shape afterward. The
|
||
draft assertion follows the archived state instead of requiring the synthetic
|
||
fixture's sample draft.
|
||
|
||
## Private full-generation rollout validation
|
||
|
||
Routing regressions cover a strict V3 refusal in the presence of causal
|
||
deletions, publication of the dedicated full-generation request before legacy
|
||
compatibility, same-identity dual-channel deduplication, full-generation
|
||
priority over stale legacy requests, and bounded durable seen-request state.
|
||
|
||
The MTP visibility regression suite uses injected clocks and transport
|
||
callbacks, with no multi-second sleeps. It covers delayed Android publication,
|
||
multiple throttled polls, stale run references, a never-visible generation,
|
||
an independently blocked adapter, and exact protocol-versus-transport timeout
|
||
classification. Android tests prove fresh request UUIDs, foreground/background
|
||
lock ownership, objects-visible-before-reference ordering, bounded phase
|
||
tracing, and a completed full-generation conversation. Compose tests exercise
|
||
the measured 312 dp phone content width as two columns, a true 280 dp narrow
|
||
layout as one column, 1.5 font-scale fallback, long French labels and `kg`/`cm`
|
||
suffixes.
|
||
|
||
Android conversation-ownership regressions reproduce the production race: an
|
||
automatic owner waiting on an old resumable run yields to explicit foreground
|
||
intent, the click publishes a fresh request UUID, the explicit coordinator
|
||
ignores the pre-click desktop run, and exactly one generation is captured for
|
||
the new run. Additional fixtures cover typed cancellation and durable handoff,
|
||
terminal-run suppression across five scheduling cycles, reactivation by new
|
||
correlated desktop evidence, and exclusive ownership. Desktop routing tests
|
||
retain the complementary invariant that an already-consumed Android request
|
||
UUID cannot create a second daemon run.
|
||
|
||
The resumed run `sy_9639f06e-58ba-4bc0-ab1b-1488aefe011c` subsequently proved
|
||
the MTP publication path: Android captured
|
||
`gen_532da95e-713f-4163-acc1-d838673c33bf`, published the immutable objects and
|
||
reference, and exposed bytes identical to the filesystem through MTP. It
|
||
stopped after `generation_reference_published` because no ACK could be created
|
||
for the daemon-deduplicated old request UUID. This evidence supersedes the
|
||
earlier pre-resume forensic snapshot without rewriting or deleting the
|
||
incomplete generation.
|
||
|
||
The authorized 2026-09-17 rollout added regression coverage for production
|
||
failures found only after installation: feedback import under `sqlite3.Row`,
|
||
the complete exporter inventory of the relocatable package, stale Android ACK
|
||
and generation-reference correlation, and the completed-generation UI state.
|
||
The real direct-libmtp smoke then completed three bidirectional conversations
|
||
on the paired phone. Restart and replay preserved identical logical hashes for
|
||
23 desktop business tables and 540 rows; SQLite integrity and foreign-key
|
||
checks passed, both outgoing desktop generations were acknowledged, and the
|
||
Android UI displayed completion. The private journal retains exact IDs and
|
||
hashes. Drive and `connectedDebugAndroidTest` were not run; the personal phone
|
||
was used only for non-destructive install, backup and manual sync validation.
|
||
|
||
## Cardio persistence/synchronization V1 validation
|
||
|
||
`TRAINLOG_CARDIO_PERSISTENCE_SYNC_V1=PASS` is covered without requiring a
|
||
physical heart-rate sensor. Android migration coverage advances schema v26 to
|
||
v27 and verifies durable raw BPM/contact/energy/RR capture; desktop migration
|
||
coverage advances v29 to v30 and verifies the additive cardio tables/indexes.
|
||
|
||
`heart_rate_exchange` validates the strict `trainlog-heart-rate` V1 envelope,
|
||
cardio/session/sleep ownership, timestamp bounds, raw RR preservation, exact
|
||
idempotent replay, and rejection of identity reuse with different measured
|
||
content. The Android full-generation regression captures a stopped sleep-owned
|
||
measurement, publishes it, consumes it through the production desktop
|
||
generation tool, accepts the correlated ACK, verifies one desktop
|
||
capture/sample/RR row, and proves the acknowledged Android capture is no longer
|
||
emitted.
|
||
|
||
`test_heart_rate_tail_recovery` covers the delayed-session correction boundary:
|
||
a sample exactly at the factual end remains, the first representable sample
|
||
after it and its RR child are removed, statistics use only retained facts, an
|
||
already bounded capture is an idempotent no-op, wrong session/capture and
|
||
invalid temporal bounds are refused, and an injected post-effect failure rolls
|
||
back every row. It then replays the old Heart Rate V1 snapshot and the same
|
||
causal operation twice to prove non-resurrection and second-sync idempotence.
|
||
It also proves that `heart_rate_tail` appears only in the separately versioned
|
||
heart-rate-correction artifact and remains absent from frozen
|
||
causal-deletions V1.
|
||
`TrainlogRepositoryDraftTest.delayedFactualFinalizationTruncatesHeartRateSamplesAndRrInclusively`
|
||
exercises the production Android delayed-finalization transaction.
|
||
|
||
The 2026-09-24 paired-device closeout applies operation
|
||
`hrx_a2222757-9a85-492f-823b-ad590208ce6b` to capture
|
||
`hrc_fe0627e0-dff6-4629-82d9-03da78c49634`. Android and desktop both report
|
||
3,429 BPM samples, 3,429 RR values, no sample after 11:16, last sample
|
||
11:15:59.759323, and 89 / 119.30300379119277 / 138 BPM. Full MTP runs
|
||
`sy_d1a29dd9-7b51-41db-83e1-5712758b3700` and
|
||
`sy_4b8b3c28-17b1-4300-bfbf-9332385f69ab` both completed through
|
||
`peer_consumed`. A production import of the retained pre-correction 4,919-point
|
||
artifact left the same counts and statistics. The live Web history detail in
|
||
Firefox rendered the 10:18--11:16 curve, seven exercises, 15 sets, 3,429 RR,
|
||
and the same min/average/max values.
|
||
|
||
Generation regressions additionally cover an archived acknowledged fork, a
|
||
legacy acknowledged row without durable ACK evidence, and consumer acceptance
|
||
of a producer-selected current fork tip while rejecting stale/null ancestry.
|
||
Android generation tests exercise the matching rules. Sleep quick-capture and
|
||
generation tests cover the schema-v33 medication-intake column order with
|
||
quantity 2, preventing note/creation-time values from being bound to the
|
||
`quantity` constraint.
|
||
|
||
The final post-device closeout passes the complete native Meson inventory
|
||
**114/114**, the complete clang ASan/UBSan Meson inventory **114/114**, and
|
||
Android `testDebugUnitTest` with **317 tests: 312 passed, five skipped, zero
|
||
failures/errors**. Android `assembleDebug`, both JSON/import validators,
|
||
changed-file C formatting, and `git diff --check` also pass.
|
||
|
||
The 2026-09-23 closeout passes the complete native Meson inventory:
|
||
**102/102 passed, 0 failed**. The complete Android JVM inventory reports
|
||
**258 tests: 253 passed, five skipped, zero failures/errors**. The five skips
|
||
remain optional historical/external fixture gates and are not Cardio V1
|
||
failures.
|
||
|
||
## Cardio BLE diagnostics V1 validation
|
||
|
||
`TRAINLOG_CARDIO_BLE_DIAGNOSTICS_V1=SOFTWARE_PASS / HARDWARE_VALIDATION_PENDING`
|
||
keeps BLE protocol parsing separate from Android GATT transport. Unit coverage
|
||
exercises 8-bit and 16-bit BPM, nullable sensor-contact semantics, cumulative
|
||
energy, one/multiple raw RR intervals, combined flags, boundary BPM values,
|
||
reserved flags, missing fields, truncated RR/energy values and unexpected
|
||
trailing bytes.
|
||
|
||
The Android diagnostic uses a 10-second Heart Rate Service-filtered scan with
|
||
an explicit expanded BLE fallback, then discovers `0x180D`, locates `0x2A37`,
|
||
checks optional Battery Service and enables the CCC notification descriptor.
|
||
It is screen-scoped and does not implement the later persistent foreground
|
||
acquisition owner.
|
||
|
||
On 2026-09-23 the diagnostic APK was installed non-destructively on the
|
||
authorized SM_G990B as Trainlog 0.1.5 versionCode 38 using the same Android
|
||
debug signing certificate as the existing private installation. Application
|
||
startup completed without SQLite/crash errors and the diagnostic route opened.
|
||
The first real 10-second HRS-filtered scan completed cleanly before the armband
|
||
was positively identified. A subsequent expanded scan identified the real
|
||
CYCPLUS H2 advertising Heart Rate Service. Trainlog connected through GATT,
|
||
discovered `0x180D`, `0x2A37` and Battery Service, enabled notifications and
|
||
received live BPM successfully. The observed payloads carried one raw RR
|
||
interval, no sensor-contact field and no energy-expended field. A 12-second
|
||
measurement window advanced from 43 to 56 notifications (13 notifications),
|
||
confirming an approximately 1 Hz emission cadence. This closes the real-sensor
|
||
hardware characterization gate without recording device-specific MAC identity
|
||
in project documentation.
|
||
|
||
The complete Android JVM inventory after this tranche reports **266 tests:
|
||
261 passed, five skipped, zero failures/errors**, and `assembleDebug` succeeds.
|
||
|
||
## Cardio persistent acquisition V1 validation
|
||
|
||
`TRAINLOG_CARDIO_PERSISTENT_ACQUISITION_V1=PASS` adds a remembered Android
|
||
heart-rate sensor, a dedicated connected-device foreground service, process-wide
|
||
live state and the global app-bar `♥ BPM` indicator. A fresh measurement is
|
||
green; connection/reconnection or stale signal is orange; no configured/active
|
||
sensor is grey. A BPM older than three seconds is removed from presentation,
|
||
and a stale GATT stream is forcibly recycled after eight seconds.
|
||
|
||
Unit coverage proves durable sensor selection/clear and the live-state
|
||
transition from connecting to measured BPM/RR, stale `-- BPM`, and
|
||
disconnected. The complete Android JVM inventory after this tranche reports
|
||
**268 tests: 263 passed, five skipped, zero failures/errors**; `assembleDebug`
|
||
also passes.
|
||
|
||
Real-device validation used the authorized CYCPLUS H2 on the private Trainlog
|
||
0.1.5 versionCode 40 installation. With no selected sensor the global indicator
|
||
rendered `♥ -- BPM` and the disconnected accessibility state. Selecting the
|
||
validated sensor produced live `♥ 75 BPM`; a complete app force-stop/restart
|
||
reloaded the remembered sensor and returned automatically at `♥ 72 BPM`.
|
||
Disabling phone Bluetooth produced `♥ -- BPM` with the reconnecting state;
|
||
reenabling Bluetooth restored the same foreground service automatically and
|
||
returned to a fresh `♥ 79 BPM`. No uninstall or data clear occurred.
|
||
|
||
## Training timeline V1 validation
|
||
|
||
`TRAINLOG_CARDIO_TRAINING_TIMELINE_V1=PASS` covers Android schema v28 and
|
||
desktop schema v31. Repository tests prove explicit exercise start/end
|
||
timestamps, one-active-exercise enforcement, restart-safe active ownership,
|
||
finalization refusal while an exercise remains active, and heart-rate samples
|
||
tagged with the active stable `entry_id` while rest samples remain unowned.
|
||
|
||
The generation regression publishes a completed timeline through the production
|
||
full-generation path, imports it through `trainlog-session-timeline` V1,
|
||
accepts the correlated ACK, and verifies idempotent replay/conflict rejection.
|
||
|
||
The 2026-09-23 closeout passes the complete native Meson inventory:
|
||
**104/104 passed, 0 failed**. The complete Android JVM inventory reports
|
||
**272 tests: 267 passed, five skipped, zero failures/errors**; `assembleDebug`
|
||
also succeeds. The five skips remain optional historical/external fixture gates.
|
||
|
||
## Android one-tap Sleep V1 validation
|
||
|
||
`TRAINLOG_SLEEP_ANDROID_ONE_TAP_V1=SOFTWARE_PASS / DEVICE_VISUAL_PENDING`
|
||
reuses the existing Sleep Diary V1 revision model and schema. The quick-capture
|
||
API creates/advances normal immutable Sleep Diary revisions for Couché,
|
||
medication intake, 30-minute structured Réveil and Levé actions. Undo is accepted only when
|
||
the action's exact applied revision is still current; older receipts conflict,
|
||
and successful undo appends a compensating revision instead of rewriting the
|
||
history.
|
||
|
||
Targeted coverage executes Couché, a configured medication with dose snapshot,
|
||
two independent awakening intervals, live sleep-owned BPM/RR capture, Levé and final
|
||
heart-rate export against the same stable sleep `entry_id`. A separate test
|
||
proves stale undo rejection and exact restoration of the latest action. A
|
||
dedicated regression proves that a second tap inside 30 minutes extends one
|
||
interval and that final get-up clamps it without leaving overlapping windows.
|
||
|
||
The complete Android JVM inventory before the final localization-only cleanup
|
||
reported **274 tests: 269 passed, five skipped, zero failures/errors** and
|
||
`assembleDebug` succeeded. After the localization cleanup the targeted
|
||
SleepQuickCapture test and `assembleDebug` passed again.
|
||
|
||
The 30-minute awakening-policy follow-up passes the current complete inventory:
|
||
**312 tests: 307 passed, five skipped, zero failures/errors**, plus
|
||
`assembleDebug`. Real-data Firefox coverage additionally asserts two orange
|
||
agenda/HR bands and two orange vector-PDF intervals for the repaired
|
||
2026-09-23 night.
|
||
|
||
The subsequent agenda/report-range regression covers exact 18:00-to-18:00
|
||
hour geometry, a visible 24-column row grid, inclusive start/end filtering,
|
||
identical preview/export snapshots, range summary recomputation, and the
|
||
independence of the selected HR/editor night from the PDF range.
|
||
|
||
The private Android installation was advanced non-destructively to Trainlog
|
||
0.1.5 versionCode 41 with the same debug signing identity and no uninstall or
|
||
data clear. The device locked before the final visual Sleep-screen inspection,
|
||
so no lock bypass was attempted and no real Sleep business button was pressed;
|
||
that visual review remains explicitly pending.
|
||
|
||
## Cardio session type V1 validation
|
||
|
||
`TRAINLOG_CARDIO_SESSION_TYPE_V1=PASS` covers the new canonical session kind
|
||
without widening frozen historical codecs. Android schema v29 migration tests
|
||
prove that v28 training/max-test rows backfill from the legacy
|
||
`session_type`, including a structurally adversarial retained-column fixture.
|
||
A product-level Android regression creates, reloads and finalizes a real
|
||
`SessionType.CARDIO` draft, verifies legacy `session_type=training` plus
|
||
canonical `session_kind=cardio`, and proves the cardio session is absent from
|
||
`history-v4`.
|
||
|
||
The end-to-end generation regression proves the complementary path:
|
||
`history-v4` contains zero cardio sessions, `cardio-sessions-v1.json`
|
||
contains the completed cardio session, the production desktop generation
|
||
consumer imports it as legacy training/canonical cardio with its performed set,
|
||
the correlated ACK is accepted, and exact replay remains idempotent.
|
||
|
||
Desktop schema v32 migration coverage validates the additive canonical column,
|
||
historical-layout tolerance and foreign-key integrity.
|
||
`cardio_session_exchange` separately covers strict cardio-only validation,
|
||
exact replay and rejection of a stable identity colliding with non-cardio
|
||
history.
|
||
|
||
The 2026-09-23 closeout passes the complete native Meson inventory:
|
||
**106/106 passed, 0 failed**. The complete Android JVM inventory reports
|
||
**277 tests: 272 passed, five skipped, zero failures/errors**; `assembleDebug`
|
||
also succeeds. The five skips remain optional historical/external fixture gates.
|
||
|
||
## Cardio calibration V1 validation
|
||
|
||
`TRAINLOG_CARDIO_CALIBRATION_V1=PASS` covers the Android v29→v30 and desktop v32→v33 additive migrations, reserved system exercise ownership, real cardio session/timeline/HR capture integration, observed peak tracking, recovery, exact replay and generation synchronization.
|
||
|
||
The calibration system exercise keeps a stable `exercise_id` for occurrence foreign keys but is deliberately absent from the ordinary exercise catalogue, BODY ZONES, profile-state and PC-catalog companions. This preserves existing exercise-domain invariants while the dedicated cardio session/calibration companions own the system occurrence.
|
||
|
||
Recovery cannot complete before three minutes after effort end. The +60/+120/+180 second references accept only a real HR sample arriving from the target instant through ten seconds after it. A missing sensor interval remains missing rather than being populated by a later reconnect.
|
||
|
||
The desktop importer independently rejects a peak not supported by the raw HR capture and rejects a recovery point that is not an exact raw sample. The 2026-09-23 closeout passes the complete native inventory **108/108** and the complete Android JVM inventory **281 tests: 276 passed, five skipped, zero failures/errors**; `assembleDebug` also succeeds.
|
||
|
||
## Cardio BPM guidance V1 validation
|
||
|
||
`TRAINLOG_CARDIO_BPM_GUIDANCE_V1=PASS` covers Android schema v31, desktop
|
||
schema v34, pure guidance logic, persistent phase/run history, live CYCPLUS
|
||
integration, strict synchronization and the Android active-session UI.
|
||
|
||
The guidance engine resolves either absolute BPM targets or calibration-relative
|
||
percentages into an immutable BPM snapshot before execution. The default
|
||
control-stability parameters are a 2 BPM hysteresis band and a 3 second
|
||
confirmation interval; these are UI/control parameters, not physiological zone
|
||
claims. A stale or missing heart-rate signal suspends guidance immediately and
|
||
never reuses the last BPM.
|
||
|
||
Supported phase exit conditions are fixed duration, entry into the resolved BPM
|
||
target, recovery below a threshold, and duration-or-recovery. Only time-based
|
||
conditions can complete without a fresh BPM measurement.
|
||
|
||
Android records ordered phases against the active stable cardio `entry_id`,
|
||
persists target/calibration provenance and stores only instruction changes.
|
||
The full-generation regression proves a completed run with raw BPM evidence is
|
||
imported through `trainlog-cardio-guidance` V1, receives a correlated consumed
|
||
ACK, and is excluded from the next ordinary Android publication. Desktop
|
||
independently verifies canonical cardio session ownership, exercise timeline,
|
||
calibration provenance and exact raw BPM evidence for non-suspended events.
|
||
|
||
The 2026-09-23 closeout passes the complete native Meson inventory
|
||
**110/110**, and the complete Android JVM inventory reports **300 tests:
|
||
295 passed, five skipped, zero failures/errors**. `assembleDebug` succeeds.
|
||
|
||
## Cardio Web visualization V1 validation
|
||
|
||
`TRAINLOG_CARDIO_WEB_VISUALIZATION_V1=PASS` adds one bounded read-only C17
|
||
heart-rate timeline projection and one loopback GET endpoint keyed only by the
|
||
stable session/cardio `se_` or Sleep Diary `sl_` identity. The projection
|
||
returns synchronized raw BPM samples with native RR arrays, canonical
|
||
session/exercise or Sleep/medication markers, guided-cardio phase snapshots and
|
||
instruction changes, and factual calibration peak/recovery data when present.
|
||
It performs no sleep-stage, return-to-sleep, HRV, readiness or medical
|
||
inference.
|
||
|
||
Web History and the Sleep workspace reuse the same React timeline component.
|
||
Min/max/mean facts are calculated over every returned BPM sample. Only the SVG
|
||
projection is bounded for large captures by retaining bucket extrema; the
|
||
source samples and statistics are not downsampled. A large synchronized night
|
||
therefore remains exact at the data boundary without forcing a 100,000-point
|
||
SVG path.
|
||
|
||
The backend test covers cardio BPM/RR, exercise intervals, guidance, calibration,
|
||
Sleep events, medication points, empty contexts and invalid identities. The HTTP
|
||
test covers valid empty GET, bad context rejection and GET-only semantics. The
|
||
2026-09-23 closeout passes the complete native inventory **111/111** and the
|
||
complete Web inventory **29 files / 183 tests**; Web typecheck and production
|
||
build also succeed.
|
||
|
||
## Trainlog 0.1.5 complete validation
|
||
|
||
`TRAINLOG_0_1_5_VALIDATION_V1=SOFTWARE_PASS / DEVICE_VISUAL_PENDING` closes the
|
||
complete software regression after Cardio Web visualization. The native Meson
|
||
inventory passes **111/111**. Android reports **300 JVM tests: 295 passed, five
|
||
historical/external skips, zero failures/errors**, and `assembleDebug`
|
||
succeeds. Web passes **29 files / 183 tests**, TypeScript typecheck and the
|
||
production Vite build.
|
||
|
||
The authorized SM_G990B was backed up before the final private candidate. The
|
||
pre-update `trainlog-android.db` was copied while the app was force-stopped and
|
||
verified at 1,892,352 bytes. Private Trainlog 0.1.5 advanced in place from
|
||
versionCode 42 to versionCode 43 using the exact same Android debug signing
|
||
certificate; `adb install -r` succeeded without uninstall or data clear.
|
||
|
||
Before and after vc43, the Android store remained schema v31 with identical
|
||
high-level business counts: 12 completed sessions, 45 catalogue exercises and
|
||
2 Sleep Diary entries. The four cardio-guidance tables are present after the
|
||
update, `PRAGMA integrity_check` returns `ok`, and
|
||
`PRAGMA foreign_key_check` returns no row.
|
||
|
||
The locked-device background smoke test confirms both Trainlog foreground
|
||
services remain alive. `HeartRateSensorService` continued receiving the real
|
||
CYCPLUS H2 and exposed 94 then 95 BPM through its foreground notification while
|
||
the phone was dozing, proving the final candidate still acquires live heart
|
||
rate without requiring the activity to remain visible.
|
||
|
||
The phone was locked during the final UI probe, so the visual smoke test of
|
||
Sommeil, Calibration cardio and active guided-cardio presentation remains
|
||
`DEVICE_VISUAL_PENDING`. No stable `v0.1.5` tag or release is authorized by
|
||
this closeout.
|
||
|
||
## Sleep tonight readiness V2 validation
|
||
|
||
The validated development delta retains frozen Sleep Diary V1 input and tests
|
||
the active V2 companion separately. Android migration coverage advances schema
|
||
v31 to v32 and proves the additive `quantity` column defaults historical
|
||
intakes to `1`, while the separate quick-publication state does not mark a day
|
||
validated. Desktop migration coverage advances v34 to v35 with the same
|
||
bounded `1..99` quantity contract.
|
||
|
||
Focused Android repository/service coverage proves the full factual lifecycle:
|
||
pre-bed medication creates one durable pending 18:00-to-18:00 entry; additional
|
||
intakes reuse it; Couché adds `BED_TIME` to that same stable entry; and a
|
||
quantity of two remains distinct from the per-unit dose. It also proves
|
||
pre-bed medication opens no HR capture, the first real post-bedtime CYCPLUS
|
||
sample opens/reuses one sleep capture at factual bedtime, Réveil leaves it
|
||
open, Levé closes it at factual final get-up, and a later sample cannot append.
|
||
The no-sensor path proves Couché through Levé creates neither capture nor
|
||
sample.
|
||
|
||
Generation/import coverage proves a pending entry can synchronize without a
|
||
fabricated `BED_TIME`, the later bedtime revision advances the same identity,
|
||
quantity round-trips exactly, V1 intake input reads as quantity one, and replay
|
||
remains idempotent. The Web suite covers all period nights, row selection and
|
||
the selected full-width Sleep HR detail, shared 18:00-axis geometry, quantity
|
||
editing/presentation and PDF `×N` presentation. HR variation unit coverage
|
||
includes stable, rise, fall, insufficient-baseline, short-capture, gap and
|
||
boundary cases.
|
||
|
||
The reported validation passes the complete Meson inventory **111/111**, both
|
||
import-contract checks **6/6**, Android `testDebugUnitTest` and `assembleDebug`,
|
||
and the complete Web inventory **191/191** with typecheck and production build.
|
||
JSON validators, changed-file formatting, and `git diff --check` also pass.
|
||
This is software evidence only: it records no device rollout and authorizes no
|
||
stable release.
|