trainlog/docs/reviews/sync_generation_capacity_recovery_v1.md

11 KiB

Generation capacity recovery V1

Cause and preserved boundary

The 2026-11-10 private desktop incident reached its unchanged limit of 16: 14 waiting_acknowledgement generations plus two acknowledged recovery members. Recovery retrieved 32 authentic Android ACKs, none for those 14. The desktop worker always captured a new generation after inbound consumption, even after a previous conversation had interrupted publication or ACK handling. Later successful siblings advanced from the same acknowledged parent while old pending branches continued consuming admission. The desktop admission SQL also omitted the acknowledged-sibling exclusion already documented and used by Android. Archiving acknowledged generations alone cannot repair this state.

The incident consumed an Android generation before desktop capture failed. That inbound commit remains durable; the run is failed, never successful. The retained Android peer_capacity_exhausted error has a different run ID from this incident and is historical evidence, not proof of current saturation.

Recovery invariant

A pending branch may leave active admission only if a distinct sibling has the same producer, consumer and parent (including a null root), is acknowledged, and owns a consumed sqlite-commit-full ACK matching its exact original run, peer identities and manifest SHA-256. Archived siblings remain valid proof. Neither elapsed time nor ACK absence proves non-consumption. All generation rows, manifests, artifacts, hashes, causal relationships and memberships remain unchanged. No sibling ACK advances the pending branch's domain publication state. Its own authentic late ACK remains accepted and replay-protected.

The worker resumes the unique unresolved branch using verified immutable bytes. Its mutable reference uses the current conversation run while the manifest and ACK use their original run. ACK selection additionally correlates the generation ID and manifest digest; the production validator still checks all ACK fields. Multiple unresolved branches fail closed. The limit remains 16 and no schema or frozen exchange format changes.

Private copy validation

The existing backup and live database both passed SQLite quick_check. A fresh SQLite backup, complete owned/transport directory copies, table-count inventory and reproduction evidence are retained privately under:

~/.local/share/trainlog/backups/capacity-recovery-20261110T181125/

The original source reproduced generation retention capacity exhausted on a separate backup. The corrected predicate identifies 13 superseded branches, leaving three active members: two acknowledged and one unresolved. All 14 pending immutable generations passed manifest and artifact verification. The one unresolved generation was successfully republished byte-for-byte on the copy. A current complete production capture then succeeded, including Programs V2 and Sleep. Fingerprints covered 56 non-sync tables: the only capture change was the expected Sleep generation-membership insertion. Session, Program, body, Sleep facts, heart-rate and Cardio values remained unchanged.

No personal payload or database is committed as a fixture.

Automated validation

  • C17 strict-warning desktop build, including the embedded Web frontend: pass.
  • test_sync_generation_exchange.py: 21 tests pass, including new capacity, exact-evidence rejection, late ACK, immutable replay, corruption rejection, divergent pending branches, committed inbound data surviving outbound saturation/restart, and archival regressions.
  • Full Meson suite with Web enabled: 113/114 pass. The remaining failure is tui_workflows, tui/tests/test_tui_workflows.c:1680, asserting three Dashboard performance points in a dated fixture. Its C sources and binary inputs are unchanged by this Python-only correction; this is an existing baseline limitation, not claimed as a passing test.
  • JSON catalog/schema validation and import-contract validation: pass.
  • Changed C/H format check: pass (no changed C/H files).
  • No C implementation changed, so a new C sanitizer checkpoint is not required.

The first full-suite configuration lacked installed Web dependencies and failed the deployment-package test. After npm ci and Web-enabled rebuild, that test passed. No production warning policy was weakened.

Local deployment and rollback

The deployed helper directory is separate from development and previous rollouts:

~/.local/share/trainlog/rollouts/sync-capacity-recovery-v1-292bcc8/tools/

It copies the daily Web rollout's tools and reference catalog with only the two corrected helpers replaced; a SHA-256 inventory is retained beside it. Both user services use 90-capacity-recovery.conf drop-ins. Existing desktop binaries, Web assets and transport adapters remain in their previous rollouts. Services were stopped for a fresh coherent predeployment backup and restarted after configuration publication. A private rollback-services.sh removes only these drop-ins and restarts the previous services; it does not roll back business data or delete any backup or generation evidence.

The initial deployment attempt showed that the daily Web shell launcher unconditionally overwrites TRAINLOG_SYNC_TOOLS_DIR. The resulting first controlled attempt still used the old helpers and failed after inbound commit. Its failure evidence is retained. The corrected drop-ins bypass that wrapper using its same underlying binary and explicit adapter paths. The sync daemon uses the isolated tools and an explicit orchestrator path. Actual /proc environment and command lines verified both paths after restart. No previous rollout files were edited.

