33 KiB
Desktop TUI
1. Purpose
The Trainlog desktop application is a C17/Notcurses interface for durable history, correction, analysis, visualization, direct data entry, and manual synchronization.
Desktop SQLite is the canonical long-term history.
From completed Session Detail, e opens the existing session editor around
trainlog_database_replace_session_exercises(). Per-set values, targets, rest,
continuous facts and explicit MAX values are corrected atomically under the
same session_id; retained occurrences keep entry_id. Destructive set or
occurrence removal uses the global confirmation flow (Non by default,
Escape/q cancel).
Desktop schema v16 snapshots tracking_mode on every session_exercises
occurrence. The exercise editor may therefore change profile axes for future
uses even when history exists. Incompatible changes require an explicit
Non-by-default confirmation which states that old sessions remain unchanged;
Escape and q cancel.
2. APP_SHELL_V1 navigation
The persistent shell has seven root sections:
Accueil | Séances | Exercices | Statistiques | Synchronisation | Paramètres
Séances contains Séance en cours, Programmer une séance, Nouvelle
séance manuelle, and Séances effectuées. The latter replaces the former
top-level Historique entry. Existing mensurations, body graphs/analysis,
exercise performance and explicit MAX views are reached through
Statistiques; this relocation adds no statistic or analytical interpretation.
STATS_V1 makes the Statistics root a data-fed Unicode dashboard rather than a
plain list. Before rendering, its controller takes one bounded read snapshot:
the newest actual-history working-performance series is identified by exact
exercise, nonempty resolved external equipment, tracking, load and performed
dose context; the latest recorded body metric
shows its real trend; and the selected period shows actual-work session counts.
An explicit MAX is reported separately and is never folded into the working
series. Assistance, targets, empty sessions and unavailable measurements do
not become progress facts. Timestamp selection and ordering use Trainlog's
exact instant comparator, including offsets and arbitrary fractional seconds.
Presentation buckets use the local date written in each persisted timestamp;
they never reinterpret history in the current machine timezone.
Malformed legacy timestamps are skipped before statistics sorting and produce a
nonfatal ignored-data warning; valid dashboard facts remain available. The same
four panels and detail actions remain visible at 72x20; terminals of at least
100 columns place performance and measurements side by side with frequency and
catalogue distribution below, rather than reusing the compact vertical stack.
Trend presentation is deliberately sparse: no observation presents an honest
unavailable state, one observation states that the trend is unavailable and
draws no graph, and two or more observations draw only their observed trend.
Dashboard exercise labels resolve the canonical display name from the stable
exercise_id when one is mapped; the resolved equipment label remains
secondary context rather than becoming the exercise identity.
The geometry policy is exact:
under 72x20 small-terminal fallback
72x20 or larger usable compact shell with temporary Navigation control
100x26 or larger visible sidebar
120x32 or larger expanded sidebar
The terminal owns a run-scoped application context with persistent header, content, sidebar when present, and two-line footer planes. It has a single event loop; planes are rendering resources, not application state. Resize recomputes the layout and preserves the route, stable list selection and a visible equivalent focus target. A sidebar focus becomes the compact Navigation control when the sidebar disappears.
Common controls:
↑ ↓ list navigation
Enter open/activate
Tab / Shift+Tab cycle rendered focus targets
F6 open Navigation
F7 open the route's action registry
Esc / b return, cancel, or close/restore an overlay
0 / Home Accueil
1/F1 … 5/F5 manual session, completed sessions, exercises, equipment, mensurations
6 Synchronisation
q quit from the application shell
The root aliases and contextual actions are built from one action registry:
the footer and F7 therefore advertise the same enabled actions that dispatch.
After contextual and shell actions are collected, their stable action identities
are deduplicated before footer layout, so F6 Navigation and F7 Actions each
appear once.
The intentional APP_SHELL_V1 shortcut changes are the replacement of the old
top-level Historique/Corps aliases by 2/F2 Séances effectuées and
5/F5 Mensurations, F6 Navigation and F7 Actions, as approved in design
§13. A route alias is ignored while an overlay is active; a local editor owns
its keys before shell aliases.
On an exercise detail, F7 Actions exposes Fusionner avec…. The current
exercise is the source; a bounded UTF-8 search overlay selects the canonical
target. A second overlay previews the explicit source → canonical direction
and the occurrence-owned data preserved by the transaction. Profile or
primary-zone conflicts leave both exercises untouched and show a specific
result. After success, the canonical target remains selected.
Minimum terminal size:
72x20
Smaller terminals display a clear fallback instead of corrupt layout.
Interface language (TRAINLOG_I18N_V0_1_1)
Trainlog version 0.1.1 is synchronized with Android as one product version; the TUI has no independent interface version. French is the default. English is the only alternate language and is selected through Settings → Language. The active TUI rerenders immediately after a successful selection.
The choice is local presentation state in the XDG configuration file
trainlog/presentation.conf (under $XDG_CONFIG_HOME, or
$HOME/.config when the XDG variable is unavailable). Invalid, absent, or
unreadable preference data falls back to French. A failed persistence attempt
does not claim a committed choice. The setting never enters the desktop SQLite
database, training data, stable IDs, schemas, exchange artifacts, transport
protocols, AI proposals, MAX, or feedback.
Trainlog-owned strings and selected-language number/date formatting are localized. Persisted/user exercise and catalogue names are byte-exact display data, never translation keys. Stable BODY ZONE IDs select localized known-zone labels where applicable; unknown IDs retain their catalogue label. Layout and minimum-terminal behavior remain language-invariant.
The current synchronization screen renders local typed status and counters. It does not translate, reinterpret, or inject Android/core/receipt/history raw summaries: those protocol and operational bytes remain opaque. No exchange, receipt, or synchronization-history format changed for this presentation work.
3. Visual rules
The TUI uses centralized semantic theme roles.
Notcurses provides a true-color Catppuccin-derived dark palette: background
#1E1E2E, surface #181825, text #CDD6F4, and semantic accent, success,
warning, error, muted, and graph roles. Unicode frames and visible selection
markers enhance presentation without becoming application semantics.
The backend owns run-scoped planes, translates terminal input into
Trainlog-owned keys, and accepts complete UTF-8 code points in bounded shell
editors without a runtime parser or ncreader. Search/form storage is capped
at 200 UTF-8 bytes plus NUL, rejects invalid or partial code points without
partial mutation, and keeps grapheme boundaries while moving or deleting.
Escape clears a non-empty search before closing an empty search. Filtered lists
retain selection by stable ID and report bounded page availability.
Input lifecycle is handled at that boundary: legacy/unknown terminal events, Notcurses PRESS events, and deliberate auto-REPEAT events become one logical Trainlog action; Notcurses RELEASE events are consumed and never reach screen navigation or prompt handling. This prevents extended terminal keyboard protocols from applying one physical keypress twice.
Color is not the sole state carrier.
Typical roles:
accent
success
warning
error
muted
graph series
Focused frames use the focus semantic role without recoloring all content. The palette uses synchronized semantic RGB role tokens; an optional Nerd Font may improve symbols, while text fallbacks are mandatory.
4. Exercise catalog
Exercise behavior is driven by:
recording_mode
tracking_mode
data_fields
No exercise-name heuristic determines an entry form.
Catalog identities are stable.
Unicode-aware normalized-name uniqueness prevents duplicate logical names.
3 Exercices also owns Body Zones V1. Creation opens a keyboard-only manifest
selector: p chooses the sole primary, Space toggles secondaries, n selects
the explicit unclassified state and clears all relations, Enter validates and Escape
cancels without mutation. Visible names use the selected presentation language
where Trainlog owns the label; stable IDs such
as chest remain internal. New set-based exercises require a primary zone.
The exercise list supports / prefix search and z cycling through all
manifest zones plus Non renseignés; x clears search and zone filter. Parent filters
include descendants, so Membres supérieurs finds direct chest/back/
shoulders/arms relations without stored parent rows. Search and filter combine.
Exercise detail displays the primary, ordered secondaries and the primary's
derived group. e edits name and zones transactionally; Escape at either edit
prompt leaves the durable row unchanged.
5. Session entry
The TUI can record sessions directly.
Set-based entry supports planned targets and actual work. Planned target values remain planning metadata: they are never copied into an actual performed set.
For a normal SETS occurrence, creation collects planning metadata only: it
does not accept a compact performed-repetitions expression, a performed set
count, sequential performed durations, or a pre-table performed load. It starts
with zero actual rows, then opens the ordered, keyboard-first actual-set table
as the sole path for creating actual work. Each row contains its actual
repetitions (or duration) and, when the occurrence has a load mode, its
independently optional Charge or Assistance value. The table uses:
Up/Down select a row
Left/Right/Tab select metric or load cell
Enter edit the selected cell
a append a row and enter its required actual metric
d/Delete delete the selected set
Escape cancel the active cell, or leave the table
f/b finish the table
Adding a row requires an explicit actual repetitions/duration value and creates no actual load; an empty load cell remains absent rather than inheriting the target. An entered actual load is finite and non-negative; the planned target remains a separate, strictly positive planning value. The current-session summary is assembled from each actual row, so mixed repetitions, optional loads and assistance values are not collapsed into one target value.
Finishing a normal SETS draft with zero actual rows is blocked with an
explicit diagnostic. This guard does not apply to MAX or continuous entries,
whose distinct persistence contracts contain no performed-set rows.
For timed work, the shared duration parser accepts forms such as:
90
90s
1:30
1m30
1m30s
2m
Persistent duration/rest units remain seconds.
Continuous exercise entry asks for duration and configured supplemental fields without set/rest/load prompts.
Session generator
On the dashboard, g opens the generator. It selects a policy BODY ZONE, goal
and duration, captures one explicit reference time, and renders a SQLite-free
preview containing target sets/repetitions, optional observed load/source,
rest, equipment, primary zone, patterns, exposure and shortage reasons.
Preview, resize and cancel write nothing. A generator result with no exercise
cannot enter persistence; a nonempty partial result remains editable with its
warnings visible. Accepting a preview builds the ordinary normal session draft
with zero actual rows and enters the existing editor. The existing completion
guard still requires actual rows for every SETS exercise.
The preview and ordinary plan editor preserve direct kg and no-target entry and
also expose a %MAX calculator. It accepts only integers 1..100 and only the
chronologically latest explicit MAX for the exact exercise/equipment external-
resistance context. Assistance is unavailable. The formula is
MAX × percentage / 100; it is not a recommendation, and only the resulting
target_weight_kg is retained when the draft is accepted. In the generator,
u restores automatic V1 load qualification, % selects the calculator and
x selects no numeric target.
The preview labels these outcomes user_selected_max_percentage or
compatible_max_unavailable and clears a calculated target if its equipment
context changes.
Generator duration is labelled as a target alongside the estimate. A
meaningful shortfall is reported without padding, and V1 states explicitly that
it generates neither warm-up nor cool-down. Multi-session/program work remains
future SESSION_GENERATOR_V2; it is not implemented by these controls.
6. Session history and editing
History is keyboard navigable.
Enter opens full session detail.
For set-based occurrences, detail displays an ordered, scrollable table of the persisted rows with series number, repetitions or duration, and Charge or Assistance. It exposes every stored per-set value, including absent loads, rather than only a compact aggregate.
Persisted session editing preserves the parent session identity and timestamps while replacing child exercise/set data transactionally.
Inside editable session exercise lists:
d delete selected exercise from the session
These delete keys now open the existing event-loop-owned confirmation state;
they do not commit directly. Selection defaults to **Non**, Escape and `q`
cancel, and Enter commits only when the explicit destructive choice is focused.
Draft abandon follows the same rule. Exercise merge keeps its impact preview
and confirmation. Feedback wording remains read-only in the TUI.
A failed replacement rolls back completely.
Removing an exercise from one session does not remove the exercise from the catalog.
7. Equipment
The legacy Equipment catalogue is no longer a normal top-level workflow.
Its storage and compatibility APIs remain available for old history and peers.
Historically, Équipements (direct alias 4/F4) provided supplied-equipment browsing,
search, detail, and custom-equipment creation and selection. Supplied definitions are generated
from catalog/equipment-v1.json; user-created definitions persist in desktop
SQLite and synchronize separately through definitions V1.
Equipment can be selected while editing an exercise occurrence. Session detail,
exercise history, and historical occurrence views resolve an equipment_id
against the supplied manifest or a local custom definition. If an historic ID
is no longer resolvable, the view shows that unknown reference explicitly.
Session detail displays the selected occurrence's stable entry_id together
with its resolved equipment, so repeated appearances of one exercise remain
visibly distinct; i opens the resolved equipment detail.
8. Body tracking
Statistiques → Mensurations (direct alias 5/F5) provides:
- a current-profile snapshot of the selected newest-first body observation;
↑/↓historical-observation selection andEntréedetail access;- weight, when present, on a separate
kgline; - only present circumferences as exact
cmvalues with thin horizontal bars scaled to the largest circumference in that observation; ←/→selection among only metrics that exist in recorded observations, with weight selected by default when available;- a reusable Notcurses evolution chart whose X axis represents elapsed time, whose labelled Y axis adds modest visible headroom, and whose 2x4 Braille raster draws a fine piecewise-linear line between stored observations;
- distinct accent diamonds for actual measurements, including one centered
point with a readable scale for a singleton series; zero points show
Aucune donnée disponible.without unnecessary axes; - first/last civil-date labels and a non-colliding intermediate graduation when the available width permits it;
- detail and correction;
- body trend visualization;
- normalized multi-metric overlay;
- no invented zero values for missing measurements.
The profile and evolution sections are deliberately separate: kilograms never share a circumference scale, missing measurements are omitted, and the profile bars make no progress or trend claim. Sparse profiles donate unused rows to the chart. The current content-plane rectangle is recalculated on every render, so the sections resize responsively down to the supported 72x20 terminal without retaining stale coordinates.
Editing preserves observation identity, timestamp, and optional session link.
9. Statistics and dashboard
The rolling 12-month normalized body graph has moved from the dashboard to
Statistiques → Mensurations; its data rules are unchanged.
The landing dashboard itself is a read-only projection over persisted facts.
Rendering consumes the route-owned snapshot and performs no SQLite access.
Its performance plot is a selected-period bucket count of improvement events,
never a sum of
kilograms across exercises or equipment. Every performed set is a working
observation. Working and explicit-MAX improvements are separate and require a
strictly earlier canonical-instant comparable point; equal-instant stable IDs
only order presentation. Raw series remain in the
per-exercise drill-down. The 30-day default switches with 1 through 5 to 7,
30, 90, 365 days or all history. Progression and frequency share a deterministic
projection of that selection: seven daily buckets for 7 days, six 5-day buckets
for 30 days, six 15-day buckets for 90 days, twelve 31-day presentation buckets
for one year, and at most twelve equal day spans covering represented history
for Tout. Canonical rolling membership is unchanged; local persisted dates only
place included facts into a display bucket, with boundary dates clamped into the
first or current bucket. The summary reports actual sessions, performed sets,
distinct actual exercises as Exercices pratiqués, and explicit MAX records.
The body summary
selects the most recently observed metric; one point has no delta or graph.
Observable session history and the
frequency histogram include a stable session ID exactly once when an occurrence
owns a persisted performed set, continuous activity, or explicit MAX result.
ended_at is presentation/lifecycle metadata, not the history gate; planned
targets and empty occurrences do not count. A capacity warning is shown when a
bounded source reaches its local limit.
Malformed persisted timestamps are excluded before sorting, reported as ignored
legacy data, and do not make the remaining dashboard unavailable.
Répartition du catalogue is a separate global catalogue projection. It
counts every active canonical exercise exactly once by its persisted primary
BODY ZONE; secondary zones do not inflate the result, merged source IDs are not
catalogue rows, and an exercise with no primary zone is shown as Non classés.
Buckets are ordered by descending count then label. Each landing-chart row owns
an independent Unicode horizontal bar between its left label and right count;
the bounded leading buckets remain at compact sizes without colliding with the
frequency chart. Enter opens the existing global exercise catalogue, never an
arbitrary exercise detail.
The dashboard rendering regression covers global event/summary facts, primary zone/unclassified catalogue projection, and sparse or multi-point measurements at 120x35, 100x30, 80x24, and the 72x20 minimum. Unicode blocks/line/point glyphs provide chart geometry; text totals remain the accessible factual equivalent at every supported width. Detail charts show no graph for zero or one actual point, and no ASCII placeholder trace for two or more points.
Rules include:
- fixed calendar month slots;
- missing months remain empty;
- no zero fill;
- no interpolation;
- when multiple observations exist in one month, the last visible monthly value is used for the compact twelve-month graph.
Detailed raw observations remain in Statistiques → Mensurations.
10. Exercise performance
Exercise detail exposes recorded performance history.
Representative comparison semantics:
load none
greatest successful reps/duration
external
greatest actual load
tie -> greatest reps/duration
assistance
lowest assistance
tie -> greatest reps/duration
A best recorded set is not automatically a measured maximum.
11. Sync page
6 Sync uses the shared synchronization engine. The supported shortcut is the
numeric 6 key.
The page shows:
- connected MTP device status;
- storage availability;
- structured synchronization history.
Manual action:
a run Android -> PC synchronization
p run PC -> Android synchronization
b run bidirectional synchronization
r refresh device status
a imports definitions V1, mobile V3, the body-zone companion and associations
V2 only. p publishes definitions V1, catalog V1, the body-zone companion,
mobile V3 (including body observations), and associations V2 only. b
completes that inbound sequence before beginning the outbound sequence.
The direction keys are direct actions: pressing a, p, or b opens one
confirmation for that exact direction; there is no separate mode-selection
step. Enter accepts the pending action and can invoke the shared engine only
once. Esc cancels a pending confirmation without an operation; when no
confirmation is pending, Esc returns from Sync. r only refreshes connected
device status. The former s synchronization shortcut is retired and inert.
The footer states the same contract in both layouts. At 100 columns or wider it
reads a Android→PC p PC→Android b PC↔Android r actualiser Échap retour;
the compact footer uses a A→PC p PC→A b A↔PC r act. Échap retour without
changing any action or direction.
Before a confirmed run, Sync identifies the selected direction in its progress
feedback. It then shows the completed run summary on success, or a
direction-prefixed diagnostic on failure. Each structured local history run
persists its selected a, p, or b direction, and the history list displays
that direction label alongside its summary. direction inconnue is used only
for a legacy entry whose direction was never recorded, rather than being
guessed.
History behaves like a compact Git log:
↑ ↓ select run
Enter open run detail
The detail view behaves like a compact git show and contains:
sync ID
trigger
time
status
request ID when applicable
Android -> PC counts
PC -> Android catalog count
summary
error when applicable
12. Shared sync engine
The TUI does not own a separate synchronization implementation.
It calls:
trainlog_sync_run(TRAINLOG_SYNC_TRIGGER_TUI, ...)
The Android-triggered daemon calls the same engine.
This keeps import/export, MTP publication, locking, history, and diagnostics in one implementation.
13. Direct MTP
Transport uses:
libudev -> exact physical USB device
libmtp -> storage/object operations
No filesystem mount is required.
Raw libmtp output is suppressed while Notcurses owns the terminal.
14. Error behavior
Input is validated before persistent mutation.
Escape cancels prompts without committing partial edits.
Synchronization failure displays a useful final diagnostic and records the structured run when a transaction actually begins.
Conflict diagnostics identify the stable affected identity, the source artifact/direction, and a concise source summary. They preserve existing session, body-observation, association, and definition data rather than silently overwriting it.
15. Build and test
meson compile -C build
meson test -C build --print-errorlogs
Validated current normal suite:
39/39 Meson tests PASS (historical checkpoint)
16. Measured max view
Creating or editing a Test de max session uses the existing exercise search
and optional equipment selector, then asks only for Poids max (kg). It does
not prompt for sets or repetitions and persists the value in max_results.
The history detail for a max-test session is a compact selectable table:
Exercice Machine Max
Pec Fly Rear Delt / Pec Fly 100 kg
Rear Delt Fly Rear Delt / Pec Fly 86 kg
Equipment is occurrence context. The rows remain distinct by movement and
stable entry_id; no machine-level maximum is calculated. A preserved
non-MAX entry, such as continuous warm-up, remains visible with — in the MAX
column.
From 3 Exercices, the selected exercise exposes:
Enter ordinary performance history
m measured max
The measured-max page is deliberately separate from ordinary best-set history.
It shows:
- count of explicit max-test sessions;
- newest successful measured result;
- best historical result using the same load mode;
- dedicated max-test graph;
- max-test history;
- external-load working percentages at 60%, 70%, 80%, and 90%.
For external load, r cycles practical rounding increments:
0.5 kg
1.0 kg
2.5 kg
5.0 kg
Working percentages are display calculations only.
Assistance remains inverse-direction:
less assistance = better
No percentage-of-max working load is produced for assistance or no-load performance.
17. Body analytics
Statistiques → Mensurations adds:
v analyse corporelle
The analytics view has two pages:
Composition et tendance
Proportions et symétrie
p configures a desktop-only estimation profile containing height and the
circumference-formula branch.
Composition can display:
- circumference-based body-fat estimate;
- estimated fat mass when body weight is present;
- estimated lean mass when body weight is present;
- weight change from the oldest available weight;
- waist change from the oldest available waist;
- body-fat estimate change when comparable observations exist.
Proportions can display:
- waist/hip ratio;
- shoulder/waist ratio;
- chest/waist ratio;
- left/right asymmetry for arms, forearms, thighs, and calves.
All estimates are explicitly labeled as estimates. No result is converted into a medical or diagnostic classification.
18. Statistics center
TRAINLOG_TUI_STATISTICS_V1=PASS organizes the Notcurses statistics route as
five keyboard-first views: Corps, Vue globale, Exercices, Zones and MAX. The
body view retains recorded weight and circumference history. The other views
use read-only aggregates over the existing desktop schema and never persist a
derived score or cache.
The global view switches between the current calendar week, current calendar
month and complete history. It reports actual sessions, occurrences, sets, repetitions,
available durations, active days/weeks and loaded volume. Loaded volume is
strictly repetitions × actual external load when both the occurrence and the
exercise declare external-load semantics; duration, cardio, assistance and
no-load work are excluded. Its common Notcurses chart cycles through bucketed
loaded volume, sessions, sets and immediate-feedback counts.
Exercise statistics are selected by canonical exercise_id, include aliases
already reconciled by persistence, and omit inapplicable load facts. Their
graph cycles through recorded load, valid volume, repetitions, sets and
explicit MAX values. The MAX detail displays only persisted explicit tests,
including latest, previous, historical best and auditable deltas.
Zone statistics count distinct actual sessions through persisted
exercise_body_zones primary/secondary associations. Exercise names are never
used to infer a zone. Planning comparisons are limited to targets and actuals
owned by the same persisted occurrence. Immediate feedback remains attached
to its occurrence; J+1 feedback remains session-global, and free text is not
classified or attributed automatically.
TRAINLOG_TUI_STATISTICS_REFINEMENT_V1=PASS keeps these five routes while
making their visual semantics explicit. Finite 7-day and 30-day graphs retain
the complete selected time domain, including inactive space. Weekly totals use
Monday-based local calendar buckets, explicit empty weeks and discrete bars;
non-negative counts and volumes use a zero baseline. Continuous observations,
exercise progression and explicit MAX histories remain line graphs.
On sufficiently large planes, the training summary is grouped into Activity,
Work, Feedback and Planned/Performed panels; compact planes retain the bounded
vertical form. Zone primary and secondary bars share one scale across every
visible row and show their raw counts. The MAX catalogue defaults to exercises
with an explicit result, shows latest value/date, and uses a to reveal all
compatible set-based exercises. Prefix search and stable-ID drill-down are
unchanged.
TRAINLOG_TUI_STATISTICS_PERIOD_BUCKETING_V1=PASS separates the textual
summary window from the comparison depth of its chart. 7j summarizes the
rolling last seven days but compares up to four available consecutive local
calendar weeks.
TRAINLOG_TUI_STATISTICS_PERIOD_BUCKET_SIZE_FIX=PASS is the historical
checkpoint that first separated comparison sizes. Its duration-based Tout
adaptation remains active; its finite rolling boundaries are superseded by the
calendar contract below. Tout uses Monday-based 7-day buckets while its
elapsed span fits the bounded graph, then switches the whole graph to calendar
months. The decision uses historical duration, so sparse old activity is not
silently discarded.
Explicit zero buckets remain visible between represented boundaries. Each chart uses one bucket size and one zero-based scale, and its title explicitly states 7-day periods, 30-day periods, complete-history weeks, or complete-history months.
TRAINLOG_TUI_STATISTICS_CALENDAR_BUCKETS_V1=PASS supersedes rolling bucket
boundaries with calendar alignment. 7j summarizes the current local calendar
week and compares Monday–Sunday weeks. 30j summarizes the current Gregorian
calendar month and compares the current month with up to three preceding
months. The current week or month remains partial until its calendar endpoint.
Historical membership uses the YYYY-MM-DD civil date stored in each persisted
timestamp. It is never reassigned by converting that timestamp through the
current machine timezone. Month keys advance by Gregorian year/month, so
28-day February, leap-year February, 30-day months and 31-day months all expose
their exact exclusive chart endpoint. French abbreviated month labels and
Monday–Sunday ranges make the aggregation visible in the TUI.
19. Training knowledge infrastructure
The desktop core includes immutable training_knowledge.h catalog access and
read-only training_context.h composition. The context uses real persisted IDs
and returns persisted zones, optional science, compatible equipment, latest
explicit MAX and bounded chronological history; it does not write data. The
TUI provides a UTF-8 cell-aware scrolling knowledge screen, tested at the 72x20
minimum terminal. V1 provides no prescription, generator or scoring flow. Its
lifecycle is TRAINING_KNOWLEDGE_V1=PASS. Its occurrence and latest-MAX readers use the settled
temporal contract: exact instant/fraction ordering, bytewise stable-ID ties,
exclusive source-text cursors, bounded selection before hydration, and one
read snapshot per call. Malformed cursors and matching stored timestamps fail
explicitly. The full-tranche audit repair chain and independent verification
passed. See Training knowledge system V1.
The normal Sessions hub has current/manual/completed paths only; the retained
SESSION_GENERATOR_V1 route is not advertised. Physical / and F7 Rechercher
dispatch the one registered list-search action on supported lists.
Equipment Detail lists V2 verified exercise options and confidence within the
72x20 layout. The generated core also exposes bounded reverse and BODY ZONE
queries without changing Body Focus geometry or session generation.
Completed-session detail also consults synchronized exercise feedback and the
session follow-up timeline read-only. Exercise labels use Pendant la séance,
H+xx, or neutral Ressenti; follow-ups use H+xx or H+?. The TUI never
renders a negative H+ and has no V1 feedback mutation.