release: prepare Trainlog 0.1.0 baseline
This commit is contained in:
parent
89abf531a3
commit
09f120566d
152 changed files with 1239 additions and 1454 deletions
23
AGENTS.md
23
AGENTS.md
|
|
@ -164,7 +164,7 @@ It is not the canonical analytics store.
|
||||||
The Android UI is driven by exercise metadata, never by exercise-name
|
The Android UI is driven by exercise metadata, never by exercise-name
|
||||||
heuristics.
|
heuristics.
|
||||||
|
|
||||||
Android local SQLite schema v12 retains the exactly-one durable active-session
|
Android local SQLite schema v17 retains the exactly-one durable active-session
|
||||||
draft introduced in v4.
|
draft introduced in v4.
|
||||||
Every meaningful draft/form mutation is persisted by the repository. Back,
|
Every meaningful draft/form mutation is persisted by the repository. Back,
|
||||||
backgrounding and process death never delete the draft. Home offers explicit
|
backgrounding and process death never delete the draft. Home offers explicit
|
||||||
|
|
@ -177,7 +177,9 @@ Android schema v11 additionally stores optional planning metadata separately
|
||||||
from actual occurrence data after the additive v10 -> v11 migration. Existing
|
from actual occurrence data after the additive v10 -> v11 migration. Existing
|
||||||
rows retain `load_mode=none`, zero rest and NULL targets. Android v12 adds the
|
rows retain `load_mode=none`, zero rest and NULL targets. Android v12 adds the
|
||||||
durable flattened exercise-alias mapping used to resolve retired creator IDs.
|
durable flattened exercise-alias mapping used to resolve retired creator IDs.
|
||||||
Desktop schema v12 owns the same alias compatibility boundary. The active
|
Desktop schema v12 owns the same alias compatibility boundary. Later additive
|
||||||
|
desktop migrations reach v18 and Android migrations reach v17 without changing
|
||||||
|
that identity contract. The active
|
||||||
completed-session exchange is the separate strict
|
completed-session exchange is the separate strict
|
||||||
`trainlog-mobile-export` V3; V1/V2 remain readable and `TRAINLOG_FORMAT_V1`
|
`trainlog-mobile-export` V3; V1/V2 remain readable and `TRAINLOG_FORMAT_V1`
|
||||||
remains unchanged.
|
remains unchanged.
|
||||||
|
|
@ -230,9 +232,11 @@ Desktop SQLite schema is versioned with:
|
||||||
PRAGMA user_version;
|
PRAGMA user_version;
|
||||||
```
|
```
|
||||||
|
|
||||||
The current desktop schema is v12. Schema v9 added an occurrence-owned explicit
|
The current desktop schema is v18. Schema v9 added an occurrence-owned explicit
|
||||||
maximum result; v10/v11 added body-zone and planning metadata, and v12 adds
|
maximum result; v10/v11 added body-zone and planning metadata; v12 added durable
|
||||||
durable flattened exercise aliases. None changes `TRAINLOG_FORMAT_V1`.
|
flattened exercise aliases; v13-v18 add machine metadata, feedback and
|
||||||
|
revisions, occurrence tracking snapshots, causal profile state, and AI proposal
|
||||||
|
storage/publication state. None changes `TRAINLOG_FORMAT_V1`.
|
||||||
|
|
||||||
Every incompatible schema evolution requires an explicit migration and
|
Every incompatible schema evolution requires an explicit migration and
|
||||||
regression coverage.
|
regression coverage.
|
||||||
|
|
@ -276,6 +280,11 @@ Canonical documents:
|
||||||
- `docs/roadmap.md`;
|
- `docs/roadmap.md`;
|
||||||
- `CHANGELOG.md`.
|
- `CHANGELOG.md`.
|
||||||
|
|
||||||
|
`README.md` is the concise repository entry point. `docs/README.md` is the
|
||||||
|
canonical documentation index. `docs/current_state.md` owns current implemented
|
||||||
|
status; `docs/roadmap.md` owns future work. Design and review records are
|
||||||
|
historical evidence and must not override those current owners.
|
||||||
|
|
||||||
Checkpoint history belongs in Git history and `docs/reviews`; canonical
|
Checkpoint history belongs in Git history and `docs/reviews`; canonical
|
||||||
documents describe the current state rather than accumulating obsolete
|
documents describe the current state rather than accumulating obsolete
|
||||||
`NEXT` sections.
|
`NEXT` sections.
|
||||||
|
|
@ -302,6 +311,10 @@ cd android
|
||||||
JAVA_HOME=/usr/lib/jvm/java-17-openjdk ./gradlew assembleDebug
|
JAVA_HOME=/usr/lib/jvm/java-17-openjdk ./gradlew assembleDebug
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Run Android unit and instrumented automation on the host/emulator. The primary
|
||||||
|
personal phone is reserved for manual, non-destructive install and sync smoke
|
||||||
|
tests; do not run `connectedDebugAndroidTest` on it.
|
||||||
|
|
||||||
Run ASan/UBSan at meaningful C implementation checkpoints.
|
Run ASan/UBSan at meaningful C implementation checkpoints.
|
||||||
|
|
||||||
Hardware-dependent MTP tests remain explicit manual validations and are not
|
Hardware-dependent MTP tests remain explicit manual validations and are not
|
||||||
|
|
|
||||||
13
CHANGELOG.md
13
CHANGELOG.md
|
|
@ -1,5 +1,18 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
- Documented stable-release distribution through GitHub Releases, including
|
||||||
|
prebuilt Android and architecture-specific dynamically linked Linux assets,
|
||||||
|
SHA-256 verification, runtime/build/optional dependencies, and the release
|
||||||
|
signing and exact-tag requirements. No v0.1.0 release is claimed until a
|
||||||
|
signed APK and authenticated GitHub publication path are available.
|
||||||
|
|
||||||
|
- Completed `TRAINLOG_FULL_REPOSITORY_DOCUMENTATION_AUDIT_V2`: added the
|
||||||
|
canonical documentation index, made README a concise entry point, separated
|
||||||
|
current state from future roadmap ownership, corrected current schema and
|
||||||
|
validation descriptions, and added concise English ownership headers across
|
||||||
|
previously undocumented C, Kotlin, Python, and shell sources. The audit
|
||||||
|
changes documentation comments only and introduces no functional behavior.
|
||||||
|
|
||||||
- Fixed fresh Android database body-zone reconstruction by applying the
|
- Fixed fresh Android database body-zone reconstruction by applying the
|
||||||
PC-published body-zone companion immediately after catalog, flattened aliases
|
PC-published body-zone companion immediately after catalog, flattened aliases
|
||||||
and strict profile-state, before fallible downstream session, equipment, AI
|
and strict profile-state, before fallible downstream session, equipment, AI
|
||||||
|
|
|
||||||
572
README.md
572
README.md
|
|
@ -1,405 +1,249 @@
|
||||||
# Trainlog
|
# Trainlog
|
||||||
|
|
||||||
Trainlog is a local-first workout and body-tracking system with two user
|
Trainlog is a local-first workout and body-data system. Its native Android app
|
||||||
interfaces:
|
is the lightweight field companion for fast capture and glanceable summaries;
|
||||||
|
its C17/Notcurses desktop TUI is the detailed surface for correction,
|
||||||
|
catalogue management, analytics, graphs, and long-term follow-up.
|
||||||
|
|
||||||
- a native Android application optimized for fast data entry during training;
|
Android and desktop each own a local SQLite database. The desktop database is
|
||||||
- a C17/Notcurses TUI used for durable history, editing, visualization,
|
the canonical long-term history. Trainlog synchronizes versioned JSON artifacts
|
||||||
statistics, and synchronization.
|
over direct MTP; it never copies SQLite database files between devices.
|
||||||
|
|
||||||
The desktop SQLite database is the canonical long-term history. Android keeps
|
## Product split
|
||||||
its own local SQLite database so recording remains usable independently from the
|
|
||||||
desktop.
|
|
||||||
|
|
||||||
## Current status
|
| Surface | Primary responsibility |
|
||||||
|
|---|---|
|
||||||
|
| Android | Capture sets, repetitions, loads, durations, continuous activities, feedback, J+1 follow-ups, body measurements, active drafts, and AI proposals; trigger sync; show quick summaries. |
|
||||||
|
| Desktop TUI | Consult and correct canonical history; manage exercises and equipment; analyze body data, MAX results, training statistics, body zones, calendar periods, and longitudinal graphs. |
|
||||||
|
|
||||||
```text
|
In short: **Android captures and summarizes. The TUI analyzes and tracks over
|
||||||
TRAINLOG_FORMAT_V1=FROZEN
|
time.** Android intentionally remains lightweight for analytics.
|
||||||
TRAINLOG_AI_SESSION_DRAFT_V1=VALIDATION_PENDING
|
|
||||||
|
|
||||||
DESKTOP_SCHEMA_V11=PASS
|
## Release links
|
||||||
ANDROID_LOCAL_WORKFLOWS=PASS
|
|
||||||
ANDROID_LOCAL_DATABASE_V10=PASS
|
|
||||||
ANDROID_LOCAL_DATABASE_V11=PASS
|
|
||||||
ANDROID_SESSION_DRAFT_V1=PASS
|
|
||||||
EXERCISE_EDIT_V1=PASS
|
|
||||||
ANDROID_BANNER_PARITY_V1=PASS
|
|
||||||
|
|
||||||
VARIABLE_REPETITION_SETS=PASS
|
- [Download the latest release](https://github.com/labfytools/trainlog/releases/latest)
|
||||||
CONTINUOUS_ACTIVITY_TRACKING=PASS
|
- [Documentation](docs/README.md)
|
||||||
|
- [Changelog](CHANGELOG.md)
|
||||||
|
|
||||||
DIRECT_MTP_TRANSPORT=PASS
|
## Installation
|
||||||
COMMON_SYNC_ENGINE=PASS
|
|
||||||
TRAINLOG_SYNCD=PASS
|
|
||||||
ANDROID_TRIGGERED_SYNC=PASS
|
|
||||||
ANDROID_SYNC_RECEIPT=PASS
|
|
||||||
BIDIRECTIONAL_SYNC_V1=PASS
|
|
||||||
MULTI_OCCURRENCE_SESSION_V2=PASS
|
|
||||||
EQUIPMENT_ASSOCIATIONS_V2=PASS
|
|
||||||
EQUIPMENT_DEFINITIONS_V1=PASS
|
|
||||||
EXERCISE_RECONCILIATION_V2=PASS
|
|
||||||
EXPLICIT_MAX_RESULTS_V1=PASS
|
|
||||||
BODY_ZONES_V1=PASS
|
|
||||||
BODY_ZONE_SYNC_V1=PASS
|
|
||||||
BODY_ZONES_DESKTOP_REAL_MIGRATION=PASS
|
|
||||||
BODY_ZONES_ANDROID_DEVICE_VALIDATION=PASS
|
|
||||||
|
|
||||||
DESKTOP_TESTS=58/58 PASS (latest validated checkpoint)
|
### Download a prebuilt release
|
||||||
ANDROID_BUILD=PASS
|
|
||||||
|
|
||||||
TRAINING_KNOWLEDGE_V1=PASS
|
Stable releases publish an Android APK, an x86-64 Linux TUI executable, and a
|
||||||
SESSION_GENERATOR_V1=PASS
|
`SHA256SUMS` file on [GitHub Releases](https://github.com/labfytools/trainlog/releases).
|
||||||
APP_SHELL_V1=IMPLEMENTED_AWAITING_VISUAL_REVIEW_2
|
Verify the downloaded binary against `SHA256SUMS` before installing it.
|
||||||
```
|
|
||||||
|
|
||||||
`APP_SHELL_V1` provides the shared seven-section application shell on both
|
The Linux TUI artifact is architecture-specific and dynamically linked. It is
|
||||||
platforms: **Accueil**, **Séances**, **Exercices**,
|
not a universally portable Linux binary: the host must provide compatible
|
||||||
**Statistiques**, **Synchronisation**, and **Paramètres**. Completed history
|
runtime libraries listed under [Dependencies](#dependencies).
|
||||||
and the existing generator now live under **Séances**; existing body and MAX
|
|
||||||
views live under **Statistiques**. It records no new statistics, schema, or
|
|
||||||
synchronization protocol. Automated review repairs are recorded, but the
|
|
||||||
status remains awaiting human visual/accessibility review; see
|
|
||||||
[the APP_SHELL_V1 review record](docs/reviews/app_shell_v1.md).
|
|
||||||
|
|
||||||
`TRAINING_KNOWLEDGE_V1` has passed its bounded scientific review, independent
|
|
||||||
temporal delta review, final engineering audit, repair verification, and final
|
|
||||||
executable validation. Its C and
|
|
||||||
Android occurrence pages and latest-MAX context share the settled exact-instant
|
|
||||||
ordering and source-text cursor contract. The independent temporal review
|
|
||||||
returned PASS with no findings. The initial full-tranche audit's four findings
|
|
||||||
were resolved by one bounded repair chain and independently verified. Fresh
|
|
||||||
validation passed: strict build, 56 Meson tests, Python knowledge/temporal
|
|
||||||
tests, validators, headers, sanitizers, 12-form temporal probes, Android 56
|
|
||||||
tests with one known fixture skip, and Java 17 debug assembly.
|
|
||||||
The [temporal correction record](docs/reviews/training_knowledge_v1_temporal_contract.md)
|
|
||||||
defines the accepted grammar, regression evidence and remaining review boundary.
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
```text
|
|
||||||
Android application
|
|
||||||
local SQLite store
|
|
||||||
|
|
|
||||||
automatic mobile snapshot
|
|
||||||
|
|
|
||||||
v
|
|
||||||
Documents/Trainlog on Android storage
|
|
||||||
|
|
|
||||||
| direct MTP / libmtp
|
|
||||||
v
|
|
||||||
trainlog_sync_run()
|
|
||||||
/ \
|
|
||||||
/ \
|
|
||||||
Android -> PC PC -> Android
|
|
||||||
snapshot import catalog publish
|
|
||||||
\ /
|
|
||||||
\ /
|
|
||||||
sync receipt
|
|
||||||
|
|
|
||||||
v
|
|
||||||
Android
|
|
||||||
|
|
||||||
Desktop TUI --------------------+
|
|
||||||
| |
|
|
||||||
+-- same sync engine -------+
|
|
||||||
|
|
|
||||||
v
|
|
||||||
desktop SQLite
|
|
||||||
canonical long-term history
|
|
||||||
```
|
|
||||||
|
|
||||||
No SQLite database file is copied between devices. The desktop does not require
|
|
||||||
a GVFS/FUSE mount of the phone.
|
|
||||||
|
|
||||||
## Exercise model
|
|
||||||
|
|
||||||
Trainlog does not infer behavior from exercise names.
|
|
||||||
|
|
||||||
```text
|
|
||||||
recording_mode = SETS | CONTINUOUS
|
|
||||||
tracking_mode = REPS | DURATION
|
|
||||||
data_fields = SPEED_KMH | DISTANCE_KM
|
|
||||||
```
|
|
||||||
|
|
||||||
Valid model-v1 combinations are:
|
|
||||||
|
|
||||||
```text
|
|
||||||
SETS + REPS
|
|
||||||
SETS + DURATION
|
|
||||||
CONTINUOUS + DURATION
|
|
||||||
```
|
|
||||||
|
|
||||||
Actual repetition sets are stored independently. The Notcurses desktop flow
|
|
||||||
collects planning only, then creates actual work in an ordered table: the user
|
|
||||||
explicitly adds every row and enters its actual repetitions (or duration) and
|
|
||||||
optional load. It does not accept compact performed-repetition input; normal
|
|
||||||
set sessions cannot finish with zero actual rows.
|
|
||||||
|
|
||||||
A session may contain several ordered occurrences of the same catalogue
|
|
||||||
exercise. Each occurrence has a stable `entry_id`, distinct from the stable
|
|
||||||
`exercise_id` of the catalogue item. Equipment selection belongs to that
|
|
||||||
occurrence, as do its actual per-set loads. `external` records an applied or
|
|
||||||
machine-displayed load; `assistance` records assistance and is not interpreted
|
|
||||||
as increasing strength. An actual load is either absent or finite and
|
|
||||||
non-negative, so an explicit zero remains distinct from no recorded load;
|
|
||||||
planned targets remain strictly positive and are never substituted for actuals.
|
|
||||||
|
|
||||||
In a `max_test` session, an occurrence may instead own one explicit positive
|
|
||||||
`max_weight_kg`. This result has no performed set, repetitions, or target-set
|
|
||||||
count. Its identity remains the movement's `exercise_id` plus the occurrence's
|
|
||||||
`entry_id`; optional equipment is context and never owns a shared maximum.
|
|
||||||
|
|
||||||
During V2 synchronization, a different-ID normalized-name collision is merged
|
|
||||||
only when recording/tracking modes match, every other known invariant is
|
|
||||||
compatible, and one `data_fields` mask contains the other. The desktop identity
|
|
||||||
is retained as canonical, the bit-mask union preserves the richer capability,
|
|
||||||
and historical occurrence snapshots remain unchanged; absent optional values
|
|
||||||
stay absent. Incomparable profiles remain explicit conflicts. Name equality
|
|
||||||
alone is never sufficient.
|
|
||||||
|
|
||||||
Exercise body zones are independent metadata sourced from the single canonical
|
|
||||||
[`catalog/body-zones-v1.json`](catalog/body-zones-v1.json) manifest. An exercise
|
|
||||||
may have one primary assignable zone and several distinct secondary zones.
|
|
||||||
Secondary relations require that primary; the explicit unclassified state has
|
|
||||||
no relations at all.
|
|
||||||
`upper_body` and `lower_body` are hierarchy groups used for display and
|
|
||||||
descendant-aware filtering; they are never duplicated as stored relations.
|
|
||||||
`full_body` and `core` remain autonomous. Historical exercises without an
|
|
||||||
objective stable-ID mapping remain visible as **Non renseignés**.
|
|
||||||
|
|
||||||
## Repository layout
|
|
||||||
|
|
||||||
```text
|
|
||||||
android/ native Kotlin/Compose Android client
|
|
||||||
tui/ C17 Notcurses desktop application and core
|
|
||||||
docs/ canonical project documentation
|
|
||||||
format/ frozen Trainlog JSON v1 schema material
|
|
||||||
catalog/ canonical versioned equipment and body-zone manifests
|
|
||||||
examples/ valid frozen-format examples
|
|
||||||
tests/ fixtures and cross-component tests
|
|
||||||
tools/ validators, import/export helpers, sync daemon tooling
|
|
||||||
```
|
|
||||||
|
|
||||||
## Desktop build and validation
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
meson setup --reconfigure build
|
chmod +x trainlog-tui-linux-x86_64-v0.1.0
|
||||||
|
./trainlog-tui-linux-x86_64-v0.1.0
|
||||||
|
```
|
||||||
|
|
||||||
|
The executable can remain in the download directory. Moving it into a directory
|
||||||
|
on `PATH`, such as a user-managed `~/.local/bin`, is optional.
|
||||||
|
|
||||||
|
For Android, download the release APK, allow installation from the browser or
|
||||||
|
file manager when Android requests it, and install the package. Android 8.0
|
||||||
|
(API 26) or later is required. Grant Trainlog all-files access only when direct
|
||||||
|
`Documents/Trainlog` synchronization is needed. Desktop synchronization also
|
||||||
|
requires the Linux side and its MTP dependencies to be installed and configured.
|
||||||
|
|
||||||
|
Android and the TUI in one GitHub Release share the same Trainlog product
|
||||||
|
version. Database schemas and JSON protocol versions are independent.
|
||||||
|
|
||||||
|
### Build from source
|
||||||
|
|
||||||
|
Desktop:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
meson setup build
|
||||||
meson compile -C build
|
meson compile -C build
|
||||||
meson test -C build --print-errorlogs
|
meson test -C build --print-errorlogs
|
||||||
|
|
||||||
python tools/validate_json.py
|
|
||||||
python tools/validate_import_contract.py
|
|
||||||
|
|
||||||
git diff --check
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Local history export for external analysis
|
Android:
|
||||||
|
|
||||||
Create the read-only `TRAINLOG_AI_EXPORT_V1` artifact in the current directory:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python tools/export_ai_history.py --database ~/.local/share/trainlog/trainlog.db
|
|
||||||
```
|
|
||||||
|
|
||||||
The default output is `trainlog_ai_export_v1.json`. It is ignored by Git
|
|
||||||
because it contains user data. After every completely successful desktop sync,
|
|
||||||
the shared sync engine regenerates this artifact and asks the external desktop
|
|
||||||
`rclone` command to copy it to
|
|
||||||
`TrainLog Gdrive:Trainlog/AI/trainlog_ai_export_v1.json`. Export or Drive
|
|
||||||
failure is reported without changing the successful Trainlog sync result.
|
|
||||||
Android never runs `rclone`, and its configuration/OAuth credentials remain in
|
|
||||||
the user's normal `~/.config/rclone/` location outside this repository and the
|
|
||||||
APK. See [the exchange documentation](docs/exchange_format.md) for the
|
|
||||||
versioned raw-data contract and feedback ownership rules.
|
|
||||||
|
|
||||||
## AI session-draft exchange
|
|
||||||
|
|
||||||
`TRAINLOG_AI_SESSION_DRAFT_V1` is a separate, strict proposal exchange; it
|
|
||||||
does not alter `TRAINLOG_FORMAT_V1`, the mobile export, or the existing
|
|
||||||
read-only `TRAINLOG_AI_EXPORT_V1` history export. An external producer places
|
|
||||||
one source object at
|
|
||||||
`TrainLog Gdrive:Trainlog/AI/inbox/trainlog_ai_session_draft_v1.json`. The desktop
|
|
||||||
fetches it during synchronization, validates and imports it transactionally,
|
|
||||||
then copies those exact fetched bytes after that commit to
|
|
||||||
`TrainLog Gdrive:Trainlog/AI/archive/` under its stable `aid_<uuid-v4>`
|
|
||||||
identity. The mutable inbox object is never moved or deleted: its logical
|
|
||||||
replay is idempotent, and an archive failure is visible and retryable without
|
|
||||||
undoing the committed import.
|
|
||||||
|
|
||||||
The desktop then publishes the distinct PC-to-Android companion
|
|
||||||
`trainlog-ai-session-drafts-v1.json` in `Documents/Trainlog`. Android keeps its
|
|
||||||
pending proposals as a collection, separate from its exactly-one active
|
|
||||||
capture draft. Starting one proposal copies targets only into that singleton
|
|
||||||
draft and atomically tombstones the proposal; deleting a proposal also leaves
|
|
||||||
a tombstone, so replay cannot recreate it. No proposal creates performed work,
|
|
||||||
history, MAX, or feedback.
|
|
||||||
|
|
||||||
The source and companion are bounded and strict: UUIDv4 `aid_` identities,
|
|
||||||
nonempty trimmed text when supplied (title 120 and draft notes 2,000 characters;
|
|
||||||
entries have no notes), at most 64 ordered entries, target sets 1..99, target
|
|
||||||
reps 1..999, and at most 256 unpublished drafts per deterministic publication.
|
|
||||||
Successful MTP publication advances the durable batch cursor; failure retries
|
|
||||||
the same batch. Only `SETS` plans are
|
|
||||||
allowed; the selected exercise's REPS-or-DURATION profile determines the one
|
|
||||||
valid target metric. The final real Google Drive and Android-triggered
|
|
||||||
bidirectional smoke test remains manual, so this checkpoint is documented as
|
|
||||||
`VALIDATION_PENDING`, not PASS. See [AI draft exchange](docs/exchange_format.md).
|
|
||||||
|
|
||||||
## Android build
|
|
||||||
|
|
||||||
Android keeps one durable in-progress workout in its local SQLite database.
|
|
||||||
Home offers **Reprendre la séance en cours** after navigation, app switching,
|
|
||||||
Activity recreation, process death or force-stop/relaunch. Added exercises and
|
|
||||||
raw unfinished form text are retained. Removing an exercise affects only the
|
|
||||||
draft; abandoning the draft requires confirmation. Final save atomically
|
|
||||||
creates completed history and clears the draft. Drafts never enter mobile
|
|
||||||
export or desktop synchronization as completed sessions.
|
|
||||||
|
|
||||||
Schema migrations are additive and preserve existing capture data. See
|
|
||||||
[Android behavior](docs/android.md) and [validation](docs/tests.md).
|
|
||||||
|
|
||||||
The current Android schema is v17. Its additive v4 -> v17 chain adds the shared
|
|
||||||
equipment catalogue, per-occurrence equipment links, durable occurrence
|
|
||||||
identities, custom-equipment definition support, explicit MAX results and
|
|
||||||
stable-source Test max resumption, then direct primary/secondary body-zone
|
|
||||||
relations and occurrence planning, without recreating completed history or
|
|
||||||
discarding the active draft. The additive v11 -> v12 migration stores flattened
|
|
||||||
exercise aliases used to resolve retired IDs during synchronization.
|
|
||||||
|
|
||||||
Exercises can be renamed in place from Android. The `ex_<uuid-v4>` identity is
|
|
||||||
unchanged; completed history, an active draft, and synchronization therefore
|
|
||||||
continue to resolve the same logical exercise. Referenced profiles are locked;
|
|
||||||
only unreferenced catalog exercises may change their recording/tracking profile.
|
|
||||||
|
|
||||||
The local Android SDK is intentionally not committed. Configure it with either
|
|
||||||
`ANDROID_HOME` or `android/local.properties`.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd android
|
cd android
|
||||||
|
JAVA_HOME=/usr/lib/jvm/java-17-openjdk ./gradlew test
|
||||||
printf 'sdk.dir=%s\n' "$HOME/Android/Sdk" > local.properties
|
JAVA_HOME=/usr/lib/jvm/java-17-openjdk ./gradlew assembleRelease
|
||||||
|
|
||||||
JAVA_HOME=/usr/lib/jvm/java-17-openjdk \
|
|
||||||
./gradlew testDebugUnitTest assembleDebug
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Android-triggered synchronization
|
`assembleRelease` produces a distributable stable APK only when release signing
|
||||||
|
is configured by the release operator. The repository does not contain signing
|
||||||
|
credentials and does not silently generate them.
|
||||||
|
|
||||||
Launch the desktop TUI from this built checkout with:
|
## Dependencies
|
||||||
|
|
||||||
```bash
|
### Linux runtime dependencies
|
||||||
cd /home/fy59/Documents/trainlog
|
|
||||||
./build/tui/trainlog
|
|
||||||
```
|
|
||||||
|
|
||||||
The desktop database is `$XDG_DATA_HOME/trainlog/trainlog.db`, or
|
The published x86-64 TUI is dynamically linked. The current build directly
|
||||||
`~/.local/share/trainlog/trainlog.db` when `XDG_DATA_HOME` is unset.
|
requires compatible versions of:
|
||||||
|
|
||||||
Build the desktop first, then install the user service:
|
- glibc and the GCC support runtime;
|
||||||
|
- SQLite 3;
|
||||||
|
- libuuid;
|
||||||
|
- utf8proc;
|
||||||
|
- libudev;
|
||||||
|
- libmtp;
|
||||||
|
- Notcurses Core;
|
||||||
|
- the standard math library.
|
||||||
|
|
||||||
```bash
|
Distribution packages may pull additional transitive libraries, including
|
||||||
bash tools/install_syncd_user.sh
|
libusb, ncursesw, unistring, gpm, libgcrypt, libgpg-error, and libdeflate.
|
||||||
```
|
Package names and ABI versions vary by distribution; inspect the release's
|
||||||
|
`ldd` evidence and install packages from the target distribution rather than
|
||||||
|
assuming one universal package command.
|
||||||
|
|
||||||
Check it with:
|
### Optional runtime and integration dependencies
|
||||||
|
|
||||||
```bash
|
- Android is optional when using the desktop TUI alone. USB/MTP synchronization
|
||||||
systemctl --user is-active trainlog-syncd.service
|
requires the Android companion, libudev, libmtp, and an unlocked connected
|
||||||
tail -f ~/.local/state/trainlog/syncd.log
|
device.
|
||||||
```
|
- `trainlog-syncd` installation expects a systemd user session. Manual TUI
|
||||||
|
synchronization does not require the user service.
|
||||||
|
- `rclone` is required only for automatic AI history upload and Drive
|
||||||
|
inbox/archive workflows. Core capture, local history, analytics, and direct
|
||||||
|
MTP synchronization work without `rclone`.
|
||||||
|
|
||||||
On Android:
|
### Build dependencies
|
||||||
|
|
||||||
```text
|
Desktop builds require:
|
||||||
Sync
|
|
||||||
-> Synchroniser maintenant
|
|
||||||
```
|
|
||||||
|
|
||||||
The request is consumed by `trainlog-syncd`, the shared bidirectional engine
|
- Meson and Ninja;
|
||||||
runs, a receipt is returned to Android, and the PC catalog is applied locally.
|
- a C17 compiler toolchain;
|
||||||
The active completed-session exchange is V3 and preserves occurrence
|
- `pkg-config`;
|
||||||
`entry_id`, per-set weights and equipment associations. Frozen V1 artifacts
|
- Notcurses Core development headers;
|
||||||
remain readable as legacy artifacts; they are not silently redefined as V2.
|
- SQLite, libuuid, utf8proc, libudev, and libmtp development headers;
|
||||||
Exercise-zone metadata travels separately in the sole bidirectional
|
- Python 3 for generated sources, validators, import/export helpers, and tests.
|
||||||
`trainlog-exercise-body-zones-v1.json` companion.
|
|
||||||
|
Android builds require JDK 17, an Android SDK supporting the configured API
|
||||||
|
levels, and the Gradle wrapper committed in this repository. People installing
|
||||||
|
the APK do not need Java, Gradle, or the Android SDK.
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
- `docs/current_state.md`: compact canonical implementation snapshot;
|
| Topic | Document | Purpose |
|
||||||
- `docs/architecture.md`: component and ownership boundaries;
|
|---|---|---|
|
||||||
- `docs/exercise_data_model.md`: exercise semantics;
|
| Documentation map | [docs/README.md](docs/README.md) | Canonical index and ownership map. |
|
||||||
- `docs/database.md`: desktop SQLite schema and migrations;
|
| Current state | [docs/current_state.md](docs/current_state.md) | Current implemented schemas, capabilities, validation, and limitations. |
|
||||||
- `docs/android.md`: Android behavior;
|
| Architecture | [docs/architecture.md](docs/architecture.md) | Component, storage, process, and ownership boundaries. |
|
||||||
- `docs/tui.md`: desktop TUI behavior;
|
| Android | [docs/android.md](docs/android.md) | Field-companion behavior and local persistence. |
|
||||||
- `docs/sync_exchange.md`: MTP synchronization artifacts and protocol;
|
| Desktop TUI | [docs/tui.md](docs/tui.md) | Notcurses workflows, correction, and analytics. |
|
||||||
- `docs/exchange_format.md`: frozen Trainlog JSON v1 contract;
|
| Database | [docs/database.md](docs/database.md) | Desktop persistence schema and migration contracts. |
|
||||||
- `docs/tests.md`: validation strategy;
|
| Exercise model | [docs/exercise_data_model.md](docs/exercise_data_model.md) | Exercise, occurrence, load, and MAX semantics. |
|
||||||
- `docs/roadmap.md`: completed gates and future cursor;
|
| Synchronization | [docs/sync_exchange.md](docs/sync_exchange.md) | Active MTP flow and companion artifacts. |
|
||||||
- `docs/domain/knowledge_system.md`: read-only scientific knowledge system,
|
| Exchange formats | [docs/exchange_format.md](docs/exchange_format.md) | Frozen and separately versioned JSON contracts. |
|
||||||
runtime-context boundary and documented future planning pipeline;
|
| Training feedback | [docs/training_feedback.md](docs/training_feedback.md) | Immediate feedback, revisions, and J+1 follow-ups. |
|
||||||
- `AGENTS.md`: development contract.
|
| Tests | [docs/tests.md](docs/tests.md) | Durable validation commands and coverage strategy. |
|
||||||
|
| Roadmap | [docs/roadmap.md](docs/roadmap.md) | Current cursor and future work only. |
|
||||||
|
| Development contract | [AGENTS.md](AGENTS.md) | Repository invariants and contribution rules. |
|
||||||
|
| Change history | [CHANGELOG.md](CHANGELOG.md) | Chronological implementation history. |
|
||||||
|
|
||||||
|
## Architecture overview
|
||||||
|
|
||||||
|
```text
|
||||||
|
Android local SQLite
|
||||||
|
|
|
||||||
|
| versioned JSON snapshots and requests
|
||||||
|
v
|
||||||
|
Documents/Trainlog on Android storage
|
||||||
|
|
|
||||||
|
| direct MTP / libmtp
|
||||||
|
v
|
||||||
|
trainlog_sync_run() <---- trainlog-syncd or desktop TUI
|
||||||
|
|
|
||||||
|
+---- import Android snapshot into desktop SQLite
|
||||||
|
+---- publish desktop catalog companions to Android
|
||||||
|
+---- write a synchronization receipt
|
||||||
|
|
||||||
|
desktop SQLite = canonical long-term history
|
||||||
|
```
|
||||||
|
|
||||||
|
The optional desktop AI flow exports read-only history and exchanges session
|
||||||
|
proposals through Google Drive via the external `rclone` process. Android does
|
||||||
|
not contain cloud credentials and does not run `rclone`.
|
||||||
|
|
||||||
|
## Current status
|
||||||
|
|
||||||
|
As of 2026-09-15:
|
||||||
|
|
||||||
|
- `TRAINLOG_FORMAT_V1=PASS/FROZEN`;
|
||||||
|
- desktop SQLite schema v18 and Android SQLite schema v17;
|
||||||
|
- Notcurses is the only active desktop terminal backend;
|
||||||
|
- direct storage is `/storage/emulated/0/Documents/Trainlog` under Android's
|
||||||
|
all-files access setting;
|
||||||
|
- mobile export V3 is active; V1/V2 remain readable legacy inputs;
|
||||||
|
- `STATS_V1=IMPLEMENTED`, `TRAINING_KNOWLEDGE_V1=PASS`, and
|
||||||
|
`SESSION_GENERATOR_V1=PASS` (the generator is hidden pending V2);
|
||||||
|
- `APP_SHELL_V1=IMPLEMENTED_AWAITING_VISUAL_REVIEW_2`;
|
||||||
|
- `TRAINLOG_AI_SESSION_DRAFT_V1=VALIDATION_PENDING` pending a real Drive plus
|
||||||
|
Android-triggered bidirectional smoke test.
|
||||||
|
|
||||||
|
The latest executable result belongs in
|
||||||
|
[current state](docs/current_state.md), not in multiple README narratives.
|
||||||
|
|
||||||
|
## Desktop development quick start
|
||||||
|
|
||||||
|
Requirements include Meson, Ninja, SQLite3, utf8proc, libuuid, libudev,
|
||||||
|
libmtp, and Notcurses.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
meson setup build
|
||||||
|
meson compile -C build
|
||||||
|
meson test -C build --print-errorlogs
|
||||||
|
./build/tui/trainlog
|
||||||
|
```
|
||||||
|
|
||||||
|
The database path is `$XDG_DATA_HOME/trainlog/trainlog.db`, or
|
||||||
|
`~/.local/share/trainlog/trainlog.db` when `XDG_DATA_HOME` is unset.
|
||||||
|
|
||||||
|
## Android development quick start
|
||||||
|
|
||||||
|
Use Java 17 and provide the Android SDK through `ANDROID_HOME` or the untracked
|
||||||
|
`android/local.properties` file.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd android
|
||||||
|
JAVA_HOME=/usr/lib/jvm/java-17-openjdk ./gradlew test
|
||||||
|
JAVA_HOME=/usr/lib/jvm/java-17-openjdk ./gradlew assembleDebug
|
||||||
|
```
|
||||||
|
|
||||||
|
Instrumented tests belong on an emulator. Do not run
|
||||||
|
`connectedDebugAndroidTest` on the primary personal phone.
|
||||||
|
|
||||||
|
## Synchronization quick start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
meson compile -C build
|
||||||
|
bash tools/install_syncd_user.sh
|
||||||
|
systemctl --user is-active trainlog-syncd.service
|
||||||
|
```
|
||||||
|
|
||||||
|
On Android, grant all-files access for the fixed `Documents/Trainlog`
|
||||||
|
directory, then use **Synchronisation → Synchroniser maintenant**. Hardware MTP
|
||||||
|
validation requires an unlocked connected device and remains an explicit manual
|
||||||
|
check.
|
||||||
|
|
||||||
## Development principles
|
## Development principles
|
||||||
|
|
||||||
- local-first;
|
- Preserve frozen, versioned compatibility boundaries.
|
||||||
- no mandatory cloud account;
|
- Use stable IDs; display names are not identities.
|
||||||
- user-owned data;
|
- Keep performed work separate from plans and continuous activity separate
|
||||||
- versioned persistent and exchange formats;
|
from fake sets.
|
||||||
- stable identities;
|
- Keep Android capture, desktop analytics, persistence, transport, and
|
||||||
- idempotent synchronization;
|
rendering responsibilities distinct.
|
||||||
- no fake data representation to force incompatible models together;
|
- Prefer explicit failure to silent data loss or corruption.
|
||||||
- strict compiler warnings;
|
- Treat migrations, idempotence, tests, and documentation as part of feature
|
||||||
- documentation and tests are part of feature completion.
|
completion.
|
||||||
|
- Keep scientific catalogs separate from runtime user data and preserve stated
|
||||||
|
uncertainty.
|
||||||
|
|
||||||
## Measured max
|
## License and project notes
|
||||||
|
|
||||||
Explicit `max_test` sessions are the only source of measured maxima. Schema v9
|
See [LICENSE](LICENSE). Forgejo is the primary repository; GitHub is a mirror.
|
||||||
stores each new weight result in a one-to-one `max_results` row; it no longer
|
Repository layout, detailed contracts, and historical review records are linked
|
||||||
encodes a maximum as a synthetic `1 × 1` set.
|
from [docs/README.md](docs/README.md).
|
||||||
|
|
||||||
Ordinary training best sets remain ordinary performance even when they exceed a
|
|
||||||
previous max-test result.
|
|
||||||
|
|
||||||
The desktop exercise catalog exposes a separate measured-max view with current
|
|
||||||
result, same-mode record, test history, a dedicated graph, and 60/70/80/90%
|
|
||||||
working loads for external resistance. Working loads are rounded to a selectable
|
|
||||||
practical increment and are not calculated for assistance.
|
|
||||||
|
|
||||||
Android and the TUI expose a dedicated `Test max` form containing exercise,
|
|
||||||
optional equipment, and `Poids max (kg)`. Android can reopen an existing Test
|
|
||||||
max and atomically replace its ordered entries while preserving the original
|
|
||||||
session and occurrence identities.
|
|
||||||
|
|
||||||
```text
|
|
||||||
MEASURED_MAX_V1=PASS
|
|
||||||
WORKING_LOAD_PERCENTAGES=PASS
|
|
||||||
ANDROID_MAX_TEST_SESSION=PASS
|
|
||||||
EXPLICIT_MAX_RESULTS_V1=PASS
|
|
||||||
```
|
|
||||||
|
|
||||||
## Body analytics
|
|
||||||
|
|
||||||
Body analytics are desktop-only. Android remains a capture client.
|
|
||||||
|
|
||||||
The TUI derives descriptive ratios, left/right asymmetry, and an optional
|
|
||||||
circumference-based body-fat estimate from real body observations.
|
|
||||||
|
|
||||||
The estimate requires a local desktop-only analytics profile containing the
|
|
||||||
formula branch and height. Estimated fat mass and lean mass are calculated only
|
|
||||||
when a real body weight is present.
|
|
||||||
|
|
||||||
Estimated values are never persisted as direct measurements.
|
|
||||||
|
|
||||||
```text
|
|
||||||
BODY_ANALYTICS_V1=PASS
|
|
||||||
BODY_COMPOSITION_ESTIMATE=PASS
|
|
||||||
BODY_PROPORTION_RATIOS=PASS
|
|
||||||
BODY_SYMMETRY_ANALYTICS=PASS
|
|
||||||
DESKTOP_TESTS=39/39 PASS
|
|
||||||
```
|
|
||||||
|
|
||||||
`SESSION_GENERATOR_V1` remains implemented but is hidden from normal Android
|
|
||||||
and TUI navigation pending V2. `catalog/exercise-names-v1.json` owns mapped
|
|
||||||
exercise display metadata across sync while preserving stable IDs and history.
|
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,10 @@ ANDROID_INLINE_EXERCISE_ROUTE=IMPLEMENTED
|
||||||
ANDROID_EXERCISE_SHELL=IMPLEMENTED
|
ANDROID_EXERCISE_SHELL=IMPLEMENTED
|
||||||
ANDROID_BODY_SHELL=IMPLEMENTED
|
ANDROID_BODY_SHELL=IMPLEMENTED
|
||||||
|
|
||||||
LOCAL_PERSISTENCE=NEXT
|
LOCAL_PERSISTENCE=IMPLEMENTED
|
||||||
MTP_SYNC=AFTER_LOCAL_WORKFLOW
|
DIRECT_STORAGE=IMPLEMENTED
|
||||||
|
MTP_SYNC=IMPLEMENTED
|
||||||
|
ANDROID_SCHEMA=17
|
||||||
```
|
```
|
||||||
|
|
||||||
## Visual contract
|
## Visual contract
|
||||||
|
|
@ -68,10 +70,12 @@ minSdk 26
|
||||||
JDK 17
|
JDK 17
|
||||||
```
|
```
|
||||||
|
|
||||||
A Gradle wrapper is intentionally not committed by the scaffold script unless
|
The committed Gradle wrapper is the canonical entry point. From this directory:
|
||||||
it can be generated locally. From this directory:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
gradle wrapper --gradle-version 9.6.0
|
JAVA_HOME=/usr/lib/jvm/java-17-openjdk ./gradlew test
|
||||||
./gradlew assembleDebug
|
JAVA_HOME=/usr/lib/jvm/java-17-openjdk ./gradlew assembleDebug
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Instrumented tests run on an emulator. Do not run
|
||||||
|
`connectedDebugAndroidTest` on the primary personal phone.
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Android TrainlogRepositoryDraftInstrumentedTest.
|
||||||
|
*
|
||||||
|
* Owns this data-layer boundary while keeping UI state, canonical desktop history, and exchange contracts separate.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.data
|
package com.labfytools.trainlog.data
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Android SessionDraftUiInstrumentedTest.
|
||||||
|
*
|
||||||
|
* Owns this Compose presentation boundary; durable state and domain rules remain in repository and model layers.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.ui
|
package com.labfytools.trainlog.ui
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Debug-only DraftUiTestActivity.
|
||||||
|
*
|
||||||
|
* Supports isolated Android UI validation and does not participate in release behavior.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.ui
|
package com.labfytools.trainlog.ui
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Android MainActivity.
|
||||||
|
*
|
||||||
|
* Connects the application lifecycle to Trainlog UI without owning domain or persistence behavior.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog
|
package com.labfytools.trainlog
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Android BodyZoneCatalog.
|
||||||
|
*
|
||||||
|
* Owns this data-layer boundary while keeping UI state, canonical desktop history, and exchange contracts separate.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.data
|
package com.labfytools.trainlog.data
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Android BodyZoneHomeOverview.
|
||||||
|
*
|
||||||
|
* Owns this data-layer boundary while keeping UI state, canonical desktop history, and exchange contracts separate.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.data
|
package com.labfytools.trainlog.data
|
||||||
|
|
||||||
enum class BodyZoneHomeState(val label: String) {
|
enum class BodyZoneHomeState(val label: String) {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Android DirectStoragePermission.
|
||||||
|
*
|
||||||
|
* Owns this data-layer boundary while keeping UI state, canonical desktop history, and exchange contracts separate.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.data
|
package com.labfytools.trainlog.data
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Android EquipmentCatalog.
|
||||||
|
*
|
||||||
|
* Owns this data-layer boundary while keeping UI state, canonical desktop history, and exchange contracts separate.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.data
|
package com.labfytools.trainlog.data
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Android ExchangeSafTree.
|
||||||
|
*
|
||||||
|
* Owns this data-layer boundary while keeping UI state, canonical desktop history, and exchange contracts separate.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.data
|
package com.labfytools.trainlog.data
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Android ExerciseNameCatalog.
|
||||||
|
*
|
||||||
|
* Owns this data-layer boundary while keeping UI state, canonical desktop history, and exchange contracts separate.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.data
|
package com.labfytools.trainlog.data
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Android PercentMaxCalculator.
|
||||||
|
*
|
||||||
|
* Owns this data-layer boundary while keeping UI state, canonical desktop history, and exchange contracts separate.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.data
|
package com.labfytools.trainlog.data
|
||||||
|
|
||||||
enum class GeneratorLoadChoice { AUTOMATIC, PERCENT_MAX, NONE }
|
enum class GeneratorLoadChoice { AUTOMATIC, PERCENT_MAX, NONE }
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Android SessionGenerationEngine.
|
||||||
|
*
|
||||||
|
* Owns this data-layer boundary while keeping UI state, canonical desktop history, and exchange contracts separate.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.data
|
package com.labfytools.trainlog.data
|
||||||
|
|
||||||
import com.labfytools.trainlog.model.RecordingMode
|
import com.labfytools.trainlog.model.RecordingMode
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Android SessionGenerationPolicy.
|
||||||
|
*
|
||||||
|
* Owns this data-layer boundary while keeping UI state, canonical desktop history, and exchange contracts separate.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.data
|
package com.labfytools.trainlog.data
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Android SessionGeneratorRepositoryModels.
|
||||||
|
*
|
||||||
|
* Owns this data-layer boundary while keeping UI state, canonical desktop history, and exchange contracts separate.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.data
|
package com.labfytools.trainlog.data
|
||||||
|
|
||||||
import com.labfytools.trainlog.model.SessionExercisePlan
|
import com.labfytools.trainlog.model.SessionExercisePlan
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Android StatisticsModels.
|
||||||
|
*
|
||||||
|
* Owns this data-layer boundary while keeping UI state, canonical desktop history, and exchange contracts separate.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.data
|
package com.labfytools.trainlog.data
|
||||||
|
|
||||||
/** STATS_V1 is a read-only projection: none of these values are persisted as facts. */
|
/** STATS_V1 is a read-only projection: none of these values are persisted as facts. */
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Android SyncCatalogInbox.
|
||||||
|
*
|
||||||
|
* Owns this data-layer boundary while keeping UI state, canonical desktop history, and exchange contracts separate.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.data
|
package com.labfytools.trainlog.data
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Android SyncExporter.
|
||||||
|
*
|
||||||
|
* Owns this data-layer boundary while keeping UI state, canonical desktop history, and exchange contracts separate.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.data
|
package com.labfytools.trainlog.data
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Android SyncRequestOutbox.
|
||||||
|
*
|
||||||
|
* Owns this data-layer boundary while keeping UI state, canonical desktop history, and exchange contracts separate.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.data
|
package com.labfytools.trainlog.data
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Android TrainingKnowledgeCatalog.
|
||||||
|
*
|
||||||
|
* Owns this data-layer boundary while keeping UI state, canonical desktop history, and exchange contracts separate.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.data
|
package com.labfytools.trainlog.data
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Android TrainlogRepository.
|
||||||
|
*
|
||||||
|
* Owns this data-layer boundary while keeping UI state, canonical desktop history, and exchange contracts separate.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.data
|
package com.labfytools.trainlog.data
|
||||||
|
|
||||||
import android.content.ContentValues
|
import android.content.ContentValues
|
||||||
|
|
@ -62,6 +67,13 @@ internal fun exerciseProfileRevision(parent: String, recording: String, tracking
|
||||||
return "pr2_" + UUID(bytes.long, bytes.long).toString()
|
return "pr2_" + UUID(bytes.long, bytes.long).toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Advances one exercise profile along its bounded causal revision chain.
|
||||||
|
*
|
||||||
|
* The transaction-owning caller supplies the database. This function mutates
|
||||||
|
* only profile-state ancestry, returns false for missing/exhausted state, and
|
||||||
|
* never rewrites historical occurrence snapshots.
|
||||||
|
*/
|
||||||
private fun advanceExerciseProfileRevision(
|
private fun advanceExerciseProfileRevision(
|
||||||
db: SQLiteDatabase, rowId: Long, recording: String, tracking: String, fields: Int,
|
db: SQLiteDatabase, rowId: Long, recording: String, tracking: String, fields: Int,
|
||||||
): Boolean {
|
): Boolean {
|
||||||
|
|
@ -6884,6 +6896,13 @@ private const val MAX_AI_DRAFT_ENTRIES = 64
|
||||||
private const val MAX_AI_TARGET_SETS = 99
|
private const val MAX_AI_TARGET_SETS = 99
|
||||||
private const val MAX_AI_TARGET_REPS = 999
|
private const val MAX_AI_TARGET_REPS = 999
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Owns creation and ordered migration of the Android-local capture database.
|
||||||
|
*
|
||||||
|
* SQLiteOpenHelper serializes lifecycle callbacks. Each additive migration
|
||||||
|
* preserves completed history and the singleton active draft; desktop schema
|
||||||
|
* numbering and synchronization remain separate contracts.
|
||||||
|
*/
|
||||||
private class TrainlogDatabaseHelper(
|
private class TrainlogDatabaseHelper(
|
||||||
private val appContext: Context,
|
private val appContext: Context,
|
||||||
databaseName: String,
|
databaseName: String,
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Android TrainlogTimestamp.
|
||||||
|
*
|
||||||
|
* Owns this data-layer boundary while keeping UI state, canonical desktop history, and exchange contracts separate.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.data
|
package com.labfytools.trainlog.data
|
||||||
|
|
||||||
internal data class TrainlogTimestampKey(
|
internal data class TrainlogTimestampKey(
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Android BodyModels model.
|
||||||
|
*
|
||||||
|
* Defines in-memory capture semantics without owning persistence, synchronization, or presentation.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.model
|
package com.labfytools.trainlog.model
|
||||||
|
|
||||||
data class BodyObservationDraft(
|
data class BodyObservationDraft(
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Android ExerciseProfile model.
|
||||||
|
*
|
||||||
|
* Defines in-memory capture semantics without owning persistence, synchronization, or presentation.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.model
|
package com.labfytools.trainlog.model
|
||||||
|
|
||||||
enum class RecordingMode(
|
enum class RecordingMode(
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Android SessionModels model.
|
||||||
|
*
|
||||||
|
* Defines in-memory capture semantics without owning persistence, synchronization, or presentation.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.model
|
package com.labfytools.trainlog.model
|
||||||
|
|
||||||
enum class SessionType(
|
enum class SessionType(
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Android AndroidAppShell.
|
||||||
|
*
|
||||||
|
* Owns this Compose presentation boundary; durable state and domain rules remain in repository and model layers.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.ui
|
package com.labfytools.trainlog.ui
|
||||||
|
|
||||||
import androidx.activity.compose.BackHandler
|
import androidx.activity.compose.BackHandler
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Android AndroidSpeechRecognitionAdapter.
|
||||||
|
*
|
||||||
|
* Owns this Compose presentation boundary; durable state and domain rules remain in repository and model layers.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.ui
|
package com.labfytools.trainlog.ui
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Android AppNavigation.
|
||||||
|
*
|
||||||
|
* Owns this Compose presentation boundary; durable state and domain rules remain in repository and model layers.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.ui
|
package com.labfytools.trainlog.ui
|
||||||
|
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Android BodyScreen.
|
||||||
|
*
|
||||||
|
* Owns this Compose presentation boundary; durable state and domain rules remain in repository and model layers.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.ui
|
package com.labfytools.trainlog.ui
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Android CompletedSessionCorrectionScreen.
|
||||||
|
*
|
||||||
|
* Owns this Compose presentation boundary; durable state and domain rules remain in repository and model layers.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.ui
|
package com.labfytools.trainlog.ui
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Android ExerciseScreen.
|
||||||
|
*
|
||||||
|
* Owns this Compose presentation boundary; durable state and domain rules remain in repository and model layers.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.ui
|
package com.labfytools.trainlog.ui
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Android FeedbackDictation.
|
||||||
|
*
|
||||||
|
* Owns this Compose presentation boundary; durable state and domain rules remain in repository and model layers.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.ui
|
package com.labfytools.trainlog.ui
|
||||||
|
|
||||||
/** Explicit, platform-independent dictation state used by Compose and tests. */
|
/** Explicit, platform-independent dictation state used by Compose and tests. */
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Android FeedbackEditor.
|
||||||
|
*
|
||||||
|
* Owns this Compose presentation boundary; durable state and domain rules remain in repository and model layers.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.ui
|
package com.labfytools.trainlog.ui
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Android HistoryScreen.
|
||||||
|
*
|
||||||
|
* Owns this Compose presentation boundary; durable state and domain rules remain in repository and model layers.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.ui
|
package com.labfytools.trainlog.ui
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Android HomeScreen.
|
||||||
|
*
|
||||||
|
* Owns this Compose presentation boundary; durable state and domain rules remain in repository and model layers.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.ui
|
package com.labfytools.trainlog.ui
|
||||||
|
|
||||||
import androidx.compose.foundation.Canvas
|
import androidx.compose.foundation.Canvas
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Android SectionScreens.
|
||||||
|
*
|
||||||
|
* Owns this Compose presentation boundary; durable state and domain rules remain in repository and model layers.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.ui
|
package com.labfytools.trainlog.ui
|
||||||
|
|
||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Android SessionDetailScreen.
|
||||||
|
*
|
||||||
|
* Owns this Compose presentation boundary; durable state and domain rules remain in repository and model layers.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.ui
|
package com.labfytools.trainlog.ui
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Android SessionGeneratorScreen.
|
||||||
|
*
|
||||||
|
* Owns this Compose presentation boundary; durable state and domain rules remain in repository and model layers.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.ui
|
package com.labfytools.trainlog.ui
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Android SessionScreen.
|
||||||
|
*
|
||||||
|
* Owns this Compose presentation boundary; durable state and domain rules remain in repository and model layers.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.ui
|
package com.labfytools.trainlog.ui
|
||||||
|
|
||||||
/* TRAINLOG_ANDROID_MAX_TEST_SESSION_V1 */
|
/* TRAINLOG_ANDROID_MAX_TEST_SESSION_V1 */
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Android StatisticsScreen.
|
||||||
|
*
|
||||||
|
* Owns this Compose presentation boundary; durable state and domain rules remain in repository and model layers.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.ui
|
package com.labfytools.trainlog.ui
|
||||||
|
|
||||||
import androidx.compose.foundation.Canvas
|
import androidx.compose.foundation.Canvas
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Android SyncScreen.
|
||||||
|
*
|
||||||
|
* Owns this Compose presentation boundary; durable state and domain rules remain in repository and model layers.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.ui
|
package com.labfytools.trainlog.ui
|
||||||
|
|
||||||
/* TRAINLOG_ANDROID_TRIGGERED_SYNC_V1 */
|
/* TRAINLOG_ANDROID_TRIGGERED_SYNC_V1 */
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Android TrainlogApp.
|
||||||
|
*
|
||||||
|
* Owns this Compose presentation boundary; durable state and domain rules remain in repository and model layers.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.ui
|
package com.labfytools.trainlog.ui
|
||||||
|
|
||||||
/* TRAINLOG_PC_CATALOG_AUTO_APPLY */
|
/* TRAINLOG_PC_CATALOG_AUTO_APPLY */
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Android TrainlogComponents.
|
||||||
|
*
|
||||||
|
* Owns this Compose presentation boundary; durable state and domain rules remain in repository and model layers.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.ui
|
package com.labfytools.trainlog.ui
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Android TrainlogIcons.
|
||||||
|
*
|
||||||
|
* Owns this Compose presentation boundary; durable state and domain rules remain in repository and model layers.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.ui
|
package com.labfytools.trainlog.ui
|
||||||
|
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Android TrainlogTheme.
|
||||||
|
*
|
||||||
|
* Owns shared presentation tokens without changing domain, persistence, or synchronization semantics.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.ui.theme
|
package com.labfytools.trainlog.ui.theme
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Regression coverage for AiSessionDraftRepositoryTest.
|
||||||
|
*
|
||||||
|
* Exercises production contracts without owning runtime behavior or persistent formats.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.data
|
package com.labfytools.trainlog.data
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Regression coverage for AndroidV10PlanningMigrationTest.
|
||||||
|
*
|
||||||
|
* Exercises production contracts without owning runtime behavior or persistent formats.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.data
|
package com.labfytools.trainlog.data
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Regression coverage for AndroidV11ExerciseAliasMigrationTest.
|
||||||
|
*
|
||||||
|
* Exercises production contracts without owning runtime behavior or persistent formats.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.data
|
package com.labfytools.trainlog.data
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Regression coverage for AndroidV16AiSessionDraftMigrationTest.
|
||||||
|
*
|
||||||
|
* Exercises production contracts without owning runtime behavior or persistent formats.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.data
|
package com.labfytools.trainlog.data
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Regression coverage for BodyZoneHomeOverviewRepositoryTest.
|
||||||
|
*
|
||||||
|
* Exercises production contracts without owning runtime behavior or persistent formats.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.data
|
package com.labfytools.trainlog.data
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Regression coverage for BodyZonesTest.
|
||||||
|
*
|
||||||
|
* Exercises production contracts without owning runtime behavior or persistent formats.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.data
|
package com.labfytools.trainlog.data
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Regression coverage for DirectExchangeStorageTest.
|
||||||
|
*
|
||||||
|
* Exercises production contracts without owning runtime behavior or persistent formats.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.data
|
package com.labfytools.trainlog.data
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Regression coverage for EquipmentCatalogTest.
|
||||||
|
*
|
||||||
|
* Exercises production contracts without owning runtime behavior or persistent formats.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.data
|
package com.labfytools.trainlog.data
|
||||||
|
|
||||||
import androidx.test.core.app.ApplicationProvider
|
import androidx.test.core.app.ApplicationProvider
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Regression coverage for ExerciseProfileStateSyncTest.
|
||||||
|
*
|
||||||
|
* Exercises production contracts without owning runtime behavior or persistent formats.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.data
|
package com.labfytools.trainlog.data
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Regression coverage for FeedbackElapsedLabelTest.
|
||||||
|
*
|
||||||
|
* Exercises production contracts without owning runtime behavior or persistent formats.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.data
|
package com.labfytools.trainlog.data
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Assert.assertTrue
|
import org.junit.Assert.assertTrue
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Regression coverage for HistoricalSessionProfileImportTest.
|
||||||
|
*
|
||||||
|
* Exercises production contracts without owning runtime behavior or persistent formats.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.data
|
package com.labfytools.trainlog.data
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Regression coverage for PercentMaxCalculatorTest.
|
||||||
|
*
|
||||||
|
* Exercises production contracts without owning runtime behavior or persistent formats.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.data
|
package com.labfytools.trainlog.data
|
||||||
|
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Regression coverage for RealAndroidV12MachineExerciseMigrationTest.
|
||||||
|
*
|
||||||
|
* Exercises production contracts without owning runtime behavior or persistent formats.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.data
|
package com.labfytools.trainlog.data
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Regression coverage for RealAndroidV14FeedbackRevisionMigrationTest.
|
||||||
|
*
|
||||||
|
* Exercises production contracts without owning runtime behavior or persistent formats.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.data
|
package com.labfytools.trainlog.data
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Regression coverage for RealAndroidV9BodyZonesMigrationTest.
|
||||||
|
*
|
||||||
|
* Exercises production contracts without owning runtime behavior or persistent formats.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.data
|
package com.labfytools.trainlog.data
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Regression coverage for SessionGenerationEngineTest.
|
||||||
|
*
|
||||||
|
* Exercises production contracts without owning runtime behavior or persistent formats.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.data
|
package com.labfytools.trainlog.data
|
||||||
|
|
||||||
import androidx.test.core.app.ApplicationProvider
|
import androidx.test.core.app.ApplicationProvider
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Regression coverage for SessionGeneratorRepositoryTest.
|
||||||
|
*
|
||||||
|
* Exercises production contracts without owning runtime behavior or persistent formats.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.data
|
package com.labfytools.trainlog.data
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Regression coverage for StatisticsRepositoryTest.
|
||||||
|
*
|
||||||
|
* Exercises production contracts without owning runtime behavior or persistent formats.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.data
|
package com.labfytools.trainlog.data
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Regression coverage for TrainingExerciseContextTest.
|
||||||
|
*
|
||||||
|
* Exercises production contracts without owning runtime behavior or persistent formats.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.data
|
package com.labfytools.trainlog.data
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Regression coverage for TrainingFeedbackRepositoryTest.
|
||||||
|
*
|
||||||
|
* Exercises production contracts without owning runtime behavior or persistent formats.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.data
|
package com.labfytools.trainlog.data
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Regression coverage for TrainingKnowledgeCatalogTest.
|
||||||
|
*
|
||||||
|
* Exercises production contracts without owning runtime behavior or persistent formats.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.data
|
package com.labfytools.trainlog.data
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Regression coverage for TrainlogRepositoryDraftTest.
|
||||||
|
*
|
||||||
|
* Exercises production contracts without owning runtime behavior or persistent formats.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.data
|
package com.labfytools.trainlog.data
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Regression coverage for TrainlogTimestampTest.
|
||||||
|
*
|
||||||
|
* Exercises production contracts without owning runtime behavior or persistent formats.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.data
|
package com.labfytools.trainlog.data
|
||||||
|
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Regression coverage for AiSessionDraftUiWiringTest.
|
||||||
|
*
|
||||||
|
* Exercises production contracts without owning runtime behavior or persistent formats.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.ui
|
package com.labfytools.trainlog.ui
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Regression coverage for AndroidSpeechRecognitionIntentTest.
|
||||||
|
*
|
||||||
|
* Exercises production contracts without owning runtime behavior or persistent formats.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.ui
|
package com.labfytools.trainlog.ui
|
||||||
|
|
||||||
import android.speech.RecognizerIntent
|
import android.speech.RecognizerIntent
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Regression coverage for AndroidUiRedesignTest.
|
||||||
|
*
|
||||||
|
* Exercises production contracts without owning runtime behavior or persistent formats.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.ui
|
package com.labfytools.trainlog.ui
|
||||||
|
|
||||||
import androidx.compose.ui.test.junit4.createComposeRule
|
import androidx.compose.ui.test.junit4.createComposeRule
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Regression coverage for AppNavigationStateTest.
|
||||||
|
*
|
||||||
|
* Exercises production contracts without owning runtime behavior or persistent formats.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.ui
|
package com.labfytools.trainlog.ui
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Regression coverage for BodyZoneHomePresentationTest.
|
||||||
|
*
|
||||||
|
* Exercises production contracts without owning runtime behavior or persistent formats.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.ui
|
package com.labfytools.trainlog.ui
|
||||||
|
|
||||||
import com.labfytools.trainlog.data.BodyZoneHomeState
|
import com.labfytools.trainlog.data.BodyZoneHomeState
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Regression coverage for BodyZoneHomeSemanticsTest.
|
||||||
|
*
|
||||||
|
* Exercises production contracts without owning runtime behavior or persistent formats.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.ui
|
package com.labfytools.trainlog.ui
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Regression coverage for DraftFeedbackUiReadbackTest.
|
||||||
|
*
|
||||||
|
* Exercises production contracts without owning runtime behavior or persistent formats.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.ui
|
package com.labfytools.trainlog.ui
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Regression coverage for FeedbackDictationControllerTest.
|
||||||
|
*
|
||||||
|
* Exercises production contracts without owning runtime behavior or persistent formats.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.ui
|
package com.labfytools.trainlog.ui
|
||||||
|
|
||||||
import org.junit.Assert.*
|
import org.junit.Assert.*
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Regression coverage for FeedbackEditorVoiceUiTest.
|
||||||
|
*
|
||||||
|
* Exercises production contracts without owning runtime behavior or persistent formats.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.ui
|
package com.labfytools.trainlog.ui
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Regression coverage for SessionGeneratorPreviewControllerTest.
|
||||||
|
*
|
||||||
|
* Exercises production contracts without owning runtime behavior or persistent formats.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.ui
|
package com.labfytools.trainlog.ui
|
||||||
|
|
||||||
import com.labfytools.trainlog.data.BodyZoneRecentExposure
|
import com.labfytools.trainlog.data.BodyZoneRecentExposure
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Regression coverage for SessionSetRowEditorTest.
|
||||||
|
*
|
||||||
|
* Exercises production contracts without owning runtime behavior or persistent formats.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.ui
|
package com.labfytools.trainlog.ui
|
||||||
|
|
||||||
import com.labfytools.trainlog.model.ExerciseProfile
|
import com.labfytools.trainlog.model.ExerciseProfile
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Regression coverage for StatisticsScreenPresentationTest.
|
||||||
|
*
|
||||||
|
* Exercises production contracts without owning runtime behavior or persistent formats.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.ui
|
package com.labfytools.trainlog.ui
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Regression coverage for SyncBundlePublicationTest.
|
||||||
|
*
|
||||||
|
* Exercises production contracts without owning runtime behavior or persistent formats.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.ui
|
package com.labfytools.trainlog.ui
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Regression coverage for TrainlogAppNavigationCallbackTest.
|
||||||
|
*
|
||||||
|
* Exercises production contracts without owning runtime behavior or persistent formats.
|
||||||
|
*/
|
||||||
package com.labfytools.trainlog.ui
|
package com.labfytools.trainlog.ui
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
|
|
||||||
73
docs/README.md
Normal file
73
docs/README.md
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
# Trainlog documentation
|
||||||
|
|
||||||
|
This file is the canonical documentation index. Canonical documents describe
|
||||||
|
the current contract or state; `reviews/` and `CHANGELOG.md` retain historical
|
||||||
|
evidence and chronology.
|
||||||
|
|
||||||
|
## Start here
|
||||||
|
|
||||||
|
- [Current state](current_state.md) owns present schemas, capabilities,
|
||||||
|
validation, and active limitations.
|
||||||
|
- [Architecture](architecture.md) owns component, process, storage, transport,
|
||||||
|
and responsibility boundaries.
|
||||||
|
- [Roadmap](roadmap.md) owns the current cursor and future work.
|
||||||
|
|
||||||
|
## Interfaces
|
||||||
|
|
||||||
|
- [Android](android.md) owns field-companion capture, local persistence,
|
||||||
|
glanceable summaries, permissions, and Android interaction rules.
|
||||||
|
- [Desktop TUI](tui.md) owns Notcurses navigation, correction, detailed
|
||||||
|
consultation, analytics, graphs, and long-term follow-up behavior.
|
||||||
|
|
||||||
|
## Data model
|
||||||
|
|
||||||
|
- [Exercise data model](exercise_data_model.md) owns exercise profiles,
|
||||||
|
occurrence identity, planned-versus-actual work, load, and MAX semantics.
|
||||||
|
- [Database](database.md) owns desktop SQLite tables, constraints, transactions,
|
||||||
|
and migrations.
|
||||||
|
- [Training feedback](training_feedback.md) owns immediate feedback, immutable
|
||||||
|
revisions, and J+1/session follow-up semantics.
|
||||||
|
- [Domain documentation](domain/) owns cited anatomy, biomechanics, equipment
|
||||||
|
interpretation, training knowledge, and session-generation science.
|
||||||
|
|
||||||
|
## Synchronization and exchange
|
||||||
|
|
||||||
|
- [Synchronization exchange](sync_exchange.md) owns the active MTP workflow,
|
||||||
|
artifact direction, replay, publication, receipts, and daemon behavior.
|
||||||
|
- [Exchange formats](exchange_format.md) owns frozen Trainlog JSON V1 and
|
||||||
|
separately versioned contracts, including AI exchange definitions.
|
||||||
|
|
||||||
|
## Analytics
|
||||||
|
|
||||||
|
- [Desktop TUI](tui.md) owns visible statistics, MAX, body analytics, calendar
|
||||||
|
buckets, and graph behavior.
|
||||||
|
- [Current state](current_state.md) records which analytics are implemented.
|
||||||
|
- [Exercise data model](exercise_data_model.md) owns the distinctions between
|
||||||
|
measured MAX, performed work, plans, and estimates.
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
- [Tests and validation](tests.md) owns durable build, test, sanitizer,
|
||||||
|
contract, and hardware-validation commands.
|
||||||
|
- [Coding style](coding_style.md) owns C17/Kotlin style and comment rules.
|
||||||
|
- [Development contract](../AGENTS.md) owns frozen boundaries, invariants,
|
||||||
|
validation policy, and safe device-testing policy.
|
||||||
|
- [Android build notes](../android/README.md) are the Android setup entry point.
|
||||||
|
- [Format material](../format/README.md) indexes frozen schema assets.
|
||||||
|
|
||||||
|
## Design
|
||||||
|
|
||||||
|
- [Design records](design/) retain proposals and design-gate evidence. They are
|
||||||
|
not the authority for current implementation status.
|
||||||
|
|
||||||
|
## Reviews and history
|
||||||
|
|
||||||
|
- [Review records](reviews/) retain historical audits, incidents, checkpoints,
|
||||||
|
and validation evidence. Their status and paths are historical.
|
||||||
|
- [Changelog](../CHANGELOG.md) owns chronological change history.
|
||||||
|
|
||||||
|
## Ownership rule
|
||||||
|
|
||||||
|
Do not copy long normative explanations between documents. The repository
|
||||||
|
`README.md` is the entry point, this file is the index,
|
||||||
|
`current_state.md` is the snapshot, and `roadmap.md` is future-oriented.
|
||||||
|
|
@ -1,615 +1,133 @@
|
||||||
# Current implementation state
|
# Current implementation state
|
||||||
|
|
||||||
Canonical snapshot: 2026-09-10.
|
Snapshot date: **2026-09-15**.
|
||||||
|
|
||||||
This document is the compact source of truth for the implemented Trainlog
|
This document owns the current implemented state. Historical checkpoints and
|
||||||
baseline. Detailed behavior belongs in the topic-specific documents.
|
closed incidents belong in [reviews](reviews/) and the
|
||||||
|
[changelog](../CHANGELOG.md).
|
||||||
|
|
||||||
## Status
|
## Product boundary
|
||||||
|
|
||||||
```text
|
Android is the field companion: it captures training and body data, preserves
|
||||||
GATE_0_PROJECT_CONTRACT=PASS
|
the active draft, receives AI proposals, triggers synchronization, and shows
|
||||||
GATE_1_TRAINLOG_FORMAT_V1=PASS/FROZEN
|
quick summaries. The C17/Notcurses TUI is the detailed consultation,
|
||||||
GATE_2_PERSISTENCE_AND_USABLE_TUI=PASS
|
correction, catalogue, analytics, graphing, and long-term tracking surface.
|
||||||
|
|
||||||
TRAINLOG_FORMAT_V1=FROZEN
|
Android captures and summarizes. The TUI analyzes and tracks over time. The
|
||||||
TRAINLOG_AI_EXPORT_V1=PASS
|
desktop SQLite database remains the canonical long-term history.
|
||||||
TRAINLOG_AI_GDRIVE_SYNC_V1=PASS
|
|
||||||
TRAINLOG_AI_SESSION_DRAFT_V1=VALIDATION_PENDING
|
|
||||||
TRAINLOG_ANDROID_BODY_ZONE_REPOPULATION_FIX=PASS
|
|
||||||
|
|
||||||
DESKTOP_SCHEMA_V11=PASS
|
## Versions and compatibility
|
||||||
ANDROID_LOCAL_DATABASE_V10=PASS
|
|
||||||
ANDROID_LOCAL_DATABASE_V11=PASS
|
|
||||||
ANDROID_SESSION_DRAFT_V1=PASS
|
|
||||||
ANDROID_DRAFT_DURABLE=PASS
|
|
||||||
ANDROID_DRAFT_BACKGROUND_SURVIVAL=PASS
|
|
||||||
ANDROID_DRAFT_PROCESS_DEATH_SURVIVAL=PASS
|
|
||||||
ANDROID_DRAFT_FORCE_STOP_SURVIVAL=PASS
|
|
||||||
ANDROID_SESSION_RESUME=PASS
|
|
||||||
ANDROID_DRAFT_FORM_RESTORE=PASS
|
|
||||||
ANDROID_DRAFT_EXERCISE_REMOVE=PASS
|
|
||||||
ANDROID_DRAFT_DISCARD=PASS
|
|
||||||
ANDROID_DRAFT_FINALIZE_ATOMIC=PASS
|
|
||||||
ANDROID_DRAFT_NOT_EXPORTED_AS_SESSION=PASS
|
|
||||||
EXERCISE_EDIT_V1=PASS
|
|
||||||
EXERCISE_RENAME_STABLE_ID=PASS
|
|
||||||
ANDROID_BANNER_PARITY_V1=PASS
|
|
||||||
ANDROID_INSTALL_ADB=PASS
|
|
||||||
ANDROID_USER_DATA_PRESERVED=PASS
|
|
||||||
ANDROID_MAX_V9_INSTALL_ADB=PASS
|
|
||||||
ANDROID_MAX_V9_REAL_DATA_MIGRATION=PASS
|
|
||||||
DESKTOP_MAX_V9_REAL_DATA_MIGRATION=PASS
|
|
||||||
REAL_DATABASE_APPLICATION=PASS
|
|
||||||
|
|
||||||
PROFILE_AWARE_EXERCISES=PASS
|
| Boundary | Current state |
|
||||||
CONTINUOUS_ACTIVITY=PASS
|
|---|---|
|
||||||
VARIABLE_REPETITION_SETS=PASS
|
| Frozen project exchange | `TRAINLOG_FORMAT_V1=PASS/FROZEN` |
|
||||||
|
| Desktop SQLite | schema v18 |
|
||||||
|
| Android SQLite | schema v17 |
|
||||||
|
| Mobile snapshot | V3 active; V1/V2 readable legacy inputs |
|
||||||
|
| Desktop terminal backend | Notcurses only |
|
||||||
|
| AI history export | `TRAINLOG_AI_EXPORT_V1` active |
|
||||||
|
| AI session proposals | `TRAINLOG_AI_SESSION_DRAFT_V1=VALIDATION_PENDING` |
|
||||||
|
| Training knowledge | `TRAINING_KNOWLEDGE_V1=PASS` |
|
||||||
|
| Session generator | `SESSION_GENERATOR_V1=PASS`, hidden pending V2 |
|
||||||
|
| Application shell | `APP_SHELL_V1=IMPLEMENTED_AWAITING_VISUAL_REVIEW_2` |
|
||||||
|
| Statistics | `STATS_V1=IMPLEMENTED` |
|
||||||
|
|
||||||
DIRECT_MTP_TRANSPORT=PASS
|
Desktop and Android schema numbers are independent. Neither changes the frozen
|
||||||
ANDROID_TO_PC_IMPORT=PASS
|
Trainlog JSON V1 contract.
|
||||||
PC_TO_ANDROID_CATALOG=PASS
|
|
||||||
COMMON_SYNC_ENGINE=PASS
|
|
||||||
TRAINLOG_SYNCD=PASS
|
|
||||||
ANDROID_TRIGGERED_SYNC=PASS
|
|
||||||
ANDROID_SYNC_RECEIPT=PASS
|
|
||||||
TUI_SYNC_LOG_SHOW=PASS
|
|
||||||
BIDIRECTIONAL_SYNC_V1=PASS
|
|
||||||
MULTI_OCCURRENCE_SESSION_V2=PASS
|
|
||||||
EQUIPMENT_ASSOCIATIONS_V2=PASS
|
|
||||||
EQUIPMENT_DEFINITIONS_V1=PASS
|
|
||||||
EXERCISE_RECONCILIATION_V2=PASS
|
|
||||||
EXPLICIT_MAX_RESULTS_V1=PASS
|
|
||||||
MAX_TEST_RESUME_STABLE_ID=PASS
|
|
||||||
BODY_ZONES_V1=PASS
|
|
||||||
BODY_ZONE_SYNC_V1=PASS
|
|
||||||
BODY_ZONES_DESKTOP_REAL_MIGRATION=PASS
|
|
||||||
BODY_ZONES_TUI_REAL_VALIDATION=PASS
|
|
||||||
BODY_ZONES_ANDROID_DEVICE_VALIDATION=PASS
|
|
||||||
|
|
||||||
TRAINING_KNOWLEDGE_V1=PASS
|
## Storage and synchronization
|
||||||
SESSION_GENERATOR_V1=PASS
|
|
||||||
APP_SHELL_V1=IMPLEMENTED_AWAITING_VISUAL_REVIEW_2
|
|
||||||
|
|
||||||
DESKTOP_TESTS=52/52 PASS (normal); affected ASan/UBSan tests=6/6 PASS
|
- Android stores capture data in its private SQLite database.
|
||||||
ANDROID_BUILD=PASS
|
- Android reads and writes exchange artifacts only in
|
||||||
TRAINLOG_ANDROID_UI_REDESIGN_V1=PASS
|
`/storage/emulated/0/Documents/Trainlog` after the user grants all-files
|
||||||
HARDWARE_SYNC_VALIDATION=HISTORICAL_PASS
|
access through system settings.
|
||||||
```
|
- The old SAF/tree-URI path and `Download/Trainlog` are historical recovery
|
||||||
|
boundaries, not active storage choices.
|
||||||
## Desktop
|
- Desktop synchronization uses direct libudev/libmtp discovery and object
|
||||||
|
access. No GVFS/FUSE mount is required.
|
||||||
Implemented:
|
- `trainlog_sync_run()` is shared by the TUI and `trainlog-syncd`.
|
||||||
|
- The desktop imports Android snapshots, publishes catalog/profile/feedback/AI
|
||||||
- C17/Notcurses true-color TUI with the APP_SHELL_V1 persistent seven-section
|
companions, and returns a receipt. Stable IDs and tombstones make defined
|
||||||
shell (72x20 minimum, UTF-8 bounded editors, resize fallback, F6 navigation,
|
replay paths idempotent.
|
||||||
F7 actions, and restored focus after overlays/routes);
|
- SQLite database files are never synchronized.
|
||||||
- UTF-8 cell-aware scrolling training-knowledge screen, tested at the 72x20
|
- The desktop AI flow uses external `rclone` for Drive inbox/archive and
|
||||||
minimum terminal;
|
read-only history export. Android owns no Drive credentials.
|
||||||
- SQLite schema v18, with stable ordered `session_exercises.entry_id`, an
|
|
||||||
immutable occurrence-owned `tracking_mode` snapshot,
|
|
||||||
bounded durable current-profile revision ancestry,
|
|
||||||
occurrence-level equipment identity, and desktop-local custom-equipment
|
|
||||||
definitions, plus occurrence-owned `max_results`; its v9 -> v10 migration
|
|
||||||
rebuilds only `performed_sets` to permit explicit zero actual loads while
|
|
||||||
preserving historic NULL and positive rows;
|
|
||||||
- canonical generated body-zone taxonomy, direct primary/secondary exercise
|
|
||||||
relations, stable-ID-only initial migration and descendant-aware filtering;
|
|
||||||
- direct session entry;
|
|
||||||
- normal desktop SETS planning followed by the table-only explicit actual-row
|
|
||||||
editor; zero-row completion is rejected while MAX and continuous entries keep
|
|
||||||
their separate no-set contracts;
|
|
||||||
- persisted session detail and editing;
|
|
||||||
- exercise removal from a session through transactional child replacement;
|
|
||||||
- exercise catalog;
|
|
||||||
- supplied-equipment browsing/search/detail and desktop-local custom-equipment
|
|
||||||
creation/selection;
|
|
||||||
- profile-aware set and continuous activities;
|
|
||||||
- heterogeneous repetition sets;
|
|
||||||
- multiple occurrences of one catalogue exercise in a session;
|
|
||||||
- per-set actual loads with distinct external/assistance semantics;
|
|
||||||
- body-observation creation/history/editing;
|
|
||||||
- `TRAINLOG_TUI_MEASUREMENTS_GRAPH_V1=PASS`: a reusable Notcurses body-history
|
|
||||||
chart with elapsed-time X spacing, labelled and margined Y scale, Braille 2x4
|
|
||||||
line rasterization, distinct real-measurement markers, responsive geometry,
|
|
||||||
and terminal-free calculation/raster regressions;
|
|
||||||
- `TRAINLOG_TUI_STATISTICS_V1=PASS`: the TUI statistics hub covers recorded
|
|
||||||
body history, global 7-day/30-day/all-time activity, canonical-exercise
|
|
||||||
facts and graphs, persisted primary/secondary body-zone activity, and
|
|
||||||
explicit MAX history. Aggregations are read-only and terminal-independent;
|
|
||||||
volume, planning and feedback scopes remain conservative and auditable;
|
|
||||||
- `TRAINLOG_TUI_STATISTICS_REFINEMENT_V1=PASS`: finite graph windows preserve
|
|
||||||
their selected domain, weekly totals use zero-based discrete bars and local
|
|
||||||
Monday buckets, wide summaries use semantic panels, zone bars share one
|
|
||||||
numeric scale, and MAX defaults to explicitly measured exercises;
|
|
||||||
- `TRAINLOG_TUI_STATISTICS_PERIOD_BUCKETING_V1=PASS`: the 7-day summary compares
|
|
||||||
up to four consecutive calendar-week buckets and preserves empty periods;
|
|
||||||
- `TRAINLOG_TUI_STATISTICS_PERIOD_BUCKET_SIZE_FIX=PASS`: historical checkpoint
|
|
||||||
that separated bucket sizes before the subsequent calendar alignment. Its
|
|
||||||
duration-based full-history adaptation remains active;
|
|
||||||
- `TRAINLOG_TUI_STATISTICS_CALENDAR_BUCKETS_V1=PASS`: the current `7j` summary
|
|
||||||
and bars are Monday–Sunday calendar weeks, while `30j` uses the current and
|
|
||||||
preceding Gregorian calendar months. Persisted civil dates determine bucket
|
|
||||||
membership without reinterpretation through the machine timezone; empty
|
|
||||||
weeks/months and partial current periods remain explicit;
|
|
||||||
- normalized body overlays;
|
|
||||||
- exercise performance history;
|
|
||||||
- direct USB/MTP device access;
|
|
||||||
- manual Android -> PC, PC -> Android, and bidirectional synchronization;
|
|
||||||
- structured synchronization history and detail.
|
|
||||||
|
|
||||||
`TRAINLOG_AI_SESSION_DRAFT_V1` is implemented but awaits one real external
|
|
||||||
Drive/Android-triggered bidirectional smoke test. The Drive source is the
|
|
||||||
single strict `TRAINLOG_AI_SESSION_DRAFT` V1 object at
|
|
||||||
`TrainLog Gdrive:Trainlog/AI/inbox/trainlog_ai_session_draft_v1.json`; after a
|
|
||||||
transactional, idempotent desktop import its exact fetched bytes are copied to
|
|
||||||
the archive by stable `aid_` ID. The inbox object remains available for logical
|
|
||||||
replay and archive/status convergence.
|
|
||||||
Desktop exports the separate `trainlog-ai-session-drafts-v1.json` companion to
|
|
||||||
Android in deterministic batches of at most 256 unpublished proposals. It
|
|
||||||
marks exactly a batch successfully published over MTP and retries an unchanged
|
|
||||||
batch after publication failure; source replay identities remain permanent.
|
|
||||||
The Android pending proposal collection is not the durable singleton
|
|
||||||
active capture draft: explicit start copies targets only and tombstones the
|
|
||||||
proposal in one transaction; explicit deletion tombstones it too. The 64-entry,
|
|
||||||
99-set, 999-rep and 256-proposal limits, profile compatibility and replay rules
|
|
||||||
are enforced. The always-visible **Brouillons** action reports the pending
|
|
||||||
proposal count, and a successful catalogue/companion import invalidates the
|
|
||||||
pending-screen query so newly imported proposals appear without restarting the
|
|
||||||
application. Inbound `NONE`, `IMPORTED`, `ALREADY_IMPORTED`, `REJECTED`,
|
|
||||||
`DRIVE_FAIL`, and `ARCHIVE_FAIL` outcomes remain visible in successful sync
|
|
||||||
history. A transport/archive failure never rolls back a committed import.
|
|
||||||
|
|
||||||
The September 2026 connected-device diagnosis found that the installed APK was
|
|
||||||
the older 2026-09-13 build: its copied local database was still schema v16 and
|
|
||||||
had no `ai_session_drafts` table. That APK therefore could not have imported the
|
|
||||||
Android companion. After a current APK is installed, one new synchronization is
|
|
||||||
required to import the still-pending companion. This diagnosis is not the real
|
|
||||||
Drive/Android acceptance smoke test, which remains pending.
|
|
||||||
|
|
||||||
Primary navigation is now shared with Android:
|
|
||||||
|
|
||||||
```text
|
|
||||||
Accueil | Séances | Exercices | Statistiques | Synchronisation | Paramètres
|
|
||||||
```
|
|
||||||
|
|
||||||
`Séances` contains the current draft, the existing generator, manual entry,
|
|
||||||
and **Séances effectuées**. `Statistiques` exposes existing mensurations,
|
|
||||||
body views and explicit MAX/performance views only. Navigation neither writes
|
|
||||||
data nor launches synchronization. The second automated repair review covered
|
|
||||||
the TUI root search/action paths, selection/focus restoration and section hubs,
|
|
||||||
plus Android compact controls, hidden internal IDs and latest-MAX display
|
|
||||||
semantics. Human visual/accessibility review is still required before this
|
|
||||||
status can advance.
|
|
||||||
|
|
||||||
## Android
|
## Android
|
||||||
|
|
||||||
Implemented:
|
Android currently provides:
|
||||||
|
|
||||||
- native Kotlin/Compose application;
|
- metadata-driven SETS+REPS, SETS+DURATION, and CONTINUOUS+DURATION capture;
|
||||||
- local SQLite database v17, with non-destructive migrations through v17;
|
- actual per-set values, optional occurrence equipment, and explicit MAX rows;
|
||||||
- one durable active-session draft, Home resume and raw-form restoration;
|
- exactly one durable active session draft with resume, confirmed discard, and
|
||||||
- `BODY_FOCUS_HOME_V1`: a compact accessible Home front/back BODY ZONES map,
|
atomic completion;
|
||||||
textual top-three exposure ranking and Catalogue/Statistics drill-through;
|
- completed-session consultation and correction;
|
||||||
- explicit confirmed discard and atomic completed-save/draft-clear;
|
- exercise catalogue editing with stable `exercise_id` and referenced-profile
|
||||||
- schema v15 immutable feedback wording revisions, Android correction UX,
|
protection;
|
||||||
read-only TUI display, and Training Feedback V2 synchronization;
|
- supplied/custom equipment and machine-exercise metadata;
|
||||||
- exercise creation;
|
- body measurements and recent BODY ZONES summaries;
|
||||||
- stable-ID exercise rename/editing with referenced-profile protection;
|
- immediate exercise feedback, immutable wording revisions, and J+1/session
|
||||||
- primary/secondary body-zone selection, display, hierarchy filtering and
|
follow-ups;
|
||||||
prefix-search composition, including visible unclassified exercises;
|
- pending AI proposal collection, explicit start/delete, and durable tombstones;
|
||||||
- inline exercise creation during session entry;
|
- 7/30/90-day, year, and all-history quick statistics;
|
||||||
- profile-aware session recording;
|
- direct-storage snapshot publication, sync triggering, receipt display, and
|
||||||
- heterogeneous repetition-set entry;
|
companion application.
|
||||||
- exercise removal from the current session draft;
|
|
||||||
- continuous activity recording;
|
|
||||||
- shared equipment selection, local custom equipment creation and occurrence
|
|
||||||
equipment persistence;
|
|
||||||
- local session history/detail;
|
|
||||||
- body measurements;
|
|
||||||
- automatic mobile snapshot maintenance;
|
|
||||||
- PC catalog application through user-enabled direct all-files access to the
|
|
||||||
fixed `Documents/Trainlog` directory;
|
|
||||||
- Android-triggered synchronization request;
|
|
||||||
- synchronization receipt handling.
|
|
||||||
|
|
||||||
The Home BODY ZONES overview is exposure-only and read-only. It uses only
|
Android does not own canonical long-term analytics. The V1 session generator is
|
||||||
actual performed sets, continuous activities and explicit MAX results from
|
implemented but intentionally hidden from normal navigation pending V2.
|
||||||
completed history, with canonical parsed-instant 7/30-day windows. Plans,
|
|
||||||
targets and drafts never qualify. Exactly the eight child anatomical zones are
|
|
||||||
processed; grouping zones cannot double count. Primary exposure dominates the
|
|
||||||
stable ranking, while secondary exposure is a weaker tie modifier. Alias
|
|
||||||
history resolves once to the canonical exercise. Unsupported zones are shown
|
|
||||||
as **Aucun exercice résolu disponible** and excluded from suggestions; empty
|
|
||||||
history is stated explicitly and suggests supported zones only. The existing
|
|
||||||
session generator remains hidden from normal UI, and the overview makes no
|
|
||||||
recovery, readiness, fatigue or prescription claim.
|
|
||||||
|
|
||||||
The Android catalog exposes **Modifier** for every existing exercise. A rename
|
## Desktop TUI
|
||||||
updates `name` and `normalized_name` in the original row, never creates an ID,
|
|
||||||
and remains valid for completed session and active-draft references. A profile
|
|
||||||
change is only accepted while the row has neither completed-session nor draft
|
|
||||||
references. Same-ID catalog reconciliation updates display metadata in place.
|
|
||||||
A different-ID normalized-name collision is reconciled only when recording and
|
|
||||||
tracking modes match, other known invariants are compatible, and one
|
|
||||||
`data_fields` mask contains the other. PC identity is canonical on Android;
|
|
||||||
the existing desktop identity is canonical on desktop. The richer bit-mask
|
|
||||||
union is retained without rewriting historical occurrence snapshots.
|
|
||||||
|
|
||||||
All Android screens are hosted by the fixed `AndroidAppShell` Material 3
|
The desktop is a strict C17 application using Notcurses. It currently provides:
|
||||||
`Scaffold` and `TopAppBar`: root routes show `TRAINLOG`, non-root routes show
|
|
||||||
their route title, and page content is rendered beneath that shell. The drawer,
|
|
||||||
top bar and content host keep navigation separate from page ownership.
|
|
||||||
|
|
||||||
## Training knowledge V1
|
- a keyboard-first seven-section shell, semantic true-color roles, UTF-8 input,
|
||||||
|
resize recovery, and a 72x20 minimum-terminal fallback;
|
||||||
|
- session creation, history, detail, and transactional correction;
|
||||||
|
- exercise and equipment catalogue management;
|
||||||
|
- explicit measured-MAX consultation and working-load calculations;
|
||||||
|
- body observation history, correction, derived analytics, and longitudinal
|
||||||
|
graphs;
|
||||||
|
- statistics derived from canonical occurrence-owned actual work, including
|
||||||
|
exact calendar-week/month buckets, performance, body, frequency, exercise,
|
||||||
|
and BODY ZONES views;
|
||||||
|
- detailed synchronization status and history through the shared engine.
|
||||||
|
|
||||||
The implemented read-only training-knowledge layer loads shared versioned JSON
|
Rendering does not own SQL or business rules. Statistics are read-only
|
||||||
catalogs as the sole authored scientific source, generates the immutable C
|
projections; they are not persisted as facts.
|
||||||
catalog representation, and loads the same assets on Android. It has no
|
|
||||||
database migration, no auto-seeding, and no synchronization artifact. The
|
|
||||||
desktop database is schema v18 and Android is schema v17. Schema v13 is additive: exercise
|
|
||||||
rows own machine-facing load semantics, optional legacy equipment provenance,
|
|
||||||
an optional scientific profile, and an explicit resolved/unresolved science
|
|
||||||
state. Legacy equipment tables and occurrence columns remain intact.
|
|
||||||
|
|
||||||
Desktop `training_knowledge.h` and Android `TrainingKnowledgeCatalog` expose
|
## Data semantics
|
||||||
source-linked science lookups and resolved-candidate filters. The additive
|
|
||||||
`equipment-exercise-relations-v2.json` catalog is the canonical
|
|
||||||
equipment-to-exercise compatibility source. Equipment owns physical identity,
|
|
||||||
load semantics, configuration labels, confidence, and source references only;
|
|
||||||
exercise knowledge exclusively owns muscles, movements, and BODY ZONES. Desktop
|
|
||||||
`training_context.h` and Android `TrainlogRepository` compose one real runtime
|
|
||||||
exercise with its persisted zones, compatible equipment, latest explicit MAX,
|
|
||||||
and bounded chronological occurrence/set history. Persisted zones remain
|
|
||||||
separate from scientific mappings; missing scientific knowledge is valid.
|
|
||||||
|
|
||||||
The scientific review passed and the reviewed catalog bytes remain unchanged.
|
- `exercise_id` identifies a catalogue exercise; `entry_id` identifies one
|
||||||
The independent temporal review returned `TEMPORAL_DELTA_REVIEW=PASS`, with no
|
ordered occurrence in a session.
|
||||||
temporal defects or repairs. It reviewed the grammar, calendar and offset
|
- Plans and performed work are distinct. A continuous activity is not a fake
|
||||||
bounds, fraction precision, bytewise ties, cursor aliasing and exclusivity,
|
performed set, and actual repetitions may be heterogeneous.
|
||||||
source capacity, snapshots, and Android/Python parity; it ran the targeted
|
- Explicit MAX results are occurrence-owned and distinct from ordinary best
|
||||||
Meson and four Python temporal tests. The initial full-tranche engineering
|
sets. Equipment/resistance/execution context remains relevant.
|
||||||
audit initially failed with stale temporal-defect documentation (BLOCKER),
|
- BODY ZONES are a UX/business projection over explicit scientific knowledge,
|
||||||
Android loader parity gaps (BLOCKER), omitted Meson generator inputs (BLOCKER),
|
not a complete anatomical taxonomy.
|
||||||
and a C role-only query mismatch (HIGH). One bounded repair chain resolved all
|
- Feedback roots retain identity while append-only revisions preserve wording
|
||||||
four findings; independent repair verification returned
|
history. J+1/session follow-ups are session-owned.
|
||||||
`FINAL_REVIEW_REPAIR_VERIFICATION=PASS` and
|
- AI proposals contain target-only plans and never create performed work,
|
||||||
`TRAINING_KNOWLEDGE_V1_ENGINEERING_REVIEW=PASS`. Fresh final validation passed:
|
history, MAX, or feedback until the user explicitly starts and completes a
|
||||||
strict build, 56 Meson tests, eight knowledge and four temporal Python tests,
|
normal capture draft.
|
||||||
knowledge/JSON/import validators, three C17 headers, affected C knowledge and
|
|
||||||
context tests under ASan/UBSan plus Python timestamp validation, normal and
|
|
||||||
sanitized 12-form temporal probes, and Android 56 tests with zero failures or
|
|
||||||
errors and one known missing-real-v9-fixture skip; Java 17 `assembleDebug` also
|
|
||||||
passed. Generated C is byte-identical with SHA-256
|
|
||||||
`e8c099f67eb111d61621b5d76592c049823af5508d43e73ec646f22e4c377fca`; all six
|
|
||||||
Android assets are byte-identical. Preservation before and after repair confirms
|
|
||||||
historical desktop/Android schemas v11/v10, unchanged catalog/science/temporal bytes, and unchanged real
|
|
||||||
database logical SHA-256 `26139cafeffbde3ec08f6ef23c5069e75afb9cd69ffffb40be5c099006fedc4d`,
|
|
||||||
counts, integrity, and foreign keys. `TRAINING_KNOWLEDGE_V1=PASS`. The
|
|
||||||
[temporal contract](reviews/training_knowledge_v1_temporal_contract.md)
|
|
||||||
defines the settled reader behavior. No manual Android install, manual TUI
|
|
||||||
visual validation or manual MTP validation is claimed for this tranche.
|
|
||||||
|
|
||||||
## Synchronization
|
## Validation status
|
||||||
|
|
||||||
Canonical exchange directory:
|
The durable commands are owned by [tests.md](tests.md). On 2026-09-15 this audit
|
||||||
|
passed desktop compilation, **58/58 Meson tests**, Android unit tests, Android
|
||||||
|
debug assembly, the JSON validator, and the import-contract validator. Link and
|
||||||
|
diff safety checks also pass in the final audit evidence.
|
||||||
|
|
||||||
```text
|
Hardware-dependent MTP and the final real Drive plus Android-triggered AI-draft
|
||||||
Documents/Trainlog
|
smoke test are not automated. The latter is why
|
||||||
```
|
`TRAINLOG_AI_SESSION_DRAFT_V1` remains `VALIDATION_PENDING`.
|
||||||
|
|
||||||
Artifacts:
|
## Active limitations
|
||||||
|
|
||||||
```text
|
- `APP_SHELL_V1` still awaits the recorded human visual/accessibility review.
|
||||||
Android -> PC
|
- AI proposal exchange still awaits one real Drive plus Android-triggered
|
||||||
trainlog-mobile-equipment-definitions-v1.json
|
bidirectional smoke test.
|
||||||
trainlog-mobile-export-v3.json
|
- Session Generator V1 is hidden while V2 planning semantics are developed.
|
||||||
trainlog-equipment-associations-v2.json
|
- Hardware MTP validation requires a connected unlocked Android device.
|
||||||
trainlog-exercise-body-zones-v1.json
|
- Scientific knowledge is bounded to reviewed catalog entries; unknown custom
|
||||||
|
exercises remain unclassified rather than inferred from names.
|
||||||
PC -> Android
|
|
||||||
trainlog-pc-equipment-definitions-v1.json
|
|
||||||
trainlog-pc-catalog-v1.json
|
|
||||||
trainlog-pc-mobile-export-v3.json
|
|
||||||
trainlog-equipment-associations-v2.json
|
|
||||||
trainlog-exercise-body-zones-v1.json
|
|
||||||
|
|
||||||
Android -> PC agent
|
|
||||||
trainlog-sync-request-v1.json
|
|
||||||
|
|
||||||
PC agent -> Android
|
|
||||||
trainlog-sync-receipt-v1.json
|
|
||||||
```
|
|
||||||
|
|
||||||
The desktop TUI and `trainlog-syncd` share `trainlog_sync_run()`. Its explicit
|
|
||||||
`a`, `p`, and `b` modes perform Android -> PC only, PC -> Android only, and
|
|
||||||
inbound-then-outbound respectively. Each direction reconciles equipment
|
|
||||||
definitions V1 before V2 artifacts that reference them.
|
|
||||||
|
|
||||||
After the shared engine has reached a definitive successful result (including
|
|
||||||
publication of an Android-request receipt when applicable), the desktop runs
|
|
||||||
`TRAINLOG_AI_EXPORT_V1` and then the external `rclone copyto` publication to
|
|
||||||
`TrainLog Gdrive:Trainlog/AI/trainlog_ai_export_v1.json`. The TUI summary and
|
|
||||||
`trainlog-syncd` log expose `SYNC`, `AI_EXPORT`, and `GDRIVE_UPLOAD` separately.
|
|
||||||
An export or Drive failure never retroactively changes the successful Trainlog
|
|
||||||
sync result. Android does not execute `rclone`; all rclone/OAuth configuration
|
|
||||||
remains external under the user's normal `~/.config/rclone/` directory.
|
|
||||||
|
|
||||||
Each structured local sync-history run persists its selected `a`, `p`, or `b`
|
|
||||||
direction. `direction inconnue` is reserved for legacy history rows whose
|
|
||||||
direction was not recorded; it is never used for a current run.
|
|
||||||
|
|
||||||
On the TUI Sync page, `a`, `p`, and `b` directly open one confirmation for the
|
|
||||||
selected direction; `Enter` runs that confirmed action once, while `Esc`
|
|
||||||
cancels it without an operation (or returns when idle). `r` refreshes device
|
|
||||||
status only, and the former `s` shortcut is inert. Full and compact footers use
|
|
||||||
the same three directional actions, with compact labels only abbreviating the
|
|
||||||
endpoints.
|
|
||||||
|
|
||||||
V2 resolves equipment by `(session_id, entry_id)`, never by display name or
|
|
||||||
catalogue identity alone. V1 files remain legacy-compatible and do not gain
|
|
||||||
multi-occurrence semantics retroactively.
|
|
||||||
|
|
||||||
Body-zone relations have one direction-neutral companion and are keyed only by
|
|
||||||
canonical zone IDs and `exercise_id`. Identical state is an idempotent skip; a
|
|
||||||
successful publication records that exact state as the publisher baseline, so
|
|
||||||
a later peer-only edit of a new custom exercise is accepted. Simultaneous
|
|
||||||
divergence is an explicit conflict and secondary lists are never unioned. The
|
|
||||||
unclassified state has no relations; orphan secondary rows are invalid. Parent
|
|
||||||
groups are derived from the manifest and never serialized as exercise
|
|
||||||
relations.
|
|
||||||
|
|
||||||
Supplied equipment remains generated from `catalog/equipment-v1.json`; its IDs
|
|
||||||
are reserved. User-created definitions synchronize additively through the
|
|
||||||
directional definitions V1 artifacts. Equal same-ID definitions are idempotent;
|
|
||||||
divergent definitions, unknown references, and association conflicts are
|
|
||||||
reported explicitly without silent overwrite.
|
|
||||||
|
|
||||||
Conflict summaries identify the stable session, body-observation, association,
|
|
||||||
or definition identity and the source artifact/direction. Existing persisted
|
|
||||||
content is preserved when a conflict is reported.
|
|
||||||
|
|
||||||
Synchronization addition counters describe durable inserts only. Exercise
|
|
||||||
rekeys or profile enrichments remain available through the separate
|
|
||||||
reconciliation counter; a compatible different-ID lookup that requires no
|
|
||||||
database write is an idempotent skip. This prevents a repeated Android
|
|
||||||
snapshot carrying a retired creator ID from being displayed as `+1 exercice`
|
|
||||||
when the canonical desktop catalog row and all business tables are unchanged.
|
|
||||||
|
|
||||||
The current Marche collision is safely reconciled to desktop identity
|
|
||||||
`ex_b1e6ffc6-75b5-45ff-a3c0-e7433c58013d`. Its catalog profile is the compatible
|
|
||||||
union `continuous/duration/data_fields=3`; prior `data_fields=1` occurrences
|
|
||||||
retain their own snapshots and absent distance values. The Android occurrence
|
|
||||||
`sxe_f25142c8-455e-4346-9bfc-31d0989e275d` retains its duration, speed, distance
|
|
||||||
and equipment. No reference to the retired Android exercise identity remains
|
|
||||||
in the validated merged database copy.
|
|
||||||
|
|
||||||
No SQLite file is copied.
|
|
||||||
|
|
||||||
No mounted Android filesystem is required.
|
|
||||||
|
|
||||||
## Session generator V1
|
|
||||||
|
|
||||||
`SESSION_GENERATOR_V1=PASS`.
|
|
||||||
The frozen separate policy is loaded identically by C and Android. It generates a
|
|
||||||
read-only, editable proposal for `full_body`, `upper_body`, `lower_body`, or
|
|
||||||
one of the eight remaining leaf zones, with four goals and policy-owned duration
|
|
||||||
bounds. It uses complete completed-history evidence, deterministic candidate
|
|
||||||
selection, explicit shortages, and an observed repeated-dose load only when the
|
|
||||||
exact exercise and compatible external equipment have a qualifying 28-day
|
|
||||||
anchor. MAX is context only and never produces a numeric target.
|
|
||||||
|
|
||||||
The Android v10 -> v11 migration adds nullable planning metadata to normal and
|
|
||||||
draft occurrences. Existing rows receive `load_mode=none`, `rest_seconds=0`,
|
|
||||||
and null targets without reconstruction. Android acceptance atomically creates
|
|
||||||
the existing normal singleton draft with no actual sets; an existing draft is a
|
|
||||||
non-mutating conflict. TUI `g` opens its preview and then the normal editor;
|
|
||||||
completion still needs actual work. Empty proposals cannot be accepted.
|
|
||||||
Nonempty partial proposals retain explicit shortages and may enter normal draft
|
|
||||||
editing. The separate active V3 mobile export preserves plan and actual fields
|
|
||||||
atomically; V1/V2 remain readable legacy formats.
|
|
||||||
|
|
||||||
The initial [independent engineering delta review](reviews/session_generator_v1_engineering_review.md)
|
|
||||||
found no findings, and the one deep final audit then found three repairable
|
|
||||||
blockers. Its bounded repairs and bounded final repair review passed. The final
|
|
||||||
matrix passed 45/45 Meson tests, named ASan/UBSan 4/4, and Android 75 tests with
|
|
||||||
zero failures/errors and one known unavailable external real-v9 fixture skip;
|
|
||||||
the structural v10 migration test executed and passed. Validators, strict C17
|
|
||||||
headers, deterministic generation and APK asset byte comparisons passed. Real
|
|
||||||
desktop preservation is baseline-equal with logical SHA-256
|
|
||||||
`5bff76850581cc3abafd583377a5de2c16d6313607a4cacb812376cfebd36cce`, ten
|
|
||||||
protected catalog/format files unchanged, and an empty index. No real app
|
|
||||||
upgrade/install or hardware MTP exercise is claimed.
|
|
||||||
|
|
||||||
## Validation checkpoint
|
|
||||||
|
|
||||||
Desktop:
|
|
||||||
|
|
||||||
```text
|
|
||||||
Historical checkpoint: 39/39 Meson tests PASS for desktop schema v11
|
|
||||||
JSON valid/invalid checks PASS
|
|
||||||
import-contract validator 6/6 PASS
|
|
||||||
Historical checkpoint: ASan/UBSan 39/39 Meson tests PASS with leak detection
|
|
||||||
standalone public-header C17 syntax PASS
|
|
||||||
real Notcurses binary zone workflows PASS in Kitty
|
|
||||||
git diff --check PASS
|
|
||||||
```
|
|
||||||
|
|
||||||
Android:
|
|
||||||
|
|
||||||
```text
|
|
||||||
Android unit tests 44/44 PASS with a real v9 copy enabled, including body-zone
|
|
||||||
v9 -> v10 migration and sync
|
|
||||||
assembleDebug PASS
|
|
||||||
APK Signature Scheme v2 verification PASS
|
|
||||||
APK certificate SHA-256 matches the local debug keystore
|
|
||||||
APK SHA-256 5355f54e83f00ef31dc0e2f1db866c43ea3e9d880875f67878b1ffe169b5dc50
|
|
||||||
```
|
|
||||||
|
|
||||||
Current real-data/environment boundary:
|
|
||||||
|
|
||||||
```text
|
|
||||||
BODY_ZONES_DESKTOP_BACKUP=PASS
|
|
||||||
BODY_ZONES_DESKTOP_REAL_V10_TO_V11=PASS
|
|
||||||
BODY_ZONES_DESKTOP_HISTORY_ROW_EQUALITY=PASS
|
|
||||||
BODY_ZONES_DESKTOP_RELATIONS=32/20_EXERCISES
|
|
||||||
BODY_ZONES_DESKTOP_UNCLASSIFIED=3
|
|
||||||
BODY_ZONES_ANDROID_INSTALL=PASS
|
|
||||||
BODY_ZONES_ANDROID_REAL_V9_COPY_TO_V10=PASS
|
|
||||||
BODY_ZONES_ANDROID_INSTALLED_DB_V10=PASS
|
|
||||||
BODY_ZONES_LIVE_MTP_ROUNDTRIP=PASS
|
|
||||||
BODY_ZONES_LIVE_MTP_SECOND_PASS_IDEMPOTENT=PASS
|
|
||||||
REAL_ANDROID_ARTIFACT_COPY_ROUNDTRIP=PASS
|
|
||||||
PC_EXPORT_ANDROID_IMPORT_THREE_PASS_COPY=PASS
|
|
||||||
ANDROID_INSTALL_R_DATABASE_HASH_PRESERVED=PASS
|
|
||||||
```
|
|
||||||
|
|
||||||
The real desktop v10 database was backed up with SQLite to
|
|
||||||
`backups/body-zones-v1-20260909T101310Z/trainlog-v10-before.db` under the Trainlog
|
|
||||||
user-data directory (SHA-256
|
|
||||||
`9d5dcacd8be941be17bd3b98bbbc1815944fec9dc1b18133111ca481f50684e9`).
|
|
||||||
The production binary migrated it to v11; integrity/FK checks pass and every
|
|
||||||
row of all eight historical tables compares equal in both directions with the
|
|
||||||
backup. The migration added 32 direct relations for 20 of 23 exercises and
|
|
||||||
left Gym échauffement, Gym/Échauffement and Marche unclassified.
|
|
||||||
|
|
||||||
The Samsung SM-G990B application data was freshly backed up before installation
|
|
||||||
under `backups/body-zones-v1-android-20260909TVFprJ8/`. The coherent v9 SQLite
|
|
||||||
copy has SHA-256
|
|
||||||
`c402b69cdbfa4912eec1553a84754af1e892fa8da0cae6c3cbd14c4f10198401`;
|
|
||||||
its integrity check passed and its foreign-key check was empty. The installed,
|
|
||||||
built and established-keystore certificate fingerprints all matched
|
|
||||||
`aa56c97f2781a0d01f007f4444c3970deb58ad8327ca3da7b0b90f37dbe2ad25`
|
|
||||||
before `adb install -r`. The normal installed migration reached v10 with every
|
|
||||||
row of the 16 existing application tables plus `android_metadata` equal in both
|
|
||||||
directions to the v9 backup. Integrity/FK checks pass, 32 direct relations cover
|
|
||||||
20 of 23 exercises, and Gym échauffement, Gym/Échauffement and Marche remain
|
|
||||||
unclassified.
|
|
||||||
|
|
||||||
The real Android UI displayed primary, secondary and derived-group metadata;
|
|
||||||
the chest, upper-body descendant, lower-body descendant and unclassified
|
|
||||||
filters; and the `Dos` + `lat` prefix intersection. Editing `Lat pull`
|
|
||||||
preloaded `Dos` and secondary `Bras`; cancellation left every application table
|
|
||||||
unchanged. Live sync runs `sy_0b00dc46-d899-4865-8718-c95085a380b2` and
|
|
||||||
`sy_fb0630a8-1938-4d62-9836-b0281955f625` both completed successfully. The
|
|
||||||
second run reported zero additions/reconciliations, both stores retained the
|
|
||||||
same 32-relation stable-ID hash, and Android application tables plus companion
|
|
||||||
exercise states compared equal to the first pass. Android now resolves the
|
|
||||||
exact request and every outbound artifact through direct filesystem access to
|
|
||||||
`Documents/Trainlog`. Historical numbered copies remain untouched. One
|
|
||||||
transaction-local exact-name snapshot is shared
|
|
||||||
by the complete export and request on the I/O dispatcher; each later sync takes
|
|
||||||
a fresh snapshot. The redundant application-start export was removed, and
|
|
||||||
request-ID replay protection remains authoritative.
|
|
||||||
|
|
||||||
`TRAINLOG_ANDROID_DIRECT_STORAGE_V1=PASS`. Android no longer uses SAF for
|
|
||||||
exchange artifacts. User-enabled all-files access gates the fixed
|
|
||||||
`/storage/emulated/0/Documents/Trainlog` directory; direct temporary-file
|
|
||||||
replacement preserves canonical names. A physical Samsung Android 16 one-sync
|
|
||||||
and its identical second run completed successfully without suffixes, and the
|
|
||||||
recovered `Dos + biceps` proposal remained visible after replay.
|
|
||||||
|
|
||||||
## Current implementation cursor
|
|
||||||
|
|
||||||
Body Zones V1, exercise reconciliation, definition-first V2 synchronization
|
|
||||||
and the current real-data importer/exporter copy validation are complete. The
|
|
||||||
body-zone taxonomy now supplies the read boundary required by a future
|
|
||||||
zone-driven planner; no session generator or proposed-load calculation is part
|
|
||||||
of this tranche.
|
|
||||||
|
|
||||||
```text
|
|
||||||
MEASURED_MAX_V1=PASS
|
|
||||||
WORKING_LOAD_PERCENTAGES=PASS
|
|
||||||
ANDROID_MAX_TEST_SESSION=PASS
|
|
||||||
EXPLICIT_MAX_RESULTS_V1=PASS
|
|
||||||
MAX_TEST_RESUME_STABLE_ID=PASS
|
|
||||||
CURRENT_OPERATIONAL_CURSOR=REAL_DATA_BASELINE_V1
|
|
||||||
NEXT_FEATURE=GYM_CATALOG_V1
|
|
||||||
```
|
|
||||||
|
|
||||||
Future work must start from this validated baseline rather than from obsolete
|
|
||||||
historical `NEXT` notes.
|
|
||||||
|
|
||||||
## Measured max v1
|
|
||||||
|
|
||||||
```text
|
|
||||||
MEASURED_MAX_V1=PASS
|
|
||||||
MEASURED_MAX_ONLY_FROM_MAX_TEST=PASS
|
|
||||||
WORKING_LOAD_PERCENTAGES=PASS
|
|
||||||
ASSISTANCE_DIRECTION_AWARE=PASS
|
|
||||||
ANDROID_MAX_TEST_SESSION=PASS
|
|
||||||
EXPLICIT_MAX_RESULTS_V1=PASS
|
|
||||||
MAX_TEST_RESUME_STABLE_ID=PASS
|
|
||||||
DESKTOP_TESTS=58/58 PASS
|
|
||||||
```
|
|
||||||
|
|
||||||
A measured maximum belongs to an exercise occurrence in an explicit `max_test`
|
|
||||||
session. Schema v9 introduced the positive `max_weight_kg` separately from sets;
|
|
||||||
equipment is optional context, so one physical machine may carry independent
|
|
||||||
Pec Fly and Rear Delt Fly results. Ordinary training is never promoted
|
|
||||||
implicitly.
|
|
||||||
|
|
||||||
The Android and TUI max forms request no set or repetition count. Android loads
|
|
||||||
a completed Test max without changing `session_id`, existing `entry_id` values,
|
|
||||||
order, exercise identity, or equipment context; finalization replaces that
|
|
||||||
session's children atomically, permits explicit value/equipment corrections,
|
|
||||||
and may append new entries.
|
|
||||||
|
|
||||||
The current measured result is the newest successful max test. The historical
|
|
||||||
record compares max tests using the same load mode.
|
|
||||||
|
|
||||||
External-load working percentages are pure calculations from the current
|
|
||||||
measured load; they are not persisted and no estimated 1RM is introduced.
|
|
||||||
|
|
||||||
`PERCENT_MAX_INPUT_V1` additionally provides a user-directed 1..100 integer
|
|
||||||
calculator in desktop planning, Android ordinary set-based manual planning, and
|
|
||||||
both generator previews. It requires the exact exercise and
|
|
||||||
external-resistance equipment identity; assistance is unavailable. It uses
|
|
||||||
`MAX × percentage / 100`, makes no recommendation, and persists only the
|
|
||||||
resulting `target_weight_kg`. Automatic generation retains the frozen V1
|
|
||||||
observed-load policy. Latest MAX means the chronologically latest explicit
|
|
||||||
result, not an aggregate record.
|
|
||||||
|
|
||||||
## Body analytics v1
|
|
||||||
|
|
||||||
```text
|
|
||||||
BODY_ANALYTICS_V1=PASS
|
|
||||||
BODY_ANALYTICS_TUI_ONLY=PASS
|
|
||||||
BODY_COMPOSITION_ESTIMATE=PASS
|
|
||||||
BODY_PROPORTION_RATIOS=PASS
|
|
||||||
BODY_SYMMETRY_ANALYTICS=PASS
|
|
||||||
NO_ESTIMATE_PERSISTENCE=PASS
|
|
||||||
DESKTOP_TESTS=58/58 PASS
|
|
||||||
```
|
|
||||||
|
|
||||||
Android remains capture-only for this feature.
|
|
||||||
|
|
||||||
The TUI derives analytics from canonical body observations. A local
|
|
||||||
desktop-only profile provides height and the circumference-formula branch
|
|
||||||
needed for the optional body-fat estimate.
|
|
||||||
|
|
||||||
No estimated body-fat, fat-mass, lean-mass, ratio, or asymmetry value is stored
|
|
||||||
as if it were a real measurement.
|
|
||||||
|
|
||||||
## Shell and naming closeout
|
|
||||||
|
|
||||||
`SESSION_GENERATOR_V1` is technically retained but hidden from normal UI pending
|
|
||||||
V2. Manual `%MAX` remains available. `exercise-names-v1.json` supplies
|
|
||||||
movement-oriented canonical metadata for mapped stable IDs; stale peer labels
|
|
||||||
cannot overwrite those names, while custom exercise naming remains editable.
|
|
||||||
`STATS_V1=IMPLEMENTED`: Android exposes a Material 3 Catppuccin Mocha/Lavender
|
|
||||||
statistics landing dashboard with selected-period fact counts, weekly
|
|
||||||
conservative working/MAX improvement-event counts, bounded measurements, weekly
|
|
||||||
session frequency, and context-owned drill-down series. The Notcurses Statistics
|
|
||||||
route loads a bounded, transactionally consistent controller snapshot outside
|
|
||||||
rendering and presents the same global facts at wide and compact widths. Working
|
|
||||||
events compare every set only within exact exercise, resolved external equipment
|
|
||||||
and performed-dose context; equal canonical instants never create an event. A
|
|
||||||
session contributes once when it owns a
|
|
||||||
performed set, continuous activity, or explicit MAX; `ended_at` does not gate
|
|
||||||
persisted history, and plans/empty occurrences contribute nothing. Exact parsed
|
|
||||||
instants, rather than SQLite text ordering, choose and order points. Its >=100-column multi-region and 72x20
|
|
||||||
compact layouts retain the same three detail actions.
|
|
||||||
Malformed legacy timestamps are skipped before sorting with a nonfatal dashboard
|
|
||||||
warning on both platforms.
|
|
||||||
No statistics schema or exchange-format field was added.
|
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ is a later, separately versioned migration.
|
||||||
|
|
||||||
```text
|
```text
|
||||||
TRAINLOG_DATABASE_SCHEMA_VERSION=18
|
TRAINLOG_DATABASE_SCHEMA_VERSION=18
|
||||||
DATABASE_SCHEMA_V18=IMPLEMENTED_VALIDATION_PENDING
|
DATABASE_SCHEMA_V18=IMPLEMENTED
|
||||||
TRAINLOG_FORMAT_V1=FROZEN
|
TRAINLOG_FORMAT_V1=FROZEN
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
552
docs/roadmap.md
552
docs/roadmap.md
|
|
@ -1,523 +1,103 @@
|
||||||
# Roadmap
|
# Roadmap
|
||||||
|
|
||||||
## MACHINE_EXERCISE_MODEL_V1 — implementation awaiting human review
|
This document owns future work. The implemented baseline is summarized in
|
||||||
|
[current_state.md](current_state.md); completed narratives and evidence belong
|
||||||
Phase 1 introduces additive schema v13, machine-facing exercise identities,
|
in [reviews](reviews/) and [CHANGELOG.md](../CHANGELOG.md).
|
||||||
exercise-owned load semantics, reusable scientific profile references, exact
|
|
||||||
manifest splits, and exercise-centric Android/TUI navigation. Real database
|
|
||||||
migration remains gated on copy validation and human review.
|
|
||||||
|
|
||||||
`EQUIPMENT_SCHEMA_CLEANUP_V1` remains a later phase after real-world
|
|
||||||
validation; it may remove obsolete legacy tables, fields, companions, and UI
|
|
||||||
only with a new explicit migration and compatibility decision.
|
|
||||||
|
|
||||||
This file is the canonical product roadmap for Trainlog.
|
|
||||||
|
|
||||||
Historical implementation detail belongs in Git history and `docs/reviews`.
|
|
||||||
The published Trainlog JSON v1 compatibility boundary remains frozen unless a
|
|
||||||
future feature explicitly introduces a new version.
|
|
||||||
|
|
||||||
## TRAINLOG_AI_SESSION_DRAFT_V1 — implementation validation pending
|
|
||||||
|
|
||||||
The implemented proposal path is deliberately separate from completed-session
|
|
||||||
exchange and the singleton capture draft. A strict single Drive source,
|
|
||||||
`TRAINLOG_AI_SESSION_DRAFT` V1, imports target-only SETS proposals into desktop
|
|
||||||
schema v18, archive-copies the exact fetched snapshot only after commit while
|
|
||||||
retaining the inbox object for logical replay, then produces the bounded
|
|
||||||
`trainlog-ai-session-drafts` V1 Android companion in deterministic batches of
|
|
||||||
at most 256 unpublished proposals. Only successful MTP publication advances
|
|
||||||
the desktop cursor; replay identities remain permanent. Android v17 retains a
|
|
||||||
pending collection, while explicit start/delete creates durable tombstones so
|
|
||||||
replay is idempotent and cannot resurrect proposals. The remaining gate is one
|
|
||||||
real Drive plus Android-triggered bidirectional synchronization smoke test;
|
|
||||||
therefore this item is `VALIDATION_PENDING` and is not PASS.
|
|
||||||
|
|
||||||
## Completed foundation
|
|
||||||
|
|
||||||
```text
|
|
||||||
GATE_0=PASS
|
|
||||||
GATE_1=PASS
|
|
||||||
GATE_2=PASS
|
|
||||||
|
|
||||||
TRAINLOG_FORMAT_V1=FROZEN
|
|
||||||
DESKTOP_SCHEMA_V11=PASS
|
|
||||||
ANDROID_LOCAL_DATABASE_V10=PASS
|
|
||||||
ANDROID_LOCAL_DATABASE_V11=PASS
|
|
||||||
SESSION_GENERATOR_V1=PASS
|
|
||||||
|
|
||||||
DIRECT_MTP_TRANSPORT=PASS
|
|
||||||
BIDIRECTIONAL_SYNC_V1=PASS
|
|
||||||
|
|
||||||
VARIABLE_REPETITION_SETS=PASS
|
|
||||||
MEASURED_MAX_V1=PASS
|
|
||||||
EXPLICIT_MAX_RESULTS_V1=PASS
|
|
||||||
MAX_TEST_RESUME_STABLE_ID=PASS
|
|
||||||
BODY_ANALYTICS_V1=PASS
|
|
||||||
EXERCISE_EDIT_V1=PASS
|
|
||||||
ANDROID_BANNER_PARITY_V1=PASS
|
|
||||||
BODY_ZONES_V1=PASS
|
|
||||||
BODY_ZONE_SYNC_V1=PASS
|
|
||||||
BODY_ZONES_ANDROID_DEVICE_VALIDATION=PASS
|
|
||||||
|
|
||||||
DESKTOP_TESTS=58/58 PASS (latest validated checkpoint)
|
|
||||||
TUI_NOTCURSES_V1=PASS
|
|
||||||
TRAINLOG_TUI_MEASUREMENTS_GRAPH_V1=PASS
|
|
||||||
TRAINLOG_TUI_STATISTICS_V1=PASS
|
|
||||||
TRAINLOG_TUI_STATISTICS_REFINEMENT_V1=PASS
|
|
||||||
TRAINLOG_TUI_STATISTICS_PERIOD_BUCKETING_V1=PASS
|
|
||||||
TRAINLOG_TUI_STATISTICS_PERIOD_BUCKET_SIZE_FIX=PASS
|
|
||||||
TRAINLOG_TUI_STATISTICS_CALENDAR_BUCKETS_V1=PASS
|
|
||||||
NCURSESW_REMOVED_FROM_ACTIVE_TUI=PASS
|
|
||||||
NOTCURSES_TRUECOLOR_THEME=PASS
|
|
||||||
```
|
|
||||||
|
|
||||||
The current product baseline includes:
|
|
||||||
|
|
||||||
- SQLite persistence and explicit migrations;
|
|
||||||
- usable Notcurses desktop TUI;
|
|
||||||
- native Android capture client;
|
|
||||||
- exercise catalog;
|
|
||||||
- canonical hierarchical body zones, primary/secondary relations and filters;
|
|
||||||
- Android `BODY_FOCUS_HOME_V1` recent-exposure map and deterministic supported-zone ranking;
|
|
||||||
- profile-aware set and continuous activity;
|
|
||||||
- heterogeneous repetition sets;
|
|
||||||
- session history and editing;
|
|
||||||
- body measurements and body history;
|
|
||||||
- measured-max sessions and measured-max analysis;
|
|
||||||
- explicit max-weight capture without synthetic sets, with stable-ID continuation;
|
|
||||||
- desktop body analytics;
|
|
||||||
- direct USB/MTP transport;
|
|
||||||
- bidirectional Android/PC synchronization;
|
|
||||||
- safe V2 exercise-identity reconciliation with richer compatible profiles;
|
|
||||||
- supplied and custom equipment definitions with occurrence-level links;
|
|
||||||
- shared synchronization engine and `trainlog-syncd`.
|
|
||||||
|
|
||||||
`BODY_ZONES_V1` is complete infrastructure for session generation: one shared
|
|
||||||
manifest, desktop v11/Android v10 relations, Android/TUI edit and display,
|
|
||||||
descendant-aware filters, unclassified history and one explicit-conflict sync
|
|
||||||
companion. The implemented generator uses this infrastructure; custom zones are
|
|
||||||
still outside this checkpoint and proposed loads remain editable plans rather
|
|
||||||
than actual work.
|
|
||||||
|
|
||||||
`TRAINING_KNOWLEDGE_V1=PASS` is read-only infrastructure. Scientific review,
|
|
||||||
independent temporal review, final engineering review, repair verification, and
|
|
||||||
final executable validation passed. The temporal contract preserves source text
|
|
||||||
and exact C/Android chronological pagination; one bounded repair chain closed
|
|
||||||
the audit's stale-documentation, Android-loader, Meson-input, and role-only C
|
|
||||||
query findings.
|
|
||||||
It provides catalog-backed scientific lookup and runtime context composition
|
|
||||||
without prescriptions, schema changes or catalog seeding. The implemented
|
|
||||||
separate session generator consumes that boundary; multi-session programming is
|
|
||||||
still not a roadmap gate. See
|
|
||||||
[Training knowledge system V1](domain/knowledge_system.md).
|
|
||||||
|
|
||||||
`EXERCISE_EDIT_V1` is a completed capture correction: Android permits
|
|
||||||
stable-ID renames, protects referenced profiles, and reconciles same-ID display
|
|
||||||
metadata without duplicates. `ANDROID_BANNER_PARITY_V1` is a presentation-only
|
|
||||||
completed checkpoint: all Android screens share the Notcurses-derived compact
|
|
||||||
header; it does not reorder the roadmap below.
|
|
||||||
|
|
||||||
`BODY_FOCUS_HOME_V1` is a completed Android Home presentation/query tranche.
|
|
||||||
It adds no schema, sync or catalog semantics. The overview uses completed
|
|
||||||
actual work only, canonical exact-instant windows and exactly eight anatomical
|
|
||||||
child zones. Its stable order is: absent primary, oldest primary, lower primary
|
|
||||||
7d then 30d, weaker lower secondary 7d then 30d, oldest secondary, then stable
|
|
||||||
zone ID. Only zones backed by a current canonical exercise with a resolved
|
|
||||||
direct relation are eligible; unsupported zones stay visible but cannot be
|
|
||||||
recommended. It is descriptive exposure guidance, not medical recovery,
|
|
||||||
fatigue/readiness analysis or a session prescription. Session Generator V1
|
|
||||||
remains hidden.
|
|
||||||
|
|
||||||
## Product boundary
|
## Product boundary
|
||||||
|
|
||||||
`ANDROID_SESSION_DRAFT_V1` is an implemented P0 capture-reliability correction:
|
|
||||||
one durable active draft, Home resume, explicit discard and atomic completion.
|
|
||||||
Host and device validation and the final tranche review pass. This repair
|
|
||||||
does not introduce planning/templates or reorder the product roadmap below.
|
|
||||||
|
|
||||||
The intended split remains:
|
|
||||||
|
|
||||||
```text
|
```text
|
||||||
ANDROID
|
Android captures and summarizes.
|
||||||
= fast capture during real use
|
The TUI analyzes and tracks over time.
|
||||||
|
Desktop SQLite is canonical long-term history.
|
||||||
DESKTOP TUI
|
|
||||||
= planning
|
|
||||||
+ catalog management
|
|
||||||
+ history
|
|
||||||
+ analytics
|
|
||||||
+ long-term decision support
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Android must not become the main analytics surface unless this boundary is
|
Future work must preserve the frozen Trainlog JSON V1 contract, stable
|
||||||
explicitly revised later.
|
identities, actual-versus-planned separation, direct MTP transport, and the
|
||||||
|
distinction between measured values and estimates.
|
||||||
|
|
||||||
## Operational cursor — real data baseline
|
## Current baseline
|
||||||
|
|
||||||
The immediate priority is not another implementation block.
|
Desktop schema v18, Android schema v17, Notcurses, direct
|
||||||
|
`Documents/Trainlog` storage, mobile export V3, Training Knowledge V1, Body
|
||||||
|
Zones V1, Training Feedback V1/V2, STATS V1, and Session Generator V1 are
|
||||||
|
implemented. Session Generator V1 is hidden pending V2. AI session-draft
|
||||||
|
exchange and APP_SHELL_V1 retain their explicit manual validation/review gates.
|
||||||
|
|
||||||
Trainlog now needs real measurements and real training sessions so later
|
## Current cursor
|
||||||
analytics are based on useful data rather than fixtures.
|
|
||||||
|
|
||||||
```text
|
```text
|
||||||
CURRENT_OPERATIONAL_CURSOR=REAL_DATA_BASELINE_V1
|
CURRENT_OPERATIONAL_CURSOR=REAL_DATA_BASELINE_V1
|
||||||
```
|
```
|
||||||
|
|
||||||
Expected baseline:
|
Collect real body observations and training sessions through Android, sync them
|
||||||
|
to desktop, and record MAX only through explicit `max_test` results. Do not seed
|
||||||
|
fictitious user history into canonical databases.
|
||||||
|
|
||||||
- first complete real body observation;
|
## Next
|
||||||
- first real training sessions;
|
|
||||||
- Android used for capture;
|
|
||||||
- Android -> PC synchronization after training;
|
|
||||||
- measured max recorded only through explicit `max_test` results;
|
|
||||||
- no fictitious user data in the canonical databases.
|
|
||||||
|
|
||||||
Gate:
|
### Gym catalog V1
|
||||||
|
|
||||||
```text
|
Build an inventory from the equipment actually available in the user's gym.
|
||||||
REAL_DATA_BASELINE_V1=PASS
|
Keep physical equipment, exercises, movement patterns, muscles, and BODY ZONES
|
||||||
```
|
distinct. One photographed machine may support several exercises. Record model
|
||||||
|
or manufacturer only when evidence identifies it.
|
||||||
|
|
||||||
## Next feature — gym catalog v1
|
### Exercise metadata V1
|
||||||
|
|
||||||
```text
|
After the gym inventory is normalized, design any remaining structured metadata
|
||||||
NEXT_FEATURE=GYM_CATALOG_V1
|
such as movement family and laterality. Reuse existing equipment and BODY ZONES
|
||||||
```
|
identities; do not encode semantics in display names or notes.
|
||||||
|
|
||||||
The catalog must be based on the equipment actually available in the user's
|
### Session Generator V2
|
||||||
gym rather than on a generic Internet exercise list.
|
|
||||||
|
|
||||||
The initial inventory will be built from a complete photo survey of the gym.
|
Replace the hidden V1 product surface with duration-aware construction that can
|
||||||
|
represent warm-up, ramp-up, main work, cool-down, transitions, setup time, and
|
||||||
|
candidate scarcity without claiming unsupported physiological precision.
|
||||||
|
|
||||||
For each useful equipment item, record where applicable:
|
### Session templates V1
|
||||||
|
|
||||||
```text
|
Offer editable conveniences built on the real catalogue and planning model.
|
||||||
gym zone
|
Templates are not rigid prescriptions and never become performed work before
|
||||||
equipment family
|
normal capture.
|
||||||
machine name
|
|
||||||
manufacturer/model if identifiable
|
|
||||||
useful duplicate count
|
|
||||||
supported exercise(s)
|
|
||||||
Trainlog recording profile
|
|
||||||
load semantics
|
|
||||||
primary body zone (BODY_ZONES_V1)
|
|
||||||
secondary body zones (BODY_ZONES_V1)
|
|
||||||
```
|
|
||||||
|
|
||||||
Important rule:
|
## Later
|
||||||
|
|
||||||
```text
|
- evolve training analytics only where sufficient real history supports the
|
||||||
one photographed machine != one exercise
|
metric, without aggregating incomparable loads;
|
||||||
```
|
- add body baseline and trend comparisons while keeping direct measurements
|
||||||
|
distinct from estimates;
|
||||||
|
- add conservative, editable progression suggestions with correct external-load
|
||||||
|
and assistance interpretation;
|
||||||
|
- support explicit user objectives without mandatory gamification;
|
||||||
|
- add user-controlled dated backup and complete/period-limited exports.
|
||||||
|
|
||||||
A single piece of equipment may support multiple exercises. Equipment and
|
## Canonical order
|
||||||
exercise identity must remain distinct concepts.
|
|
||||||
The primary/secondary body-zone dimension is already implemented by Body Zones
|
|
||||||
V1; gym inventory should link stable exercise identities to that model rather
|
|
||||||
than inventing another free-text body-region field.
|
|
||||||
|
|
||||||
Gate:
|
|
||||||
|
|
||||||
```text
|
|
||||||
GYM_CATALOG_V1=PASS
|
|
||||||
```
|
|
||||||
|
|
||||||
## Exercise metadata v1
|
|
||||||
|
|
||||||
After the real gym inventory is normalized, enrich the exercise catalog with
|
|
||||||
structured metadata needed by planning and analytics.
|
|
||||||
|
|
||||||
Remaining candidate metadata:
|
|
||||||
|
|
||||||
```text
|
|
||||||
equipment
|
|
||||||
movement_family
|
|
||||||
laterality
|
|
||||||
```
|
|
||||||
|
|
||||||
The final schema must be designed before implementation. Do not encode these
|
|
||||||
concepts into names or free-form notes as a substitute for a real model.
|
|
||||||
Body-zone metadata is no longer future scope here; its frozen V1 taxonomy and
|
|
||||||
direct relations must be reused.
|
|
||||||
|
|
||||||
Gate:
|
|
||||||
|
|
||||||
```text
|
|
||||||
EXERCISE_METADATA_V1=PASS
|
|
||||||
```
|
|
||||||
|
|
||||||
## Session generator v1
|
|
||||||
|
|
||||||
`SESSION_GENERATOR_V1=PASS`.
|
|
||||||
It provides
|
|
||||||
a bounded policy-driven generator in both UIs, then hands accepted nonempty
|
|
||||||
proposals to normal draft/editor flows where actual work is captured separately.
|
|
||||||
It does not create reusable templates, a planned-session sync product surface,
|
|
||||||
or a multi-session program. The one deep final audit found repairable gaps;
|
|
||||||
its bounded repairs, review, and final validation matrix passed.
|
|
||||||
|
|
||||||
`PERCENT_MAX_INPUT_V1` is an explicit calculator layered on editable V1 targets;
|
|
||||||
it does not change automatic selection policy. Any broader multi-session,
|
|
||||||
periodized, or program-producing work is deferred to a future
|
|
||||||
`SESSION_GENERATOR_V2` and is not present today.
|
|
||||||
|
|
||||||
## Session templates v1
|
|
||||||
|
|
||||||
Templates are built from the real exercise catalog and planning model.
|
|
||||||
|
|
||||||
Candidate examples:
|
|
||||||
|
|
||||||
```text
|
|
||||||
glutes + arms
|
|
||||||
back + shoulders
|
|
||||||
chest + arms
|
|
||||||
legs
|
|
||||||
full body
|
|
||||||
cardio
|
|
||||||
```
|
|
||||||
|
|
||||||
Templates are conveniences, not rigid training prescriptions.
|
|
||||||
|
|
||||||
Gate:
|
|
||||||
|
|
||||||
```text
|
|
||||||
SESSION_TEMPLATES_V1=PASS
|
|
||||||
```
|
|
||||||
|
|
||||||
## Training analytics v1
|
|
||||||
|
|
||||||
Implement only after enough real sessions exist to make the output meaningful.
|
|
||||||
|
|
||||||
Candidate views:
|
|
||||||
|
|
||||||
```text
|
|
||||||
7 days
|
|
||||||
30 days
|
|
||||||
90 days
|
|
||||||
since baseline
|
|
||||||
```
|
|
||||||
|
|
||||||
Candidate metrics:
|
|
||||||
|
|
||||||
- training frequency;
|
|
||||||
- sets;
|
|
||||||
- repetitions;
|
|
||||||
- duration;
|
|
||||||
- external load;
|
|
||||||
- assistance;
|
|
||||||
- measured-max progression;
|
|
||||||
- useful volume metrics where semantically valid;
|
|
||||||
- progression by exercise.
|
|
||||||
|
|
||||||
Use the existing Body Zones V1 read surface to aggregate by canonical body zone
|
|
||||||
and hierarchy when evaluating training balance. A separate movement-family
|
|
||||||
taxonomy, if ever needed, must be versioned rather than inferred from names.
|
|
||||||
|
|
||||||
Gate:
|
|
||||||
|
|
||||||
```text
|
|
||||||
TRAINING_ANALYTICS_V1=PASS
|
|
||||||
```
|
|
||||||
|
|
||||||
## Body analytics v2
|
|
||||||
|
|
||||||
Implement after multiple real body observations exist.
|
|
||||||
|
|
||||||
Possible additions:
|
|
||||||
|
|
||||||
- baseline comparison;
|
|
||||||
- previous-observation comparison;
|
|
||||||
- 7/30/90-day trends;
|
|
||||||
- moving-average weight trend;
|
|
||||||
- waist trend;
|
|
||||||
- anthropometric estimate trend;
|
|
||||||
- left/right asymmetry trend;
|
|
||||||
- obvious measurement-outlier warnings.
|
|
||||||
|
|
||||||
Permanent semantic rule:
|
|
||||||
|
|
||||||
```text
|
|
||||||
DIRECT_MEASUREMENT != ESTIMATE
|
|
||||||
```
|
|
||||||
|
|
||||||
Derived body-composition values remain explicitly labelled estimates.
|
|
||||||
|
|
||||||
Gate:
|
|
||||||
|
|
||||||
```text
|
|
||||||
BODY_ANALYTICS_V2=PASS
|
|
||||||
```
|
|
||||||
|
|
||||||
## Progression assist v1
|
|
||||||
|
|
||||||
Use accumulated history to produce conservative training suggestions.
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
|
|
||||||
```text
|
|
||||||
external load:
|
|
||||||
stable completed work
|
|
||||||
-> suggest a small load increase
|
|
||||||
|
|
||||||
assistance:
|
|
||||||
stable completed work
|
|
||||||
-> suggest a small assistance decrease
|
|
||||||
```
|
|
||||||
|
|
||||||
The system proposes. It does not silently change planned training.
|
|
||||||
|
|
||||||
Measured max and estimated performance must remain distinct concepts.
|
|
||||||
|
|
||||||
Gate:
|
|
||||||
|
|
||||||
```text
|
|
||||||
PROGRESSION_ASSIST_V1=PASS
|
|
||||||
```
|
|
||||||
|
|
||||||
## Objectives v1
|
|
||||||
|
|
||||||
Support explicit user-defined objectives such as:
|
|
||||||
|
|
||||||
```text
|
|
||||||
body weight
|
|
||||||
waist circumference
|
|
||||||
plank duration
|
|
||||||
exercise load
|
|
||||||
repetitions
|
|
||||||
measured max
|
|
||||||
weekly training frequency
|
|
||||||
```
|
|
||||||
|
|
||||||
Display current value, target, remaining difference, and trend where the data
|
|
||||||
supports it.
|
|
||||||
|
|
||||||
Avoid mandatory gamification.
|
|
||||||
|
|
||||||
Gate:
|
|
||||||
|
|
||||||
```text
|
|
||||||
OBJECTIVES_V1=PASS
|
|
||||||
```
|
|
||||||
|
|
||||||
## Backup and export v1
|
|
||||||
|
|
||||||
SQLite remains canonical.
|
|
||||||
|
|
||||||
Provide durable user-controlled export and backup:
|
|
||||||
|
|
||||||
```text
|
|
||||||
SQLite
|
|
||||||
-> dated backup
|
|
||||||
-> complete JSON export
|
|
||||||
-> CSV export where useful
|
|
||||||
-> period-limited export
|
|
||||||
```
|
|
||||||
|
|
||||||
Gate:
|
|
||||||
|
|
||||||
```text
|
|
||||||
BACKUP_EXPORT_V1=PASS
|
|
||||||
```
|
|
||||||
|
|
||||||
## Canonical implementation order
|
|
||||||
|
|
||||||
```text
|
```text
|
||||||
REAL_DATA_BASELINE_V1
|
REAL_DATA_BASELINE_V1
|
||||||
|
|
-> GYM_CATALOG_V1
|
||||||
v
|
-> EXERCISE_METADATA_V1
|
||||||
GYM_CATALOG_V1
|
-> SESSION_GENERATOR_V2
|
||||||
|
|
-> SESSION_TEMPLATES_V1
|
||||||
v
|
-> ANALYTICS_EVOLUTION
|
||||||
EXERCISE_METADATA_V1
|
-> BODY_ANALYTICS_V2
|
||||||
|
|
-> PROGRESSION_ASSIST_V1
|
||||||
v
|
-> OBJECTIVES_V1
|
||||||
SESSION_PLANNER_V1
|
-> BACKUP_EXPORT_V1
|
||||||
|
|
|
||||||
v
|
|
||||||
SESSION_TEMPLATES_V1
|
|
||||||
|
|
|
||||||
v
|
|
||||||
TRAINING_ANALYTICS_V1
|
|
||||||
|
|
|
||||||
v
|
|
||||||
BODY_ANALYTICS_V2
|
|
||||||
|
|
|
||||||
v
|
|
||||||
PROGRESSION_ASSIST_V1
|
|
||||||
|
|
|
||||||
v
|
|
||||||
OBJECTIVES_V1
|
|
||||||
|
|
|
||||||
v
|
|
||||||
BACKUP_EXPORT_V1
|
|
||||||
```
|
```
|
||||||
|
|
||||||
This order is canonical until explicitly revised.
|
## Outstanding validation gates
|
||||||
|
|
||||||
## APP_SHELL_V1 visual-review boundary
|
- `APP_SHELL_V1=IMPLEMENTED_AWAITING_VISUAL_REVIEW_2`: complete the recorded
|
||||||
|
human visual/accessibility matrix without redefining product semantics.
|
||||||
`APP_SHELL_V1=IMPLEMENTED_AWAITING_VISUAL_REVIEW_2`. The implemented shell does
|
- `TRAINLOG_AI_SESSION_DRAFT_V1=VALIDATION_PENDING`: complete one real Drive
|
||||||
not open STATS_V1, EXERCISE_NAMING_V1, a schema migration, or a new
|
plus Android-triggered bidirectional synchronization smoke test.
|
||||||
synchronization feature. The remaining checkpoint is a human visual and
|
|
||||||
accessibility review of the existing seven-root shell: TUI at 72x20, 80x24,
|
|
||||||
100x25, 100x30, 120x31 and 120x35; Android at 320, 360, 393 and 412 dp, with
|
|
||||||
large system font, IME form interaction, durable-draft leave guards and
|
|
||||||
TalkBack. The `_2` suffix records the completed automated repair review only;
|
|
||||||
its human-review outcome is not recorded here in advance.
|
|
||||||
|
|
||||||
## Permanent constraints
|
## Permanent constraints
|
||||||
|
|
||||||
Do not regress to:
|
Do not regress to SQLite-file synchronization, a mandatory mounted Android
|
||||||
|
filesystem, exercise-name identity heuristics, fake sets for continuous work,
|
||||||
```text
|
synthetic uniform actuals, ordinary training promoted to measured MAX,
|
||||||
SQLite file synchronization
|
assistance interpreted as external load, or estimates presented as direct
|
||||||
mandatory mounted Android filesystem
|
measurements. Incompatible exchange semantics require a new version.
|
||||||
exercise-name identity heuristics
|
|
||||||
fake performed sets for continuous activity
|
|
||||||
fake uniform targets for heterogeneous actual sets
|
|
||||||
ordinary training promoted to measured max
|
|
||||||
assistance interpreted as external load
|
|
||||||
body-composition estimates presented as direct measurements
|
|
||||||
incompatible changes to Trainlog JSON v1 without a new format version
|
|
||||||
```
|
|
||||||
|
|
||||||
## Session generator V2
|
|
||||||
|
|
||||||
V1 remains technically implemented but hidden from normal UI. V2 must construct
|
|
||||||
the requested duration with warm-up, appropriate ramp-up, coherent main work,
|
|
||||||
cool-down, transition/setup time, and explicit candidate-scarcity handling.
|
|
||||||
|
|
||||||
## STATS_V1
|
|
||||||
|
|
||||||
Implemented as a derived-query tranche with no summary schema: the Android
|
|
||||||
landing dashboard provides 7/30/90-day, year and all-history filters;
|
|
||||||
the performance landing graph counts weekly improvements from every performed
|
|
||||||
set under strict canonical exercise/nonempty resolved external-equipment/
|
|
||||||
performed-dose comparison, with explicit MAX classified separately. Only a
|
|
||||||
strictly later canonical instant can improve; equal-instant IDs order display
|
|
||||||
only. Raw values remain drill-down data and are never globally aggregated
|
|
||||||
as kilograms. Body charts show only actually recorded measures; weekly frequency
|
|
||||||
uses MAX as a marker on the once-counted session. The Notcurses
|
|
||||||
landing route gives the same visual dashboard hierarchy and routes to the
|
|
||||||
existing detailed persisted views. Its controller-owned snapshot is loaded
|
|
||||||
outside rendering; wide terminals show performance, body and frequency regions
|
|
||||||
while 80x24 and 72x20 retain all three factual summaries. The selected-period
|
|
||||||
summary reports sessions, performed sets, distinct actual exercises and explicit
|
|
||||||
MAX records; body selects the latest actually observed metric, and weekly frequency counts each stable session
|
|
||||||
ID exactly once when it owns at least one persisted performed set, continuous
|
|
||||||
activity, or explicit MAX result. Planned targets and empty occurrences do not
|
|
||||||
count; `ended_at` is presentation/lifecycle metadata, not the history gate.
|
|
||||||
Exact parsed instants govern boundaries and ordering. Assistance, missing values
|
|
||||||
and incomparable equipment contexts are never coerced into progress values.
|
|
||||||
Malformed legacy timestamps are skipped by the projection with a visible warning
|
|
||||||
while valid statistics remain available.
|
|
||||||
Future equipment identification may collect manufacturer, model, name, photo,
|
|
||||||
pictogram, and notes to form a proposal which the user must verify before
|
|
||||||
persistence. OCR/image recognition and a custom-equipment relation schema are
|
|
||||||
not part of EQUIPMENT_KNOWLEDGE_V2.
|
|
||||||
## TRAINING_FEEDBACK_V1
|
|
||||||
|
|
||||||
Implemented for human review: schema v14, Android voice/manual append-only
|
|
||||||
capture and readback, desktop read-only consultation, and bidirectional strict
|
|
||||||
companion convergence. Automatic interpretation and recommendations remain
|
|
||||||
future scope.
|
|
||||||
|
|
|
||||||
|
|
@ -120,8 +120,9 @@ tui_workflows
|
||||||
app_shell
|
app_shell
|
||||||
```
|
```
|
||||||
|
|
||||||
The current desktop suite, including APP_SHELL_V1 production-transition
|
The desktop suite includes APP_SHELL_V1 production-transition coverage and the
|
||||||
coverage and the AI-draft exchange regressions, is 56/56. The following generator-specific checkpoint counts remain
|
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:
|
historical evidence:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
|
|
@ -234,6 +235,28 @@ Notable regression coverage:
|
||||||
|
|
||||||
## 5. Build
|
## 5. Build
|
||||||
|
|
||||||
|
Stable-release validation additionally builds the signed release variant and
|
||||||
|
checks the packaged product versions and binary linkage:
|
||||||
|
|
||||||
|
```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
|
||||||
|
|
||||||
|
file trainlog-tui-linux-x86_64-v0.1.0
|
||||||
|
ldd trainlog-tui-linux-x86_64-v0.1.0
|
||||||
|
sha256sum trainlog-android-v0.1.0.apk \
|
||||||
|
trainlog-tui-linux-x86_64-v0.1.0
|
||||||
|
```
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
meson setup --reconfigure build
|
meson setup --reconfigure build
|
||||||
meson compile -C build
|
meson compile -C build
|
||||||
|
|
@ -533,7 +556,7 @@ Coverage proves:
|
||||||
and footer F6/F7 single-occurrence output at 120x35, 100x30, 80x24, and
|
and footer F6/F7 single-occurrence output at 120x35, 100x30, 80x24, and
|
||||||
72x20.
|
72x20.
|
||||||
|
|
||||||
Historical validation checkpoint (the current desktop suite is 56/56):
|
Historical validation checkpoint (not the current suite size):
|
||||||
|
|
||||||
```text
|
```text
|
||||||
39/39 Meson tests PASS
|
39/39 Meson tests PASS
|
||||||
|
|
|
||||||
|
|
@ -548,7 +548,7 @@ meson test -C build --print-errorlogs
|
||||||
Validated current normal suite:
|
Validated current normal suite:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
39/39 Meson tests PASS
|
39/39 Meson tests PASS (historical checkpoint)
|
||||||
```
|
```
|
||||||
|
|
||||||
## 16. Measured max view
|
## 16. Measured max view
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,7 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
"""Trainlog export pc catalog helper.
|
||||||
|
|
||||||
|
Owns this command-line workflow while preserving the versioned database and exchange contracts it consumes."""
|
||||||
import argparse
|
import argparse
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,7 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
"""Trainlog import mobile export helper.
|
||||||
|
|
||||||
|
Owns this command-line workflow while preserving the versioned database and exchange contracts it consumes."""
|
||||||
import argparse
|
import argparse
|
||||||
import json
|
import json
|
||||||
import math
|
import math
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
|
# Trainlog install syncd user helper.
|
||||||
|
# Owns this command-line workflow while preserving the versioned database and exchange contracts it consumes.
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
ROOT="$(
|
ROOT="$(
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Trainlog app shell interface.
|
||||||
|
*
|
||||||
|
* Declares the module boundary and ownership contract; implementation and persistence remain in their owning modules.
|
||||||
|
*/
|
||||||
#ifndef TRAINLOG_APP_SHELL_H
|
#ifndef TRAINLOG_APP_SHELL_H
|
||||||
#define TRAINLOG_APP_SHELL_H
|
#define TRAINLOG_APP_SHELL_H
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Trainlog body analytics interface.
|
||||||
|
*
|
||||||
|
* Declares the module boundary and ownership contract; implementation and persistence remain in their owning modules.
|
||||||
|
*/
|
||||||
#ifndef TRAINLOG_BODY_ANALYTICS_H
|
#ifndef TRAINLOG_BODY_ANALYTICS_H
|
||||||
#define TRAINLOG_BODY_ANALYTICS_H
|
#define TRAINLOG_BODY_ANALYTICS_H
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Trainlog body zone catalog interface.
|
||||||
|
*
|
||||||
|
* Declares the module boundary and ownership contract; implementation and persistence remain in their owning modules.
|
||||||
|
*/
|
||||||
#ifndef TRAINLOG_BODY_ZONE_CATALOG_H
|
#ifndef TRAINLOG_BODY_ZONE_CATALOG_H
|
||||||
#define TRAINLOG_BODY_ZONE_CATALOG_H
|
#define TRAINLOG_BODY_ZONE_CATALOG_H
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Trainlog bodyviz interface.
|
||||||
|
*
|
||||||
|
* Declares the module boundary and ownership contract; implementation and persistence remain in their owning modules.
|
||||||
|
*/
|
||||||
#ifndef TRAINLOG_BODYVIZ_H
|
#ifndef TRAINLOG_BODYVIZ_H
|
||||||
#define TRAINLOG_BODYVIZ_H
|
#define TRAINLOG_BODYVIZ_H
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Trainlog catalog interface.
|
||||||
|
*
|
||||||
|
* Declares the module boundary and ownership contract; implementation and persistence remain in their owning modules.
|
||||||
|
*/
|
||||||
#ifndef TRAINLOG_CATALOG_H
|
#ifndef TRAINLOG_CATALOG_H
|
||||||
#define TRAINLOG_CATALOG_H
|
#define TRAINLOG_CATALOG_H
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* Trainlog chart interface.
|
||||||
|
*
|
||||||
|
* Declares the module boundary and ownership contract; implementation and persistence remain in their owning modules.
|
||||||
|
*/
|
||||||
#ifndef TRAINLOG_CHART_H
|
#ifndef TRAINLOG_CHART_H
|
||||||
#define TRAINLOG_CHART_H
|
#define TRAINLOG_CHART_H
|
||||||
|
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue