diff --git a/CHANGELOG.md b/CHANGELOG.md index b50760e..8f21ede 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,19 @@ Development opened after the stable v0.1.2 release. +- Hardened direct-MTP full-generation conversations after a private Android + run was frozen before generation capture. Android now emits bounded + run/phase metadata, refreshes the user-enabled background listener before a + foreground request, and confirms MediaProvider/MTP visibility for immutable + generation objects before publishing their reference. Desktop MTP pulls are + throttled to one complete refresh every three seconds, keep a separate + 30-second operation budget, and report expiration of the conversation as a + correlated protocol timeout instead of a misleading final one-second + transport timeout. Explicit requests continue to receive fresh UUIDs. + Mensurations now retains two compact columns at the deployed phone's 312 dp + content width, with a narrow/large-font fallback. Schemas, causal semantics, + generation formats, ACK formats and standalone V3 strictness are unchanged. + - Routed Android foreground and daemon-triggered daily synchronization through the existing full-generation orchestrator whenever its trusted local configuration is present. The standalone mobile V3 importer remains strict diff --git a/docs/android.md b/docs/android.md index a480230..374da15 100644 --- a/docs/android.md +++ b/docs/android.md @@ -1067,6 +1067,28 @@ generation remains resumable only until the desktop generation for that run is durably consumed; a fully completed run is then excluded from foreground selection. This preserves late-ACK recovery without replaying completed runs. +The direct-MTP path confirms transport visibility independently from ordinary +`File.exists()` checks. Android fsyncs and atomically commits every immutable +generation artifact, asks MediaProvider to scan those files, waits for all +bounded callbacks, and only then atomically publishes and scans +`android-generation-v1.json`. The same boundary covers the peer advertisement, +explicit request signal, Android consumption ACK and bounded error reference. +Manifest/reference remain the commit markers; scanning does not change their +bytes or protocol meaning. Production logging records only `run_id`, phase, +artifact name, generation ID and result. + +When background synchronization is enabled, an explicit UI request refreshes +the foreground listener before starting the UI-owned conversation. The shared +process lock prevents a second run while the UI coordinator is alive; if the +activity is frozen or destroyed, the listener can validate and resume the +durable request without inventing another generation. + +The Mensurations grid uses two 140 dp compact fields plus a 10 dp gutter at +normal phone widths. The deployed 1080 px/480 dpi phone supplies approximately +312 dp inside the screen frame and therefore receives two columns. Layouts +below 290 dp, or font scales of 1.5 and above, retain the readable one-column +fallback. + Backup manifests retain format version 1 and the current Android schema. Their `product_version` is read from installed package metadata, so development builds record `0.1.3` without a second hard-coded product-version source. diff --git a/docs/current_state.md b/docs/current_state.md index eccae91..f143027 100644 --- a/docs/current_state.md +++ b/docs/current_state.md @@ -9,6 +9,25 @@ USB listener with mandatory notification and a private-folder Drive grant with bounded WorkManager checks. Both use the same concurrency-guarded generation coordinator as SyncScreen. +Direct-MTP publication now has an explicit Android visibility boundary. +Durable immutable generation files are submitted to MediaProvider and all scan +callbacks complete before the mutable generation reference is published; the +reference and Android ACK use the same confirmation. Bounded diagnostic logs +contain only run/generation identities, artifact names, phases and results. +The desktop worker performs lightweight local correlation checks continuously +but opens a full MTP pull no more than once every three seconds. A 30-second +individual adapter budget is distinct from the overall conversation deadline, +so an absent correlated generation reports a protocol wait timeout while an +actually blocked adapter reports `transport_timeout`. + +The failed private run `sy_9639f06e-58ba-4bc0-ab1b-1488aefe011c` received its +desktop request and archive-ACK files on the Android filesystem, but created no +Android generation row/reference/object. System evidence shows the activity +was moved to Android's file-access settings and frozen before capture. The +retained Android generation reference and MTP bytes both belonged to an older +run; no hidden new generation was found. No causal or business mutation +occurred. + Existing preparation rows expose separate editing and Android-delivery states and an accessible send action. Draft/local advances the same preparation to a ready revision before delivery; ready/local delivers the current revision. diff --git a/docs/sync_exchange.md b/docs/sync_exchange.md index cdb7c2d..d1524af 100644 --- a/docs/sync_exchange.md +++ b/docs/sync_exchange.md @@ -959,6 +959,19 @@ It reads the peer advertisement, current Android generation reference and current Android consumption ACK, then downloads only the generation directory named by that validated reference. It does not recursively mirror retained Android generations, desktop publications, or unrelated exchange history. +Local correlation is checked every 50 ms without USB work; complete MTP pulls +are throttled to a three-second cadence. Each MTP operation has its own +30-second ceiling while the conversation retains its configured global +deadline. Exhausting the global deadline while waiting for a correlated +reference reports `timeout waiting for correlated android-generation-v1.json`; +only an adapter that exceeds its independent operation budget reports +`transport_timeout`. + +Android direct-file publication has a separate MediaProvider visibility +barrier. Immutable generation files, including the manifest, become visible +before `android-generation-v1.json` is committed. The reference therefore +cannot advertise a generation whose files have not completed the Android MTP +visibility callbacks. No remote object is removed during polling or scanning. `GET /api/v1/prepared-items` is a separate read-only Web projection. It does not change the frozen Dashboard `next_session.available` field or any exchange diff --git a/docs/tests.md b/docs/tests.md index 7604d78..fbbcfb3 100644 --- a/docs/tests.md +++ b/docs/tests.md @@ -1544,6 +1544,26 @@ fixture's sample draft. ## Private full-generation rollout validation +The MTP visibility regression suite uses injected clocks and transport +callbacks, with no multi-second sleeps. It covers delayed Android publication, +multiple throttled polls, stale run references, a never-visible generation, +an independently blocked adapter, and exact protocol-versus-transport timeout +classification. Android tests prove fresh request UUIDs, foreground/background +lock ownership, objects-visible-before-reference ordering, bounded phase +tracing, and a completed full-generation conversation. Compose tests exercise +the measured 312 dp phone content width as two columns, a true 280 dp narrow +layout as one column, 1.5 font-scale fallback, long French labels and `kg`/`cm` +suffixes. + +Read-only forensics for failed run +`sy_9639f06e-58ba-4bc0-ab1b-1488aefe011c` found the correlated desktop request +and archive acknowledgements on Android, but no correlated generation +reference, immutable directory or error record. The direct filesystem and MTP +views contained identical bytes for the retained older Android reference. +Logcat showed Android moving Trainlog behind the file-access settings surface +and freezing it before capture. The post-v19 backup predates this run and is +not presented as SQLite evidence for it. + The authorized 2026-09-17 rollout added regression coverage for production failures found only after installation: feedback import under `sqlite3.Row`, the complete exporter inventory of the relocatable package, stale Android ACK