docs(web): document deletion and program v1

This commit is contained in:
fy59 2026-09-18 17:15:19 +02:00
parent 4dffb05210
commit a0fc48f082
17 changed files with 225 additions and 20 deletions

View file

@ -2,6 +2,20 @@
## Unreleased — 0.1.2
- Completed Web Sessions deletion and Program V1 on desktop schema v25. Separate
revision-guarded Core commands now withdraw manual preparations and AI
proposals or causally delete inactive execution drafts and selected completed
sessions. The accessible trash confirmation preserves derived preparations,
active/concurrent drafts and unrelated performed history; proposal withdrawals
use the bounded `trainlog-ai-session-drafts` V2 tombstone extension to prevent
legacy replay resurrection on Android.
- Added Programs as the fourth Sessions subtab with complete paged listing,
search/state filtering, stable detail links, archival, strict bounded
`trainlog-program` V1 import preview and transactional commit. An explicit
program-session action creates a distinct provenance-bearing manual
preparation without automatic Android delivery or performed data.
- Completed the controlled Web Sessions presentation/withdrawal rollout on the
paired private installation. Desktop migrated from schema v23 to v24 and
Android from v22 to v23 without clearing application data. The requested

View file

@ -16,6 +16,8 @@ evidence and chronology.
- [Web Sessions V1](web_sessions.md) owns the implemented preparation, resume,
history and Android-delivery workflow.
- [Program format V1](program_format_v1.md) owns the strict desktop planning
import contract used by the Sessions Programs subtab.
- [Android](android.md) owns field-companion capture, local persistence,
glanceable summaries, permissions, and Android interaction rules.

View file

@ -6,7 +6,9 @@ preparation is inert until explicit start; an occupied singleton is never
overwritten. Starting preserves the desktop-reserved execution `session_id`,
copies targets only and creates no performed facts.
Android schema v23 adds the durable manual-preparation withdrawal ledger.
Android schema v23 adds the durable manual-preparation withdrawal ledger. The
schema is unchanged by Web proposal withdrawal: the existing durable AI draft
state stores the V2 companion's `deleted` tombstone.
Consumption is part of the outer generation transaction. Exact pending
deliveries are changed to `cancelled`; exact started deliveries remain started,
and their active or completed execution data is untouched. The stored result is
@ -93,7 +95,9 @@ most 64 contiguous ordered SETS entries, target sets 1..99 and REPS targets
1..999 (or the profile-required duration target). Entries must match the
resolved exercise profile and known equipment.
Pending proposals can be explicitly started or deleted. Start is one local
Pending proposals can be explicitly started or deleted. A desktop V2 proposal
withdrawal uses that same permanent tombstone boundary, removes only proposal
entries, and leaves started drafts and performed sessions untouched. Start is one local
transaction: it refuses when the singleton active draft exists, copies targets
only, marks the proposal `started`, and removes its proposal entries. Delete
is gated by a destructive confirmation naming the proposal; Cancel, Back and

View file

