docs(sync): record native candidate evidence
This commit is contained in:
parent
2c5a8be282
commit
cc87f26b98
6 changed files with 89 additions and 19 deletions
|
|
@ -2,6 +2,12 @@
|
||||||
|
|
||||||
## Unreleased — 0.1.2
|
## 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,
|
- Normalized the recent synchronization, generation-MTP, Android backup,
|
||||||
Web Dashboard serialization, tests, I/O doubles and packaging code for human
|
Web Dashboard serialization, tests, I/O doubles and packaging code for human
|
||||||
readability. Added scoped C formatting rules and review guidance without
|
readability. Added scoped C formatting rules and review guidance without
|
||||||
|
|
|
||||||
|
|
@ -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
|
Physical-device MTP, release signing, real user backup/restore, main integration
|
||||||
and deployment are not complete.
|
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**.
|
Snapshot date: **2026-09-17**.
|
||||||
|
|
||||||
`TRAINLOG_CODE_READABILITY_V1` normalizes the recent generation-MTP,
|
`TRAINLOG_CODE_READABILITY_V1` normalizes the recent generation-MTP,
|
||||||
|
|
|
||||||
|
|
@ -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
|
This is a non-executed rollout checklist. The development branch implements the
|
||||||
path; the daily installation still uses V3.
|
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
|
## Compatible set
|
||||||
|
|
||||||
- desktop binary containing schema v21, the Web API and the generation worker;
|
- desktop binary containing schema v21, the Web API and the generation worker;
|
||||||
|
|
|
||||||
|
|
@ -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
|
backup tests, the I/O-boundary MTP browser proof, JSON/import validators and
|
||||||
the isolated harness cover the other normalized sources.
|
the isolated harness cover the other normalized sources.
|
||||||
|
|
||||||
GCC 16.2.1 now compiles beyond the former `misleading-indentation` failures in
|
GCC 16.2.1 and Clang 22.1.8 both pass separate strict C17 builds and the full
|
||||||
`json_writer.c`, `web_dashboard_json.c`, `web_dashboard.c`,
|
**81/81** Meson inventory with `werror=true` and warning level 3. The former GCC
|
||||||
`dashboard_layout.c`, and `generation_mtp.c`. The separate strict build remains
|
`format-truncation` findings are closed by exact checked path construction in
|
||||||
blocked by distinct pre-existing `format-truncation` diagnostics in the Web
|
the generation-MTP tests/double and by a production accepted-response
|
||||||
accepted-response buffer and bounded test path buffers. Warning policy remains
|
serializer that reports its byte count. Boundary tests cover exact fit,
|
||||||
unchanged; GCC is not reported as green.
|
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**
|
The readability closeout preserves the **81/81** normal Meson and **81/81**
|
||||||
ASan/UBSan inventories. The isolated Android run reports **220 tests: 215
|
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
|
JDK 17. The real Firefox/object-I/O-boundary MTP conversation also passes after
|
||||||
formatting. A source comparison against the pre-readability commit confirms
|
formatting. A source comparison against the pre-readability commit confirms
|
||||||
that the scoped test names and assertion counts are unchanged.
|
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.
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,9 @@ from pathlib import Path
|
||||||
ROOT = Path(__file__).resolve().parents[1]
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
BUILD = ROOT / os.environ.get("TRAINLOG_WEB_E2E_BUILD", "build") / "tui"
|
BUILD = ROOT / os.environ.get("TRAINLOG_WEB_E2E_BUILD", "build") / "tui"
|
||||||
GECKO = Path(
|
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()
|
transport.mkdir()
|
||||||
owned = root / "owned"
|
owned = root / "owned"
|
||||||
owned.mkdir()
|
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)
|
remote.mkdir(parents=True, exist_ok=True)
|
||||||
release = root / "release-android"
|
release = root / "release-android"
|
||||||
android_log = (root / "android.log").open("wb")
|
android_log = (root / "android.log").open("wb")
|
||||||
|
|
@ -140,11 +144,15 @@ class BrowserSyncTest(unittest.TestCase):
|
||||||
driver.get(f"http://127.0.0.1:{port}/")
|
driver.get(f"http://127.0.0.1:{port}/")
|
||||||
wait = WebDriverWait(driver, 15)
|
wait = WebDriverWait(driver, 15)
|
||||||
button = wait.until(
|
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())
|
wait.until(lambda _: button.is_enabled())
|
||||||
button.click()
|
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(
|
observed = await_condition(
|
||||||
lambda: (
|
lambda: (
|
||||||
json.loads(state_path.read_text())
|
json.loads(state_path.read_text())
|
||||||
|
|
@ -164,7 +172,10 @@ class BrowserSyncTest(unittest.TestCase):
|
||||||
self.assertEqual(committed["result"], "running")
|
self.assertEqual(committed["result"], "running")
|
||||||
with sqlite3.connect(xdg / "trainlog" / "trainlog.db") as database:
|
with sqlite3.connect(xdg / "trainlog" / "trainlog.db") as database:
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
database.execute("SELECT COUNT(*) FROM execution_drafts").fetchone()[0], 1
|
database.execute(
|
||||||
|
"SELECT COUNT(*) FROM execution_drafts"
|
||||||
|
).fetchone()[0],
|
||||||
|
1,
|
||||||
)
|
)
|
||||||
release.touch()
|
release.touch()
|
||||||
final = await_condition(
|
final = await_condition(
|
||||||
|
|
@ -186,16 +197,21 @@ class BrowserSyncTest(unittest.TestCase):
|
||||||
)
|
)
|
||||||
summary.click()
|
summary.click()
|
||||||
wait.until(
|
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()
|
driver.refresh()
|
||||||
wait.until(lambda d: "Synchronisation confirmée" in d.page_source)
|
wait.until(lambda d: "Synchronisation confirmée" in d.page_source)
|
||||||
state = json.loads(
|
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(state["phase"], "completed")
|
||||||
self.assertEqual(
|
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:
|
finally:
|
||||||
if driver is not None:
|
if driver is not None:
|
||||||
|
|
|
||||||
|
|
@ -139,7 +139,12 @@ def main() -> int:
|
||||||
if args.mtp_adapter is None:
|
if args.mtp_adapter is None:
|
||||||
raise RuntimeError("MTP adapter is required")
|
raise RuntimeError("MTP adapter is required")
|
||||||
pump = lambda: run_adapter(
|
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
|
# Android publishes its durable identity only while participating. A
|
||||||
# missing expected advertisement is therefore a bounded wait state;
|
# missing expected advertisement is therefore a bounded wait state;
|
||||||
|
|
@ -162,7 +167,9 @@ def main() -> int:
|
||||||
request_path = args.transport_root / "request-v1.json"
|
request_path = args.transport_root / "request-v1.json"
|
||||||
publish_json(request_path, request)
|
publish_json(request_path, request)
|
||||||
if args.mode == "mtp":
|
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(
|
emit(
|
||||||
args.run_id,
|
args.run_id,
|
||||||
"waiting_android_publication",
|
"waiting_android_publication",
|
||||||
|
|
@ -178,7 +185,9 @@ def main() -> int:
|
||||||
ack_path = args.transport_root / "desktop-consumption-ack-v1.json"
|
ack_path = args.transport_root / "desktop-consumption-ack-v1.json"
|
||||||
publish_json(ack_path, ack)
|
publish_json(ack_path, ack)
|
||||||
if args.mode == "mtp":
|
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":
|
if ack["result"] != "consumed":
|
||||||
raise RuntimeError("desktop rejected Android generation")
|
raise RuntimeError("desktop rejected Android generation")
|
||||||
emit(
|
emit(
|
||||||
|
|
@ -209,8 +218,15 @@ def main() -> int:
|
||||||
}
|
}
|
||||||
publish_json(args.transport_root / "desktop-generation-v1.json", ref)
|
publish_json(args.transport_root / "desktop-generation-v1.json", ref)
|
||||||
if args.mode == "mtp":
|
if args.mode == "mtp":
|
||||||
run_adapter(args.mtp_adapter, "push", args.expected_peer, args.transport_root, deadline)
|
run_adapter(
|
||||||
emit(args.run_id, "published", outbound_generation_id=outgoing_id, manifest_sha256=digest)
|
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)
|
emit(args.run_id, "waiting_acknowledgement", outbound_generation_id=outgoing_id)
|
||||||
android_ack = args.transport_root / "android-consumption-ack-v1.json"
|
android_ack = args.transport_root / "android-consumption-ack-v1.json"
|
||||||
android_ack_value = wait_json(
|
android_ack_value = wait_json(
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue