From cc87f26b981a60724a55959e881b36accf692daf Mon Sep 17 00:00:00 2001 From: fy59 Date: Thu, 17 Sep 2026 15:26:14 +0200 Subject: [PATCH] docs(sync): record native candidate evidence --- CHANGELOG.md | 6 ++++++ docs/current_state.md | 7 +++++++ docs/sync_rollout_readiness.md | 12 ++++++++++++ docs/tests.md | 25 +++++++++++++++++++------ tests/test_web_sync_browser.py | 32 ++++++++++++++++++++++++-------- tools/sync_peer_worker.py | 26 +++++++++++++++++++++----- 6 files changed, 89 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 903dc53..d7bd0ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ ## Unreleased — 0.1.2 +- Closed the native candidate warning gate with checked path and HTTP response + bounds, strict GCC and Clang builds, full Clang ASan/UBSan validation, and a + stable concurrent object-I/O MTP double. Rebuilt the desktop, current Android + and non-installed schema-v17 bridge candidates with recorded digests; no + service, device, user database, signing secret or deployment was touched. + - Normalized the recent synchronization, generation-MTP, Android backup, Web Dashboard serialization, tests, I/O doubles and packaging code for human readability. Added scoped C formatting rules and review guidance without diff --git a/docs/current_state.md b/docs/current_state.md index ec72f39..1e70b10 100644 --- a/docs/current_state.md +++ b/docs/current_state.md @@ -8,6 +8,13 @@ non-installed schema-v17 bridge candidate is reproducibly built and tested. Physical-device MTP, release signing, real user backup/restore, main integration and deployment are not complete. +`TRAINLOG_NATIVE_BUILD_AND_CANDIDATE_V1` closes the native warning gate. Strict +GCC 16.2.1 and Clang 22.1.8 builds now pass the same 81-test inventory, and the +Clang ASan/UBSan build passes it with leak detection. Bounded path assembly and +the Web sync accepted response reject insufficient capacity without publishing +partial output. Fresh desktop, current-Android and schema-v17 bridge candidates +were rebuilt from commit `2c5a8be2827e31fc5be2accb65073bfeb79d6068`. + Snapshot date: **2026-09-17**. `TRAINLOG_CODE_READABILITY_V1` normalizes the recent generation-MTP, diff --git a/docs/sync_rollout_readiness.md b/docs/sync_rollout_readiness.md index 9d03eac..b972a63 100644 --- a/docs/sync_rollout_readiness.md +++ b/docs/sync_rollout_readiness.md @@ -49,6 +49,18 @@ and non-destructive hardware smoke. Drive remains separate and untested here. This is a non-executed rollout checklist. The development branch implements the path; the daily installation still uses V3. +The latest software-only candidate set was rebuilt under the private root +`/home/fy59/.cache/trainlog/native-build-candidate-v1.VbFjE9` from source commit +`2c5a8be2827e31fc5be2accb65073bfeb79d6068`. Its desktop inventory records every +packaged file digest plus schemas, protocols, entry points and runtime +dependencies. The current debug APK SHA-256 is +`aca2f499ee9ff53ba5913a76a2a0117274e3e5545967ce361f6f126a6af497e1`; the +non-installed bridge APK SHA-256 is +`0a28d81cbb0818cdf2fee72d5024a4b706fcfda39f1182c682c12f000a026cb4`; and its +synthetic schema-v17 backup SHA-256 is +`06eb3999c6f3ce2fd8f551b38727a0a67fe6b48dde4c3244b3d50357afa7e6a3`. +These are unsigned development evidence, not deployment authorization. + ## Compatible set - desktop binary containing schema v21, the Web API and the generation worker; diff --git a/docs/tests.md b/docs/tests.md index 5f0b358..de5716f 100644 --- a/docs/tests.md +++ b/docs/tests.md @@ -1317,12 +1317,14 @@ behavioral assertions. Python syntax/orchestrator tests, Android generation and backup tests, the I/O-boundary MTP browser proof, JSON/import validators and the isolated harness cover the other normalized sources. -GCC 16.2.1 now compiles beyond the former `misleading-indentation` failures in -`json_writer.c`, `web_dashboard_json.c`, `web_dashboard.c`, -`dashboard_layout.c`, and `generation_mtp.c`. The separate strict build remains -blocked by distinct pre-existing `format-truncation` diagnostics in the Web -accepted-response buffer and bounded test path buffers. Warning policy remains -unchanged; GCC is not reported as green. +GCC 16.2.1 and Clang 22.1.8 both pass separate strict C17 builds and the full +**81/81** Meson inventory with `werror=true` and warning level 3. The former GCC +`format-truncation` findings are closed by exact checked path construction in +the generation-MTP tests/double and by a production accepted-response +serializer that reports its byte count. Boundary tests cover exact fit, +one-byte insufficiency and multibyte path components; failure leaves no partial +path or response available to a caller. Clang ASan/UBSan passes the same +**81/81** inventory with leak detection. The readability closeout preserves the **81/81** normal Meson and **81/81** ASan/UBSan inventories. The isolated Android run reports **220 tests: 215 @@ -1330,3 +1332,14 @@ passed, five skipped, zero failures/errors**, and `assembleDebug` succeeds on JDK 17. The real Firefox/object-I/O-boundary MTP conversation also passes after formatting. A source comparison against the pre-readability commit confirms that the scoped test names and assertion counts are unchanged. + +The isolated JDK 17 harness reports **220 Android tests: 215 passed, five +skipped, zero failures/errors**. The skips are optional, externally supplied +copy gates: `BridgeBackupMigrationTest.verifiedSchemaSeventeenBridgeBackupMigratesThroughCurrentOwner`, +`RealAndroidV9BodyZonesMigrationTest.realVersionNineCopyMigratesWithoutChangingExistingTables`, +`RealAndroidV14FeedbackRevisionMigrationTest.freshRealCopyMigratesLosslesslyToVersionFifteen`, +and the version-thirteen and version-twelve methods in +`RealAndroidV12MachineExerciseMigrationTest`. They require respectively the +bridge backup input, a real v9 fixture, a real v14 fixture/output directory, +and real v13/v12 fixtures with explicit output directories. Synthetic bridge +and current-schema migration proofs run separately; no user database is read. diff --git a/tests/test_web_sync_browser.py b/tests/test_web_sync_browser.py index eb8f969..5865895 100644 --- a/tests/test_web_sync_browser.py +++ b/tests/test_web_sync_browser.py @@ -7,7 +7,9 @@ from pathlib import Path ROOT = Path(__file__).resolve().parents[1] BUILD = ROOT / os.environ.get("TRAINLOG_WEB_E2E_BUILD", "build") / "tui" GECKO = Path( - os.environ.get("TRAINLOG_GECKODRIVER", "/home/fy59/.cache/trainlog/web-e2e-tools/geckodriver") + os.environ.get( + "TRAINLOG_GECKODRIVER", "/home/fy59/.cache/trainlog/web-e2e-tools/geckodriver" + ) ) @@ -40,7 +42,9 @@ class BrowserSyncTest(unittest.TestCase): transport.mkdir() owned = root / "owned" owned.mkdir() - remote = (root / "device/Documents/Trainlog") if mode == "mtp" else transport + remote = ( + (root / "device/Documents/Trainlog") if mode == "mtp" else transport + ) remote.mkdir(parents=True, exist_ok=True) release = root / "release-android" android_log = (root / "android.log").open("wb") @@ -140,11 +144,15 @@ class BrowserSyncTest(unittest.TestCase): driver.get(f"http://127.0.0.1:{port}/") wait = WebDriverWait(driver, 15) button = wait.until( - lambda d: d.find_element(By.XPATH, "//button[normalize-space()='Synchroniser']") + lambda d: d.find_element( + By.XPATH, "//button[normalize-space()='Synchroniser']" + ) ) wait.until(lambda _: button.is_enabled()) button.click() - state_path = Path(str(xdg / "trainlog" / "trainlog.db") + ".sync-run.json") + state_path = Path( + str(xdg / "trainlog" / "trainlog.db") + ".sync-run.json" + ) observed = await_condition( lambda: ( json.loads(state_path.read_text()) @@ -164,7 +172,10 @@ class BrowserSyncTest(unittest.TestCase): self.assertEqual(committed["result"], "running") with sqlite3.connect(xdg / "trainlog" / "trainlog.db") as database: self.assertEqual( - database.execute("SELECT COUNT(*) FROM execution_drafts").fetchone()[0], 1 + database.execute( + "SELECT COUNT(*) FROM execution_drafts" + ).fetchone()[0], + 1, ) release.touch() final = await_condition( @@ -186,16 +197,21 @@ class BrowserSyncTest(unittest.TestCase): ) summary.click() wait.until( - lambda d: "Brouillons reçus" in d.page_source and "active" in d.page_source + lambda d: "Brouillons reçus" in d.page_source + and "active" in d.page_source ) driver.refresh() wait.until(lambda d: "Synchronisation confirmée" in d.page_source) state = json.loads( - Path(str(xdg / "trainlog" / "trainlog.db") + ".sync-run.json").read_text() + Path( + str(xdg / "trainlog" / "trainlog.db") + ".sync-run.json" + ).read_text() ) self.assertEqual(state["phase"], "completed") self.assertEqual( - android.wait(timeout=30), 0, (root / "android.log").read_text(errors="replace") + android.wait(timeout=30), + 0, + (root / "android.log").read_text(errors="replace"), ) finally: if driver is not None: diff --git a/tools/sync_peer_worker.py b/tools/sync_peer_worker.py index fb655f7..b453223 100644 --- a/tools/sync_peer_worker.py +++ b/tools/sync_peer_worker.py @@ -139,7 +139,12 @@ def main() -> int: if args.mtp_adapter is None: raise RuntimeError("MTP adapter is required") pump = lambda: run_adapter( - args.mtp_adapter, "pull", args.expected_peer, args.transport_root, deadline, True + args.mtp_adapter, + "pull", + args.expected_peer, + args.transport_root, + deadline, + True, ) # Android publishes its durable identity only while participating. A # missing expected advertisement is therefore a bounded wait state; @@ -162,7 +167,9 @@ def main() -> int: request_path = args.transport_root / "request-v1.json" publish_json(request_path, request) if args.mode == "mtp": - run_adapter(args.mtp_adapter, "push", args.expected_peer, args.transport_root, deadline) + run_adapter( + args.mtp_adapter, "push", args.expected_peer, args.transport_root, deadline + ) emit( args.run_id, "waiting_android_publication", @@ -178,7 +185,9 @@ def main() -> int: ack_path = args.transport_root / "desktop-consumption-ack-v1.json" publish_json(ack_path, ack) if args.mode == "mtp": - run_adapter(args.mtp_adapter, "push", args.expected_peer, args.transport_root, deadline) + run_adapter( + args.mtp_adapter, "push", args.expected_peer, args.transport_root, deadline + ) if ack["result"] != "consumed": raise RuntimeError("desktop rejected Android generation") emit( @@ -209,8 +218,15 @@ def main() -> int: } publish_json(args.transport_root / "desktop-generation-v1.json", ref) if args.mode == "mtp": - run_adapter(args.mtp_adapter, "push", args.expected_peer, args.transport_root, deadline) - emit(args.run_id, "published", outbound_generation_id=outgoing_id, manifest_sha256=digest) + run_adapter( + args.mtp_adapter, "push", args.expected_peer, args.transport_root, deadline + ) + emit( + args.run_id, + "published", + outbound_generation_id=outgoing_id, + manifest_sha256=digest, + ) emit(args.run_id, "waiting_acknowledgement", outbound_generation_id=outgoing_id) android_ack = args.transport_root / "android-consumption-ack-v1.json" android_ack_value = wait_json(