@ -52,7 +52,7 @@ parallel implementation of its rules.
| Boundary | Current state |
|---|---|
| Frozen project exchange | `TRAINLOG_FORMAT_V1=PASS/FROZEN` |
| Desktop SQLite | schema v24 |
| Desktop SQLite | schema v25 |
| Android SQLite | schema v23 |
| Mobile snapshot | V3 active; V1/V2 readable legacy inputs; explicit V4 codec staged, not selected by transport |
| Desktop terminal backend | Notcurses only |
@ -221,10 +221,11 @@ progression series, an original BODY ZONES SVG, and private versioned layout
persistence with optimistic conflict and CSRF/Origin protection. It provides no
automatic browser launch. Sessions provides complete paged preparation,
resume and history views, stable details, optimistic manual-preparation writes,
explicit proposal derivation and generation-backed Android delivery. Analyse,
Programmes and Exercises remain explicit placeholders.
explicit proposal derivation and generation-backed Android delivery. Programmes
is implemented as the fourth Sessions subtab; Analyse and Exercises remain
explicit placeholders, and no standalone Programs route exists.
Desktop schema v24 owns immutable manual-preparation revisions, stable ordered
Desktop schema v25 owns immutable manual-preparation revisions, stable ordered
occurrences, persistent HTTP idempotency keys, delivery-to-execution identity,
and durable revision-bound withdrawal records. Android schema v23 stores
received preparations and permanent withdrawal results separately from AI
@ -241,6 +242,19 @@ presentation. The preparation detail exposes a protected, idempotent **Delete
preparation** action; withdrawn rows remain technically inspectable but cannot
be edited or delivered.
Sessions rows expose independent right-edge trash actions and an accessible
destructive confirmation dialog. Proposal withdrawal preserves derived
preparations and is propagated by `trainlog-ai-session-drafts` V2; inactive
execution drafts and completed sessions use revision-guarded causal deletion.
Active or concurrently changed drafts conflict instead of being erased.
Program V1 is desktop-owned planning data in schema v25. The Sessions Programs
subtab lists, searches, filters, deep-links and archives programs. Local JSON
import is strict and bounded, previews through Core before committing, and has
deterministic replay/conflict behavior. One explicit program-session action
creates a provenance-bearing manual preparation without Android delivery or
performed work.
The controlled deployment preserved the installed Android signing identity and
advanced its private `versionCode` from 13 to 14 without uninstalling or
clearing data. The requested six-occurrence validation preparation was already

View file

@ -1,5 +1,19 @@
# Desktop database
## Schema v25: Web deletion requests and Program V1
Schema v25 adds durable Program V1 definitions, their ordered session and
occurrence children, immutable import digests, request replay records and
source provenance on derived manual preparations. It also adds the bounded Web
session-deletion request ledger and nullable AI-proposal `withdrawn_at` state.
Program import and each application command are transactional. Program rows
contain planning targets only and cannot own performed sets.
Proposal deletion is logical. Draft and completed-session deletion append an
immutable causal operation and advance deleted causal state in the same
transaction; completed sessions also receive a durable deleted finalization.
These paths do not implement a generic cascading browser delete.
## Schema v24: durable preparation withdrawal
Schema v24 adds nullable `session_preparations.withdrawn_at` and the immutable
@ -37,8 +51,8 @@ is a later, separately versioned migration.
## 1. Status
```text
TRAINLOG_DATABASE_SCHEMA_VERSION=23
DATABASE_SCHEMA_V23=IMPLEMENTED
TRAINLOG_DATABASE_SCHEMA_VERSION=25
DATABASE_SCHEMA_V25=IMPLEMENTED
TRAINLOG_FORMAT_V1=FROZEN
```
@ -57,7 +71,7 @@ PRAGMA user_version;
Current value:
```text
23
25
```
The independent actual-set loads documented in the current desktop, Android

View file

@ -4,12 +4,13 @@ Status: implementation contract for `TRAINLOG_WEB_SESSIONS_V1`.
## Scope and domain boundaries
The Sessions route has three independent views:
The Sessions route has four independent views:
- Preparation lists manual preparations and immutable imported AI proposals.
- Resume lists lifecycle-backed execution drafts that are not finalized or causally deleted.
- History lists completed sessions. A missing `ended_at` is rendered as an unknown end time; it
does not make a historical row an active draft.
- Programs lists durable planning definitions and can explicitly derive a manual preparation.
An AI proposal is never mutated by viewing it or by deriving a manual preparation. Derivation is
an explicit command which creates a new `sp_<uuid-v4>` preparation and records the proposal ID and
@ -78,6 +79,10 @@ generation containing it is published.
## Explicit exclusions
This V1 does not implement Programs, Analysis, or the standalone Exercises route. It does not add
Program V1 and its import boundary are specified separately in
[`program_format_v1.md`](../program_format_v1.md). They do not turn a program
into a template, proposal, draft or completed session.
This V1 does not implement Analysis or the standalone Exercises route. It does not add
live Web workout capture, historical correction, automatic training prescriptions, proposal
acceptance, or automatic proposal-to-execution conversion.

62
docs/program_format_v1.md Normal file
View file