A second attempt received a current Android generation but exposed a missing reference catalog in the isolated deployment, before its import transaction. The original catalog was copied into the isolated package. The deployed consumer then accepted that exact Android generation on a fresh database copy, and all 14 production capture participants exported successfully using that same deployed package on the copy. Its private test ACK was never published to the phone. The failed attempt remains recorded as failed.

Physical validation

The Android-triggered recovery run sy_f9f0f9eb-e804-4149-a438-755ce0f3fdac completed over Bluetooth on 2026-11-10. It consumed Android generation gen_5f1d5c2c-d87c-4f35-9ca1-2ea3254c65c1 and replayed the exact pending desktop generation gen_cf95dd3b-6fff-4791-9101-e0eceb24ee7d. Android returned its authentic original-run consumed ACK; the desktop ledger corroborated both halves. The active count is three and the 13 superseded pending branches remain retained. This proves historical recovery, not publication of current PC edits.

After the initial deployment faults were repaired, a Web waiter and the phone's new explicit conversation had incompatible freshness baselines. The stale waiting orchestrator was sent SIGTERM, recorded interrupted, released its owned worker and lock, and preserved evidence. The Android-triggered daemon then completed the recovery. A concurrent Web attempt correctly failed the common lock instead of starting a second engine. A later Web waiter was also interrupted before import so the remaining controlled validations could be conducted through the phone's explicit trigger and daemon ledger alone.

Before current-snapshot convergence, the returned Android snapshot contained 31 sessions, 7 body observations and 44 exercises. Desktop had 46 exercises, including the two requested new ones; their actual Android return remains a separate convergence check. All 5,817 pre-existing owned immutable files were verified unchanged. The live non-sync table comparison found only Sleep publication validation/acknowledgement timestamp updates, with no change to business values or revision identities. No Android APK or app data has been changed. ADB exposes no connected device; the paired Bluetooth device is connected. The transport advertisement includes manifest/ACK/archive/recovery V1, history V4 and Programs V1/V2 capabilities. An advertisement is protocol evidence, not proof of the installed APK version.

The current-snapshot run sy_d18e8160-2917-458f-a1ce-3b3c58927a1b completed in both directions at 17:26:45 UTC. Its outbound gen_27f564c0-155b-41ba-8906-768894235499 has an authentic Android consumed ACK. The user also confirmed completion without error on the phone. Comparing all 56 non-sync tables against the predeployment backup found no business-value or revision changes or deleted keys: only validation/ACK timestamps and 11 new Sleep generation-membership rows changed. Existing sessions, Programs and planning, body data, Sleep facts, heart-rate and Cardio tables were unchanged.

The distinct idempotence run sy_97e963d7-189f-4c44-b49d-bf339b14347d also completed in both directions, with outgoing gen_283f98e0-13fe-4def-ae9d-ad5b10f3063f. Both current outgoing manifests contain Programs V2 and Sleep and have exact authentic consumed ACKs; each inbound generation has a committed consumed ledger row. The user confirmed that this attempt entered progress and completed, and that both requested exercises are visible in Android. A received Android history artifact also contains both names, 46 exercises, 31 sessions and 7 body observations; all three stable-ID sets equal the canonical desktop sets.

The final 56-table comparison against the snapshot taken before idempotence found no change to business values or revisions. Expected synchronization metadata changes were limited to Sleep validation/ACK timestamps, 11 additional Sleep generation-membership rows, and two body-zone synchronization markers for the two newly propagated exercises. Existing body-zone rows were retained. No session, set, Program, body, Sleep fact, heart-rate sample or Cardio row was deleted or duplicated. Desktop retains 517 sets, 6 Programs, 98 Program sessions, 23 execution links, 17 Sleep entries, 19 heart-rate captures and 93,903 samples plus 93,903 RR intervals. SQLite quick_check remains ok. All 13 historical pending generations still validate from their immutable files, and no unresolved unsuperseded desktop generation remains. The active window is three after capture (the normal next archive pass returns it to two); the unchanged admission limit is 16.

Private proof owners include final-convergence.json, android-roundtrip.json, the three completed-run reports and their authentic ACK files, coherent predeployment/before-idempotence/final SQLite backups, copied immutable trees, validation logs, installed-file digests and rollback instructions. The Web's separate last waiter remains truthfully interrupted; the Android-triggered service ledger owns the successful controlled runs. No success status was fabricated or copied into a different conversation's history.

TRAINLOG_SYNC_GENERATION_CAPACITY_RECOVERY_V1=PASS applies to this correction, its deployment and the controlled bidirectional convergence/idempotence checks. It does not claim the unrelated baseline TUI test passes. No Android APK, identity, app data, database schema, or remote Git repository was changed.