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
|
||||
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.
|
||||
Every meaningful draft/form mutation is persisted by the repository. Back,
|
||||
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
|
||||
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.
|
||||
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
|
||||
`trainlog-mobile-export` V3; V1/V2 remain readable and `TRAINLOG_FORMAT_V1`
|
||||
remains unchanged.
|
||||
|
|
@ -230,9 +232,11 @@ Desktop SQLite schema is versioned with:
|
|||
PRAGMA user_version;
|
||||
```
|
||||
|
||||
The current desktop schema is v12. Schema v9 added an occurrence-owned explicit
|
||||
maximum result; v10/v11 added body-zone and planning metadata, and v12 adds
|
||||
durable flattened exercise aliases. None changes `TRAINLOG_FORMAT_V1`.
|
||||
The current desktop schema is v18. Schema v9 added an occurrence-owned explicit
|
||||
maximum result; v10/v11 added body-zone and planning metadata; v12 added durable
|
||||
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
|
||||
regression coverage.
|
||||
|
|
@ -276,6 +280,11 @@ Canonical documents:
|
|||
- `docs/roadmap.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
|
||||
documents describe the current state rather than accumulating obsolete
|
||||
`NEXT` sections.
|
||||
|
|
@ -302,6 +311,10 @@ cd android
|
|||
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.
|
||||
|
||||
Hardware-dependent MTP tests remain explicit manual validations and are not
|
||||
|
|
|
|||
13
CHANGELOG.md
13
CHANGELOG.md
|
|
@ -1,5 +1,18 @@
|
|||
# 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
|
||||
PC-published body-zone companion immediately after catalog, flattened aliases
|
||||
and strict profile-state, before fallible downstream session, equipment, AI
|
||||
|
|
|
|||
572
README.md
572
README.md
|
|
@ -1,405 +1,249 @@
|
|||
# Trainlog
|
||||
|
||||
Trainlog is a local-first workout and body-tracking system with two user
|
||||
interfaces:
|
||||
Trainlog is a local-first workout and body-data system. Its native Android app
|
||||
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;
|
||||
- a C17/Notcurses TUI used for durable history, editing, visualization,
|
||||
statistics, and synchronization.
|
||||
Android and desktop each own a local SQLite database. The desktop database is
|
||||
the canonical long-term history. Trainlog synchronizes versioned JSON artifacts
|
||||
over direct MTP; it never copies SQLite database files between devices.
|
||||
|
||||
The desktop SQLite database is the canonical long-term history. Android keeps
|
||||
its own local SQLite database so recording remains usable independently from the
|
||||
desktop.
|
||||
## Product split
|
||||
|
||||
## 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
|
||||
TRAINLOG_FORMAT_V1=FROZEN
|
||||
TRAINLOG_AI_SESSION_DRAFT_V1=VALIDATION_PENDING
|
||||
In short: **Android captures and summarizes. The TUI analyzes and tracks over
|
||||
time.** Android intentionally remains lightweight for analytics.
|
||||
|
||||
DESKTOP_SCHEMA_V11=PASS
|
||||
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
|
||||
## Release links
|
||||
|
||||
VARIABLE_REPETITION_SETS=PASS
|
||||
CONTINUOUS_ACTIVITY_TRACKING=PASS
|
||||
- [Download the latest release](https://github.com/labfytools/trainlog/releases/latest)
|
||||
- [Documentation](docs/README.md)
|
||||
- [Changelog](CHANGELOG.md)
|
||||
|
||||
DIRECT_MTP_TRANSPORT=PASS
|
||||
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
|
||||
## Installation
|
||||
|
||||
DESKTOP_TESTS=58/58 PASS (latest validated checkpoint)
|
||||
ANDROID_BUILD=PASS
|
||||
### Download a prebuilt release
|
||||
|
||||
TRAINING_KNOWLEDGE_V1=PASS
|
||||
SESSION_GENERATOR_V1=PASS
|
||||
APP_SHELL_V1=IMPLEMENTED_AWAITING_VISUAL_REVIEW_2
|
||||
```
|
||||
Stable releases publish an Android APK, an x86-64 Linux TUI executable, and a
|
||||
`SHA256SUMS` file on [GitHub Releases](https://github.com/labfytools/trainlog/releases).
|
||||
Verify the downloaded binary against `SHA256SUMS` before installing it.
|
||||
|
||||
`APP_SHELL_V1` provides the shared seven-section application shell on both
|
||||
platforms: **Accueil**, **Séances**, **Exercices**,
|
||||
**Statistiques**, **Synchronisation**, and **Paramètres**. Completed history
|
||||
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
|
||||
The Linux TUI artifact is architecture-specific and dynamically linked. It is
|
||||
not a universally portable Linux binary: the host must provide compatible
|
||||
runtime libraries listed under [Dependencies](#dependencies).
|
||||
|
||||
```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 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
|
||||
|
||||
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:
|
||||
Android:
|
||||
|
||||
```bash
|
||||
cd android
|
||||
|
||||
printf 'sdk.dir=%s\n' "$HOME/Android/Sdk" > local.properties
|
||||
|
||||
JAVA_HOME=/usr/lib/jvm/java-17-openjdk \
|
||||
./gradlew testDebugUnitTest assembleDebug
|
||||
JAVA_HOME=/usr/lib/jvm/java-17-openjdk ./gradlew test
|
||||
JAVA_HOME=/usr/lib/jvm/java-17-openjdk ./gradlew assembleRelease
|
||||
```
|
||||
|
||||
## 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
|
||||
cd /home/fy59/Documents/trainlog
|
||||
./build/tui/trainlog
|
||||
```
|
||||
### Linux runtime dependencies
|
||||
|
||||
The desktop database is `$XDG_DATA_HOME/trainlog/trainlog.db`, or
|
||||
`~/.local/share/trainlog/trainlog.db` when `XDG_DATA_HOME` is unset.
|
||||
The published x86-64 TUI is dynamically linked. The current build directly
|
||||
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
|
||||
bash tools/install_syncd_user.sh
|
||||
```
|
||||
Distribution packages may pull additional transitive libraries, including
|
||||
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
|
||||
systemctl --user is-active trainlog-syncd.service
|
||||
tail -f ~/.local/state/trainlog/syncd.log
|
||||
```
|
||||
- Android is optional when using the desktop TUI alone. USB/MTP synchronization
|
||||
requires the Android companion, libudev, libmtp, and an unlocked connected
|
||||
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
|
||||
Sync
|
||||
-> Synchroniser maintenant
|
||||
```
|
||||
Desktop builds require:
|
||||
|
||||
The request is consumed by `trainlog-syncd`, the shared bidirectional engine
|
||||
runs, a receipt is returned to Android, and the PC catalog is applied locally.
|
||||
The active completed-session exchange is V3 and preserves occurrence
|
||||
`entry_id`, per-set weights and equipment associations. Frozen V1 artifacts
|
||||
remain readable as legacy artifacts; they are not silently redefined as V2.
|
||||
Exercise-zone metadata travels separately in the sole bidirectional
|
||||
`trainlog-exercise-body-zones-v1.json` companion.
|
||||
- Meson and Ninja;
|
||||
- a C17 compiler toolchain;
|
||||
- `pkg-config`;
|
||||
- Notcurses Core development headers;
|
||||
- SQLite, libuuid, utf8proc, libudev, and libmtp development headers;
|
||||
- Python 3 for generated sources, validators, import/export helpers, and tests.
|
||||
|
||||
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
|
||||
|
||||
- `docs/current_state.md`: compact canonical implementation snapshot;
|
||||
- `docs/architecture.md`: component and ownership boundaries;
|
||||
- `docs/exercise_data_model.md`: exercise semantics;
|
||||
- `docs/database.md`: desktop SQLite schema and migrations;
|
||||
- `docs/android.md`: Android behavior;
|
||||
- `docs/tui.md`: desktop TUI behavior;
|
||||
- `docs/sync_exchange.md`: MTP synchronization artifacts and protocol;
|
||||
- `docs/exchange_format.md`: frozen Trainlog JSON v1 contract;
|
||||
- `docs/tests.md`: validation strategy;
|
||||
- `docs/roadmap.md`: completed gates and future cursor;
|
||||
- `docs/domain/knowledge_system.md`: read-only scientific knowledge system,
|
||||
runtime-context boundary and documented future planning pipeline;
|
||||
- `AGENTS.md`: development contract.
|
||||
| Topic | Document | Purpose |
|
||||
|---|---|---|
|
||||
| Documentation map | [docs/README.md](docs/README.md) | Canonical index and ownership map. |
|
||||
| Current state | [docs/current_state.md](docs/current_state.md) | Current implemented schemas, capabilities, validation, and limitations. |
|
||||
| Architecture | [docs/architecture.md](docs/architecture.md) | Component, storage, process, and ownership boundaries. |
|
||||
| Android | [docs/android.md](docs/android.md) | Field-companion behavior and local persistence. |
|
||||
| Desktop TUI | [docs/tui.md](docs/tui.md) | Notcurses workflows, correction, and analytics. |
|
||||
| Database | [docs/database.md](docs/database.md) | Desktop persistence schema and migration contracts. |
|
||||
| Exercise model | [docs/exercise_data_model.md](docs/exercise_data_model.md) | Exercise, occurrence, load, and MAX semantics. |
|
||||
| Synchronization | [docs/sync_exchange.md](docs/sync_exchange.md) | Active MTP flow and companion artifacts. |
|
||||
| Exchange formats | [docs/exchange_format.md](docs/exchange_format.md) | Frozen and separately versioned JSON contracts. |
|
||||
| Training feedback | [docs/training_feedback.md](docs/training_feedback.md) | Immediate feedback, revisions, and J+1 follow-ups. |
|
||||
| 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
|
||||
|
||||
- local-first;
|
||||
- no mandatory cloud account;
|
||||
- user-owned data;
|
||||
- versioned persistent and exchange formats;
|
||||
- stable identities;
|
||||
- idempotent synchronization;
|
||||
- no fake data representation to force incompatible models together;
|
||||
- strict compiler warnings;
|
||||
- documentation and tests are part of feature completion.
|
||||
- Preserve frozen, versioned compatibility boundaries.
|
||||
- Use stable IDs; display names are not identities.
|
||||
- Keep performed work separate from plans and continuous activity separate
|
||||
from fake sets.
|
||||
- Keep Android capture, desktop analytics, persistence, transport, and
|
||||
rendering responsibilities distinct.
|
||||
- Prefer explicit failure to silent data loss or corruption.
|
||||
- Treat migrations, idempotence, tests, and documentation as part of feature
|
||||
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
|
||||
stores each new weight result in a one-to-one `max_results` row; it no longer
|
||||
encodes a maximum as a synthetic `1 × 1` set.
|
||||
|
||||
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.
|
||||
See [LICENSE](LICENSE). Forgejo is the primary repository; GitHub is a mirror.
|
||||
Repository layout, detailed contracts, and historical review records are linked
|
||||
from [docs/README.md](docs/README.md).
|
||||
|
|
|
|||
|
|
@ -11,8 +11,10 @@ ANDROID_INLINE_EXERCISE_ROUTE=IMPLEMENTED
|
|||
ANDROID_EXERCISE_SHELL=IMPLEMENTED
|
||||
ANDROID_BODY_SHELL=IMPLEMENTED
|
||||
|
||||
LOCAL_PERSISTENCE=NEXT
|
||||
MTP_SYNC=AFTER_LOCAL_WORKFLOW
|
||||
LOCAL_PERSISTENCE=IMPLEMENTED
|
||||
DIRECT_STORAGE=IMPLEMENTED
|
||||
MTP_SYNC=IMPLEMENTED
|
||||
ANDROID_SCHEMA=17
|
||||
```
|
||||
|
||||
## Visual contract
|
||||
|
|
@ -68,10 +70,12 @@ minSdk 26
|
|||
JDK 17
|
||||
```
|
||||
|
||||
A Gradle wrapper is intentionally not committed by the scaffold script unless
|
||||
it can be generated locally. From this directory:
|
||||
The committed Gradle wrapper is the canonical entry point. From this directory:
|
||||
|
||||
```bash
|
||||
gradle wrapper --gradle-version 9.6.0
|
||||
./gradlew assembleDebug
|
||||
JAVA_HOME=/usr/lib/jvm/java-17-openjdk ./gradlew test
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
/** 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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
import android.content.ContentValues
|
||||
|
|
@ -62,6 +67,13 @@ internal fun exerciseProfileRevision(parent: String, recording: String, tracking
|
|||
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(
|
||||
db: SQLiteDatabase, rowId: Long, recording: String, tracking: String, fields: Int,
|
||||
): 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_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 val appContext: Context,
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
/** 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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
/* 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
|
||||
|
||||
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
|
||||
|
||||
/* 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
|
||||
|
||||
/* 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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
import org.junit.Assert.assertEquals
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
Canonical snapshot: 2026-09-10.
|
||||
Snapshot date: **2026-09-15**.
|
||||
|
||||
This document is the compact source of truth for the implemented Trainlog
|
||||
baseline. Detailed behavior belongs in the topic-specific documents.
|
||||
This document owns the current implemented state. Historical checkpoints and
|
||||
closed incidents belong in [reviews](reviews/) and the
|
||||
[changelog](../CHANGELOG.md).
|
||||
|
||||
## Status
|
||||
## Product boundary
|
||||
|
||||
```text
|
||||
GATE_0_PROJECT_CONTRACT=PASS
|
||||
GATE_1_TRAINLOG_FORMAT_V1=PASS/FROZEN
|
||||
GATE_2_PERSISTENCE_AND_USABLE_TUI=PASS
|
||||
Android is the field companion: it captures training and body data, preserves
|
||||
the active draft, receives AI proposals, triggers synchronization, and shows
|
||||
quick summaries. The C17/Notcurses TUI is the detailed consultation,
|
||||
correction, catalogue, analytics, graphing, and long-term tracking surface.
|
||||
|
||||
TRAINLOG_FORMAT_V1=FROZEN
|
||||
TRAINLOG_AI_EXPORT_V1=PASS
|
||||
TRAINLOG_AI_GDRIVE_SYNC_V1=PASS
|
||||
TRAINLOG_AI_SESSION_DRAFT_V1=VALIDATION_PENDING
|
||||
TRAINLOG_ANDROID_BODY_ZONE_REPOPULATION_FIX=PASS
|
||||
Android captures and summarizes. The TUI analyzes and tracks over time. The
|
||||
desktop SQLite database remains the canonical long-term history.
|
||||
|
||||
DESKTOP_SCHEMA_V11=PASS
|
||||
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
|
||||
## Versions and compatibility
|
||||
|
||||
PROFILE_AWARE_EXERCISES=PASS
|
||||
CONTINUOUS_ACTIVITY=PASS
|
||||
VARIABLE_REPETITION_SETS=PASS
|
||||
| Boundary | Current state |
|
||||
|---|---|
|
||||
| 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
|
||||
ANDROID_TO_PC_IMPORT=PASS
|
||||
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
|
||||
Desktop and Android schema numbers are independent. Neither changes the frozen
|
||||
Trainlog JSON V1 contract.
|
||||
|
||||
TRAINING_KNOWLEDGE_V1=PASS
|
||||
SESSION_GENERATOR_V1=PASS
|
||||
APP_SHELL_V1=IMPLEMENTED_AWAITING_VISUAL_REVIEW_2
|
||||
## Storage and synchronization
|
||||
|
||||
DESKTOP_TESTS=52/52 PASS (normal); affected ASan/UBSan tests=6/6 PASS
|
||||
ANDROID_BUILD=PASS
|
||||
TRAINLOG_ANDROID_UI_REDESIGN_V1=PASS
|
||||
HARDWARE_SYNC_VALIDATION=HISTORICAL_PASS
|
||||
```
|
||||
|
||||
## Desktop
|
||||
|
||||
Implemented:
|
||||
|
||||
- C17/Notcurses true-color TUI with the APP_SHELL_V1 persistent seven-section
|
||||
shell (72x20 minimum, UTF-8 bounded editors, resize fallback, F6 navigation,
|
||||
F7 actions, and restored focus after overlays/routes);
|
||||
- UTF-8 cell-aware scrolling training-knowledge screen, tested at the 72x20
|
||||
minimum terminal;
|
||||
- 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 stores capture data in its private SQLite database.
|
||||
- Android reads and writes exchange artifacts only in
|
||||
`/storage/emulated/0/Documents/Trainlog` after the user grants all-files
|
||||
access through system settings.
|
||||
- The old SAF/tree-URI path and `Download/Trainlog` are historical recovery
|
||||
boundaries, not active storage choices.
|
||||
- Desktop synchronization uses direct libudev/libmtp discovery and object
|
||||
access. No GVFS/FUSE mount is required.
|
||||
- `trainlog_sync_run()` is shared by the TUI and `trainlog-syncd`.
|
||||
- The desktop imports Android snapshots, publishes catalog/profile/feedback/AI
|
||||
companions, and returns a receipt. Stable IDs and tombstones make defined
|
||||
replay paths idempotent.
|
||||
- SQLite database files are never synchronized.
|
||||
- The desktop AI flow uses external `rclone` for Drive inbox/archive and
|
||||
read-only history export. Android owns no Drive credentials.
|
||||
|
||||
## Android
|
||||
|
||||
Implemented:
|
||||
Android currently provides:
|
||||
|
||||
- native Kotlin/Compose application;
|
||||
- local SQLite database v17, with non-destructive migrations through v17;
|
||||
- one durable active-session draft, Home resume and raw-form restoration;
|
||||
- `BODY_FOCUS_HOME_V1`: a compact accessible Home front/back BODY ZONES map,
|
||||
textual top-three exposure ranking and Catalogue/Statistics drill-through;
|
||||
- explicit confirmed discard and atomic completed-save/draft-clear;
|
||||
- schema v15 immutable feedback wording revisions, Android correction UX,
|
||||
read-only TUI display, and Training Feedback V2 synchronization;
|
||||
- exercise creation;
|
||||
- stable-ID exercise rename/editing with referenced-profile protection;
|
||||
- primary/secondary body-zone selection, display, hierarchy filtering and
|
||||
prefix-search composition, including visible unclassified exercises;
|
||||
- inline exercise creation during session entry;
|
||||
- profile-aware session recording;
|
||||
- heterogeneous repetition-set entry;
|
||||
- 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.
|
||||
- metadata-driven SETS+REPS, SETS+DURATION, and CONTINUOUS+DURATION capture;
|
||||
- actual per-set values, optional occurrence equipment, and explicit MAX rows;
|
||||
- exactly one durable active session draft with resume, confirmed discard, and
|
||||
atomic completion;
|
||||
- completed-session consultation and correction;
|
||||
- exercise catalogue editing with stable `exercise_id` and referenced-profile
|
||||
protection;
|
||||
- supplied/custom equipment and machine-exercise metadata;
|
||||
- body measurements and recent BODY ZONES summaries;
|
||||
- immediate exercise feedback, immutable wording revisions, and J+1/session
|
||||
follow-ups;
|
||||
- pending AI proposal collection, explicit start/delete, and durable tombstones;
|
||||
- 7/30/90-day, year, and all-history quick statistics;
|
||||
- direct-storage snapshot publication, sync triggering, receipt display, and
|
||||
companion application.
|
||||
|
||||
The Home BODY ZONES overview is exposure-only and read-only. It uses only
|
||||
actual performed sets, continuous activities and explicit MAX results from
|
||||
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.
|
||||
Android does not own canonical long-term analytics. The V1 session generator is
|
||||
implemented but intentionally hidden from normal navigation pending V2.
|
||||
|
||||
The Android catalog exposes **Modifier** for every existing exercise. A rename
|
||||
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.
|
||||
## Desktop TUI
|
||||
|
||||
All Android screens are hosted by the fixed `AndroidAppShell` Material 3
|
||||
`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.
|
||||
The desktop is a strict C17 application using Notcurses. It currently provides:
|
||||
|
||||
## 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
|
||||
catalogs as the sole authored scientific source, generates the immutable C
|
||||
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.
|
||||
Rendering does not own SQL or business rules. Statistics are read-only
|
||||
projections; they are not persisted as facts.
|
||||
|
||||
Desktop `training_knowledge.h` and Android `TrainingKnowledgeCatalog` expose
|
||||
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.
|
||||
## Data semantics
|
||||
|
||||
The scientific review passed and the reviewed catalog bytes remain unchanged.
|
||||
The independent temporal review returned `TEMPORAL_DELTA_REVIEW=PASS`, with no
|
||||
temporal defects or repairs. It reviewed the grammar, calendar and offset
|
||||
bounds, fraction precision, bytewise ties, cursor aliasing and exclusivity,
|
||||
source capacity, snapshots, and Android/Python parity; it ran the targeted
|
||||
Meson and four Python temporal tests. The initial full-tranche engineering
|
||||
audit initially failed with stale temporal-defect documentation (BLOCKER),
|
||||
Android loader parity gaps (BLOCKER), omitted Meson generator inputs (BLOCKER),
|
||||
and a C role-only query mismatch (HIGH). One bounded repair chain resolved all
|
||||
four findings; independent repair verification returned
|
||||
`FINAL_REVIEW_REPAIR_VERIFICATION=PASS` and
|
||||
`TRAINING_KNOWLEDGE_V1_ENGINEERING_REVIEW=PASS`. Fresh final validation passed:
|
||||
strict build, 56 Meson tests, eight knowledge and four temporal Python tests,
|
||||
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.
|
||||
- `exercise_id` identifies a catalogue exercise; `entry_id` identifies one
|
||||
ordered occurrence in a session.
|
||||
- Plans and performed work are distinct. A continuous activity is not a fake
|
||||
performed set, and actual repetitions may be heterogeneous.
|
||||
- Explicit MAX results are occurrence-owned and distinct from ordinary best
|
||||
sets. Equipment/resistance/execution context remains relevant.
|
||||
- BODY ZONES are a UX/business projection over explicit scientific knowledge,
|
||||
not a complete anatomical taxonomy.
|
||||
- Feedback roots retain identity while append-only revisions preserve wording
|
||||
history. J+1/session follow-ups are session-owned.
|
||||
- AI proposals contain target-only plans and never create performed work,
|
||||
history, MAX, or feedback until the user explicitly starts and completes a
|
||||
normal capture draft.
|
||||
|
||||
## 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
|
||||
Documents/Trainlog
|
||||
```
|
||||
Hardware-dependent MTP and the final real Drive plus Android-triggered AI-draft
|
||||
smoke test are not automated. The latter is why
|
||||
`TRAINLOG_AI_SESSION_DRAFT_V1` remains `VALIDATION_PENDING`.
|
||||
|
||||
Artifacts:
|
||||
## Active limitations
|
||||
|
||||
```text
|
||||
Android -> PC
|
||||
trainlog-mobile-equipment-definitions-v1.json
|
||||
trainlog-mobile-export-v3.json
|
||||
trainlog-equipment-associations-v2.json
|
||||
trainlog-exercise-body-zones-v1.json
|
||||
|
||||
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.
|
||||
- `APP_SHELL_V1` still awaits the recorded human visual/accessibility review.
|
||||
- AI proposal exchange still awaits one real Drive plus Android-triggered
|
||||
bidirectional smoke test.
|
||||
- Session Generator V1 is hidden while V2 planning semantics are developed.
|
||||
- Hardware MTP validation requires a connected unlocked Android device.
|
||||
- Scientific knowledge is bounded to reviewed catalog entries; unknown custom
|
||||
exercises remain unclassified rather than inferred from names.
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ is a later, separately versioned migration.
|
|||
|
||||
```text
|
||||
TRAINLOG_DATABASE_SCHEMA_VERSION=18
|
||||
DATABASE_SCHEMA_V18=IMPLEMENTED_VALIDATION_PENDING
|
||||
DATABASE_SCHEMA_V18=IMPLEMENTED
|
||||
TRAINLOG_FORMAT_V1=FROZEN
|
||||
```
|
||||
|
||||
|
|
|
|||
552
docs/roadmap.md
552
docs/roadmap.md
|
|
@ -1,523 +1,103 @@
|
|||
# Roadmap
|
||||
|
||||
## MACHINE_EXERCISE_MODEL_V1 — implementation awaiting human review
|
||||
|
||||
Phase 1 introduces additive schema v13, machine-facing exercise identities,
|
||||
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.
|
||||
This document owns future work. The implemented baseline is summarized in
|
||||
[current_state.md](current_state.md); completed narratives and evidence belong
|
||||
in [reviews](reviews/) and [CHANGELOG.md](../CHANGELOG.md).
|
||||
|
||||
## 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
|
||||
ANDROID
|
||||
= fast capture during real use
|
||||
|
||||
DESKTOP TUI
|
||||
= planning
|
||||
+ catalog management
|
||||
+ history
|
||||
+ analytics
|
||||
+ long-term decision support
|
||||
Android captures and summarizes.
|
||||
The TUI analyzes and tracks over time.
|
||||
Desktop SQLite is canonical long-term history.
|
||||
```
|
||||
|
||||
Android must not become the main analytics surface unless this boundary is
|
||||
explicitly revised later.
|
||||
Future work must preserve the frozen Trainlog JSON V1 contract, stable
|
||||
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
|
||||
analytics are based on useful data rather than fixtures.
|
||||
## Current cursor
|
||||
|
||||
```text
|
||||
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;
|
||||
- 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.
|
||||
## Next
|
||||
|
||||
Gate:
|
||||
### Gym catalog V1
|
||||
|
||||
```text
|
||||
REAL_DATA_BASELINE_V1=PASS
|
||||
```
|
||||
Build an inventory from the equipment actually available in the user's gym.
|
||||
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
|
||||
NEXT_FEATURE=GYM_CATALOG_V1
|
||||
```
|
||||
After the gym inventory is normalized, design any remaining structured metadata
|
||||
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
|
||||
gym rather than on a generic Internet exercise list.
|
||||
### Session Generator V2
|
||||
|
||||
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
|
||||
gym zone
|
||||
equipment family
|
||||
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)
|
||||
```
|
||||
Offer editable conveniences built on the real catalogue and planning model.
|
||||
Templates are not rigid prescriptions and never become performed work before
|
||||
normal capture.
|
||||
|
||||
Important rule:
|
||||
## Later
|
||||
|
||||
```text
|
||||
one photographed machine != one exercise
|
||||
```
|
||||
- evolve training analytics only where sufficient real history supports the
|
||||
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
|
||||
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
|
||||
## Canonical order
|
||||
|
||||
```text
|
||||
REAL_DATA_BASELINE_V1
|
||||
|
|
||||
v
|
||||
GYM_CATALOG_V1
|
||||
|
|
||||
v
|
||||
EXERCISE_METADATA_V1
|
||||
|
|
||||
v
|
||||
SESSION_PLANNER_V1
|
||||
|
|
||||
v
|
||||
SESSION_TEMPLATES_V1
|
||||
|
|
||||
v
|
||||
TRAINING_ANALYTICS_V1
|
||||
|
|
||||
v
|
||||
BODY_ANALYTICS_V2
|
||||
|
|
||||
v
|
||||
PROGRESSION_ASSIST_V1
|
||||
|
|
||||
v
|
||||
OBJECTIVES_V1
|
||||
|
|
||||
v
|
||||
BACKUP_EXPORT_V1
|
||||
-> GYM_CATALOG_V1
|
||||
-> EXERCISE_METADATA_V1
|
||||
-> SESSION_GENERATOR_V2
|
||||
-> SESSION_TEMPLATES_V1
|
||||
-> ANALYTICS_EVOLUTION
|
||||
-> BODY_ANALYTICS_V2
|
||||
-> PROGRESSION_ASSIST_V1
|
||||
-> OBJECTIVES_V1
|
||||
-> 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`. The implemented shell does
|
||||
not open STATS_V1, EXERCISE_NAMING_V1, a schema migration, or a new
|
||||
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.
|
||||
- `APP_SHELL_V1=IMPLEMENTED_AWAITING_VISUAL_REVIEW_2`: complete the recorded
|
||||
human visual/accessibility matrix without redefining product semantics.
|
||||
- `TRAINLOG_AI_SESSION_DRAFT_V1=VALIDATION_PENDING`: complete one real Drive
|
||||
plus Android-triggered bidirectional synchronization smoke test.
|
||||
|
||||
## Permanent constraints
|
||||
|
||||
Do not regress to:
|
||||
|
||||
```text
|
||||
SQLite file synchronization
|
||||
mandatory mounted Android filesystem
|
||||
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.
|
||||
Do not regress to SQLite-file synchronization, a mandatory mounted Android
|
||||
filesystem, exercise-name identity heuristics, fake sets for continuous work,
|
||||
synthetic uniform actuals, ordinary training promoted to measured MAX,
|
||||
assistance interpreted as external load, or estimates presented as direct
|
||||
measurements. Incompatible exchange semantics require a new version.
|
||||
|
|
|
|||
|
|
@ -120,8 +120,9 @@ tui_workflows
|
|||
app_shell
|
||||
```
|
||||
|
||||
The current desktop suite, including APP_SHELL_V1 production-transition
|
||||
coverage and the AI-draft exchange regressions, is 56/56. The following generator-specific checkpoint counts remain
|
||||
The desktop suite includes APP_SHELL_V1 production-transition coverage and the
|
||||
AI-draft exchange regressions. The configured suite size and latest result are
|
||||
recorded once in `current_state.md`; the following generator-specific checkpoint counts remain
|
||||
historical evidence:
|
||||
|
||||
```text
|
||||
|
|
@ -234,6 +235,28 @@ Notable regression coverage:
|
|||
|
||||
## 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
|
||||
meson setup --reconfigure build
|
||||
meson compile -C build
|
||||
|
|
@ -533,7 +556,7 @@ Coverage proves:
|
|||
and footer F6/F7 single-occurrence output at 120x35, 100x30, 80x24, and
|
||||
72x20.
|
||||
|
||||
Historical validation checkpoint (the current desktop suite is 56/56):
|
||||
Historical validation checkpoint (not the current suite size):
|
||||
|
||||
```text
|
||||
39/39 Meson tests PASS
|
||||
|
|
|
|||
|
|
@ -548,7 +548,7 @@ meson test -C build --print-errorlogs
|
|||
Validated current normal suite:
|
||||
|
||||
```text
|
||||
39/39 Meson tests PASS
|
||||
39/39 Meson tests PASS (historical checkpoint)
|
||||
```
|
||||
|
||||
## 16. Measured max view
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
#!/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 json
|
||||
import os
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
#!/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 json
|
||||
import math
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
#!/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
|
||||
|
||||
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
|
||||
#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
|
||||
#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
|
||||
#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
|
||||
#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
|
||||
#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
|
||||
#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