@ -0,0 +1,62 @@
# Trainlog Program format V1
`trainlog-program` V1 is a strict desktop import format for planning data. It
does not change `TRAINLOG_FORMAT_V1` and is not an Android synchronization
artifact.
## Identity and ownership
A program, each ordered session definition and each ordered exercise occurrence
has a non-empty stable import identity of at most 128 UTF-8 bytes. The `pg_`,
`pgs_`, and `pge_` UUIDv4 forms are recommended creator forms, while imported
identities remain opaque. Display titles are not identities. A program is not a
reusable session template, AI proposal, manual preparation, execution draft or
completed session.
Desktop schema v25 owns the imported program and its exact source SHA-256.
Exact same-ID content replay is idempotent. The same ID with a different byte
payload is an explicit conflict. A different ID may reuse a title because titles
have no identity semantics.
## Document shape
The root has exactly `format`, `version`, and `program`, with format
`trainlog-program` and integer version `1`. The program object has exactly:
- `program_id`, `title`, nullable `note`, `state`, nullable `start_date`,
nullable `end_date`, and `sessions`;
- state `active` on import;
- 1–64 ordered sessions;
- title length 1–200 UTF-8 bytes and note length at most 4,000;
- canonical `YYYY-MM-DD` civil dates, with the end not before the start.
Each session has exactly `program_session_id`, `title`, `session_type`, nullable
`planned_for`, nullable `note`, and `occurrences`. Session type is `training` or
`max_test`; each session contains 1–64 occurrences.
Each occurrence has exactly `entry_id`, `exercise_id`, nullable `equipment_id`,
`load_mode`, `rest_seconds`, nullable `target_sets`, nullable `target_reps`,
nullable `target_duration_seconds`, nullable `target_weight_kg`, and nullable
`notes`. Exercise and equipment identities must already resolve in the desktop
catalog. Recording/tracking profiles and target/load combinations are validated
by the same Core rules used for manual preparations. No actual set, MAX result,
completion timestamp or other performed field is accepted.
The complete UTF-8 JSON document is limited to 512 KiB. Duplicate object keys,
unknown keys, non-finite numbers, invalid UTF-8, duplicate identities, excessive
collections and incompatible profiles are rejected before any row is written.
## Preview, commit and lifecycle
The Web file picker sends bytes to the Core validation endpoint first. Its
preview reports identity, title, dates, session count, unknown-exercise count
and warnings while leaving the database unchanged. Import occurs only after the
user confirms and commits all program/session/occurrence/source rows in one
transaction.
Archive is a revision-guarded lifecycle transition. It hides no derived data
and retains the source definition and digest. Creating a preparation from one
session is a separate idempotent command that copies the ordered plan and stores
program/session provenance on a new `sp_<uuid-v4>` manual preparation. It does
not create a delivery, Android object, execution draft, completed session or
performed measurement.

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

After

Width:  |  Height:  |  Size: 86 KiB

View file

@ -0,0 +1,47 @@
# Web Sessions deletion and Programs V1 evidence
Date: 2026-09-18
This pre-deployment record covers the completed development branch. It does not
claim installation, migration of real data, service restart, phone validation,
tagging or release publication.
## Implemented boundaries
- Separate Core commands withdraw manual preparations and AI proposals, or
causally delete inactive execution drafts and explicitly selected completed
sessions. They require current revisions and durable request identities.
- Proposal withdrawal retains its import ledger and every derived preparation.
The V2 AI-proposal companion publishes a permanent Android tombstone; an
older V1 proposal cannot recreate it. Started executions and performed work
are not proposal-deletion effects.
- The row trash action is independent from detail navigation. The accessible
confirmation dialog has initial safe focus, an Escape path, a focus trap and
trigger focus restoration.
- Program V1 remains desktop planning data. Strict preview and transactional
import share the Core validator. Archive is lifecycle state, and explicit
preparation creation retains program/session provenance without delivery or
performed data.
## Validation
- GCC/Web Meson suite: 88/88 passed.
- Clang strict suite: 86/86 passed.
- Clang ASan/UBSan suite: 86/86 passed.
- Frontend typecheck, production build and 93 Vitest tests passed.
- Android `testDebugUnitTest` and `assembleDebug` passed.
- JSON, import-contract and Python contract tests passed; the two Firefox tests
passed separately in an isolated temporary Python environment.
- The real embedded server was exercised at desktop size and at an exact
390×844 viewport. Its synthetic-only captures include the Sessions list,
destructive confirmation and Programs empty state.
- `tools/check_changed_c_format.sh main` and `git diff --check` passed.
## Deployment gate
Desktop schema v25 and proposal synchronization V2 require the grouped gate:
retain the installed package, create consistent desktop and Android backups,
validate v24→v25 migration on a database copy, verify Android package signing
and monotonic versioning, then install/restart/synchronize and inspect the real
Web routes only after explicit authorization. No operation in this record
crossed that gate.

