diff --git a/CHANGELOG.md b/CHANGELOG.md index 89005ab..e66af51 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/docs/README.md b/docs/README.md index 7075358..0e71e4c 100644 --- a/docs/README.md +++ b/docs/README.md @@ -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. diff --git a/docs/android.md b/docs/android.md index be0ade8..d62e9ad 100644 --- a/docs/android.md +++ b/docs/android.md @@ -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 diff --git a/docs/current_state.md b/docs/current_state.md index 19cec25..dc5d32b 100644 --- a/docs/current_state.md +++ b/docs/current_state.md @@ -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 diff --git a/docs/database.md b/docs/database.md index 60169b7..eac1ab9 100644 --- a/docs/database.md +++ b/docs/database.md @@ -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 diff --git a/docs/design/web_sessions_v1.md b/docs/design/web_sessions_v1.md index 8d09f18..663d698 100644 --- a/docs/design/web_sessions_v1.md +++ b/docs/design/web_sessions_v1.md @@ -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_` 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. diff --git a/docs/program_format_v1.md b/docs/program_format_v1.md new file mode 100644 index 0000000..4fe694c --- /dev/null +++ b/docs/program_format_v1.md @@ -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_` manual preparation. It does +not create a delivery, Android object, execution draft, completed session or +performed measurement. diff --git a/docs/reviews/evidence/web-programs-empty-mobile.png b/docs/reviews/evidence/web-programs-empty-mobile.png new file mode 100644 index 0000000..5a1ea94 Binary files /dev/null and b/docs/reviews/evidence/web-programs-empty-mobile.png differ diff --git a/docs/reviews/evidence/web-sessions-delete-confirmation-mobile.png b/docs/reviews/evidence/web-sessions-delete-confirmation-mobile.png index cf5d0ad..e1a770f 100644 Binary files a/docs/reviews/evidence/web-sessions-delete-confirmation-mobile.png and b/docs/reviews/evidence/web-sessions-delete-confirmation-mobile.png differ diff --git a/docs/reviews/evidence/web-sessions-editor-mobile.png b/docs/reviews/evidence/web-sessions-editor-mobile.png index b1a82e1..e5e75e1 100644 Binary files a/docs/reviews/evidence/web-sessions-editor-mobile.png and b/docs/reviews/evidence/web-sessions-editor-mobile.png differ diff --git a/docs/reviews/evidence/web-sessions-list-desktop.png b/docs/reviews/evidence/web-sessions-list-desktop.png index 47fa83c..50dd1a3 100644 Binary files a/docs/reviews/evidence/web-sessions-list-desktop.png and b/docs/reviews/evidence/web-sessions-list-desktop.png differ diff --git a/docs/reviews/evidence/web-sessions-withdrawn-detail-desktop.png b/docs/reviews/evidence/web-sessions-withdrawn-detail-desktop.png index ba390b7..7d6ad4d 100644 Binary files a/docs/reviews/evidence/web-sessions-withdrawn-detail-desktop.png and b/docs/reviews/evidence/web-sessions-withdrawn-detail-desktop.png differ diff --git a/docs/reviews/web_sessions_delete_programs_v1_evidence.md b/docs/reviews/web_sessions_delete_programs_v1_evidence.md new file mode 100644 index 0000000..bd2649e --- /dev/null +++ b/docs/reviews/web_sessions_delete_programs_v1_evidence.md @@ -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. diff --git a/docs/roadmap.md b/docs/roadmap.md index f258a11..a24db16 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -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. diff --git a/docs/sync_exchange.md b/docs/sync_exchange.md index 87f2f3e..40f84f4 100644 --- a/docs/sync_exchange.md +++ b/docs/sync_exchange.md @@ -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 diff --git a/docs/tests.md b/docs/tests.md index 1b4a474..154ea68 100644 --- a/docs/tests.md +++ b/docs/tests.md @@ -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 diff --git a/docs/web_sessions.md b/docs/web_sessions.md index a5cf356..9df7c5c 100644 --- a/docs/web_sessions.md +++ b/docs/web_sessions.md @@ -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.