View file

@ -24,7 +24,7 @@ distinction between measured values and estimates.
## Current baseline
Desktop schema v24, Android schema v23, Notcurses, direct
Desktop schema v25, Android schema v23, 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
@ -33,7 +33,9 @@ exchange and APP_SHELL_V1 retain their explicit manual validation/review gates.
CLI/HTTP infrastructure, embedded frontend shell and the frozen Dashboard data
contract, frozen interactive grid, versioned layout persistence and factual
tile rendering exist. The Dashboard's bounded progression and BODY ZONES
visualizations and Sessions V1 are implemented; the other three Web routes remain placeholders.
visualizations and Sessions V1 are implemented. Programs is implemented inside
Sessions; Analyse, Exercises and the standalone top-level Programs route remain
placeholders or out of scope.
The corrective prepared-item projection, bounded phase-owned MTP outbox,
Sessions presentation correction, local date preference and durable preparation
withdrawal are implemented. The withdrawal uses the separate versioned
@ -55,8 +57,9 @@ transactional consumption, durable correlated ACK and restart/replay entry
points. The orchestrator, Web API and Web control are complete behind an
explicit trusted opt-in. The authorized private direct-MTP rollout passed on
real hardware; automatic V3 selection remains unchanged without that opt-in.
The next separately contracted Web surface is Exercises. Sessions V1 is
complete in software and is not reopened by that future contract.
The next separately contracted Web surface is Exercises. Sessions deletion and
its Programs subtab are complete in software and are not reopened by that
future contract.
`TRAINLOG_SYNC_GAP_CONTRACT_V1=CONTRACT_FROZEN / IMPLEMENTATION_IN_PROGRESS`
defines the complete-sync target and its bounded dependency order.

View file

@ -40,7 +40,7 @@ available because availability is independently guarded by causal state.
## Machine-exercise Phase 1 compatibility
Desktop schema v24 and Android schema v23 retain mobile export V3, readable V1/V2 imports,
Desktop schema v25 and Android schema v23 retain mobile export V3, readable V1/V2 imports,
equipment definitions V1, equipment associations V2, exercise aliases V1, and
the BODY ZONES companion without wire-format changes. Machine metadata is not
silently added to a frozen artifact: stable exercise IDs and canonical names
@ -143,7 +143,7 @@ does not publish to it or let it override an artifact in the new endpoint.
| PC -> Android | `trainlog-equipment-associations-v2.json` | `trainlog-equipment-associations` v2 |
| PC -> Android | `trainlog-exercise-body-zones-v1.json` | `trainlog-exercise-body-zones` v1 |
| PC -> Android | `trainlog-exercise-aliases-v1.json` | `trainlog-exercise-aliases` v1 |
| PC -> Android | `trainlog-ai-session-drafts-v1.json` | `trainlog-ai-session-drafts` v1 companion |
| PC -> Android | `trainlog-ai-session-drafts-v1.json` | `trainlog-ai-session-drafts` v1/v2 companion; v2 adds proposal withdrawals |
| Android -> PC agent | `trainlog-sync-request-v1.json` | `trainlog-sync-request` v1 |
| PC agent -> Android | `trainlog-sync-receipt-v1.json` | `trainlog-sync-receipt` v1 |
@ -522,7 +522,11 @@ retryable desktop status and never undoes the committed import. The desktop
outbound companion selects at most 256 not-yet-published proposals in stable
creation/identity order. Successful MTP publication transactionally marks only
that exact batch; failure leaves it unchanged for retry, while later normal
syncs drain further batches. The permanent Drive import ledger is independent.
syncs drain further batches. V2 additionally carries at most 256 permanent
proposal withdrawals. Desktop admission keeps the complete withdrawal set
within that bound. Android stores each as a deleted tombstone before removing
pending proposal entries, so an older V1 replay cannot resurrect the proposal.
The permanent Drive import ledger is independent.
The inbound outcome (`NONE`, `IMPORTED`, `ALREADY_IMPORTED`, `REJECTED`,
`DRIVE_FAIL`, or `ARCHIVE_FAIL`) is retained in the successful sync report and
history; fetched-invalid, Drive, and archive outcomes do not fail an otherwise

View file

@ -41,6 +41,23 @@ The C MTP regression keeps a malformed retained generation beside the current
reference and proves that pull downloads only the referenced generation while
still rejecting a truncated object inside that current generation.
`web_session_deletions` exercises proposal withdrawal replay, preservation of a
derived preparation, active-draft conflict, causal draft/session deletion and
completed-session finalization. The HTTP suite covers stale and successful
proposal DELETE requests and post-delete deep-link absence. Vitest covers the
focus-managed confirmation, Cancel and Escape zero-mutation paths, the real
DELETE call and focus restoration. AI companion tests prove that V2 exports a
withdrawal without the proposal and that Android's tombstone rejects later V1
replay.
`web_programs` exercises validation preview without writes, transactional
import, exact replay, detail serialization, archive replay, preparation
creation, provenance and absence of delivery. Schema migration coverage checks
all v25 tables and columns. Programs Vitest covers preview before the only
importing mutation and explicit preparation creation/navigation. The full HTTP,
pagination, strict-parser and Core suites retain malformed, duplicate-key,
bounds, catalog/profile/equipment, conflict and rollback coverage.
`test_web_sessions_browser.py` runs headless Firefox against the embedded
production assets and C HTTP server with private temporary XDG roots. It creates
only a synthetic ready preparation and delivery, verifies French then persisted

View file

@ -1,10 +1,12 @@
# Web Sessions V1
The Sessions route implements three distinct views:
The Sessions route implements four distinct views:
- Preparation: manual preparations and immutable AI proposals;
- Resume: lifecycle-backed execution drafts, continued only on Android;
- History: completed sessions, including explicit unknown end times.
- Programs: durable desktop planning definitions, import and explicit manual
preparation creation.
Manual preparation supports a title, session type, optional calendar date,
note, catalogue selection, duplicate occurrences, keyboard reordering,
@ -58,5 +60,22 @@ The tombstone rejects an older delivery replay, including after process restart.
Desktop marks a withdrawal acknowledged only after the correlated Android
generation ACK. Until then Web reports that Android synchronization is pending.
Every deletable row has a separate right-edge trash action with an accessible
target. It never opens the detail. A focus-managed `alertdialog` names the item,
kind, relevant date and consequence; Cancel and Escape write nothing. Proposal
deletion records a durable logical withdrawal and publishes a bounded V2
proposal tombstone without deleting derived preparations or performed work.
Inactive execution drafts and completed sessions use the existing causal
operation/state boundary. Active or stale drafts conflict, and completed-session
deletion records a finalization so older snapshots cannot recreate the session.
Programs are planning data owned by desktop schema v25. Import first runs the
same strict Core validator in preview mode, then commits only after explicit
confirmation. Exact content replay is idempotent; same-ID divergent content is
a conflict. Archiving preserves definitions and derived preparations. Creating
a preparation from one program session copies targets and provenance into a
new manual preparation but creates no delivery, execution or performed data.
See [Program format V1](program_format_v1.md).
This V1 does not provide live Web capture, historical correction, automatic
training generation, Programs, Analysis or the standalone Exercises route.
training generation, Analysis or the standalone Exercises route.