Skip to content

Post-Batch-1 cleanup: RS-5.8 size budget, F9 default flip, F10 stale-tile report - #88

Merged
dorkmo merged 4 commits into
mainfrom
post-batch1-cleanup
Aug 1, 2026
Merged

Post-Batch-1 cleanup: RS-5.8 size budget, F9 default flip, F10 stale-tile report#88
dorkmo merged 4 commits into
mainfrom
post-batch1-cleanup

Conversation

@dorkmo

@dorkmo dorkmo commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Three items queued behind the Batch 1 merge (PR #87), now that the on-air acceptance passed.

RS-5.8 — L072 size budget actually enforced (4f23abd)

The "L072 cross-compile" CI job has been red for months: check_size_budget.py still asserted the pre-unification flash schema (MM_BOOT_BASE/MM_BOOT_SIZE, removed by the flash-layout remediation), on top of the C-comment parse crash fixed earlier. Decision taken: under the unified layout the enforceable hazard is the app load image growing into the CFG region, so the budget is [MM_APP_BASE, MM_CFG_BASE) = 184 KB. The Makefile banner now states the same layout, one source of truth. Passes locally at 23,036 B / 184 KB — this PR should turn that job green for the first time.

F9 default flip (4f23abd)

HOST_ALLOW_REG_WRITE_DIAG now defaults 0 in config.h, per the plan on PR #87: the flip waited for the on-air soak, which passed 2026-07-30 (flag=0 build: arming + profile switch clean, forbidden-register writes rejected with ERR_PROTO, 360 s soak — bench-evidence/FW_BATCH1_acceptance_2026-07-30/RESULTS.md). Bench builds that need the diag register surface use make EXTRA_CFLAGS="-DHOST_ALLOW_REG_WRITE_DIAG=1".

F10 — 0x6C stale-tile report (cb71ee3)

The reassembly-timeout keyframe self-heal was measured net harmful (n=2/side: +1.88 pts fragment loss, −16 % frames delivered, +31 % timeouts — it amplifies the loss it fires on), but couldn't just be deleted: the base canvas is persistent, so a lost tile update displays stale imagery until the encoder happens to resend it, and only the receiver knows what arrived.

New CMD_OP_TILE_STALE (0x6C), base → tractor: u16le base_seq_ref + tile bitmap. 14 B body at the 12×8 grid ≈ 15.4 ms on air vs 1298.8 ms for the keyframe it replaces (84× cheaper). Advisory, level-triggered: a lost report is superseded by the next one.

Path: web_ui scans Canvas.arrived_ms every 3 s for tiles stale > 20 s (horizon must exceed the encoder's full sweep rotation, since a static-scene receiver cannot distinguish "static" from "lost" except via the sweep's periodic re-ship) → image_rx_daemon radiates one copy → image_tx_daemon → camera_service back-dates tile_last_seq past the escalation horizon, so the existing age-escalation sort front-runs marked tiles into the next scheduled frame at zero extra image airtime. The reassembly-timeout pokes are gated behind LIFETRAC_KF_ON_REASM_TIMEOUT (default off); keyframe requests remain for cold start / base_seq mismatch / mode change.

Env knobs: LIFETRAC_TILE_STALE_ENABLE (1), LIFETRAC_TILE_STALE_PERIOD_S (3.0), LIFETRAC_TILE_STALE_AFTER_MS (20000), LIFETRAC_KF_ON_REASM_TIMEOUT (0).

Verification

  • Base-station suite (CI invocation): 1089 passed, 2446 subtests, includes 10 new F10 tests (proto roundtrip, wire-cost pin, stale-scan semantics).
  • L072 firmware: builds clean at flag=0 default (38,558 B), all 27 bench check targets green (run on the RS-5.8/F9 commit).
  • F10 is not yet verified on air — queued for the next bench session: induce fragment loss, watch a marked tile re-ship without a keyframe.

🤖 Generated with Claude Code

dorkmo and others added 3 commits August 1, 2026 16:10
…yout; F9 diag default flipped to 0

RS-5.8: check_size_budget.py asserted the pre-unification schema —
MM_BOOT_BASE/MM_BOOT_SIZE, which no longer exist — so even after the C-comment
parse fix it could not run. Decision, taken from the header itself: under the
unified layout the real hazard is the app load image growing into the CFG
region, so the enforced budget is [MM_APP_BASE, MM_CFG_BASE) = 184 KB, not the
nominal MM_APP_SIZE (192 KB, which would silently admit a CFG collision). The
.cfg_reserved section is naturally excluded by ending the region at cfg_base.
RAM assertion unchanged. The Makefile banner ("BOOT = 4 KB / APP = 180 KB")
disagreed with the header and is reconciled to the unified truth. Local run:
PASS at 23,036 B / 184 KB. This was the last failure in the "L072
cross-compile" CI job, red for months — expected green after this commit.

F9 default flip: HOST_ALLOW_REG_WRITE_DIAG now defaults to 0. The soak the
2026-07-30 commit required has passed on air: a flag=0 build was flashed to
both boards, the wire probe confirmed production arming ACKs while 0x1D and
raw-TX 0x83 are FORBIDDEN, and a full 360 s FHSS run (1318 frames,
rx_decode_err=0) ran clean. Production builds now ship with the 13-register
diagnostic surface closed — including 0x1D/0x1E, the FCC airtime-invariant
bypass that was the reason the flag existed. Bench diagnostics (T2 TCXO,
SPI-isolation) build with EXTRA_CFLAGS="-DHOST_ALLOW_REG_WRITE_DIAG=1",
documented in config.h. NOTE: the currently-flashed bench boards run the
flag=1 build deliberately (they are the diagnostic bench); the flip changes
what a fresh production build ships with.

Firmware 38558 B; 27-target check green; size budget PASS.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…me self-heal

The reassembly-timeout keyframe request was measured net harmful
(n=2/side: +1.88 pts fragment loss, -16% frames delivered, +31%
timeouts — it amplifies the loss it fires on), but couldn't simply be
deleted: the base canvas is persistent, so a lost tile update displays
stale imagery until the encoder happens to resend it, and only the
receiver knows what arrived.

CMD_OP_TILE_STALE (0x6C), base -> tractor: u16le base_seq_ref + u8
bitmap[(n_tiles+7)/8]. 14 B body at the 12x8 grid = 15.4 ms on air vs
1298.8 ms for the keyframe it replaces (84x cheaper). Advisory dirty
marks, level-triggered: a lost report is superseded by the next one.

Path: web_ui scans Canvas.arrived_ms every LIFETRAC_TILE_STALE_PERIOD_S
(3 s) for tiles older than LIFETRAC_TILE_STALE_AFTER_MS (20 s — must
exceed the encoder's full sweep rotation, since a static-scene receiver
cannot distinguish "static" from "lost" except via the sweep's periodic
re-ship) -> MQTT -> image_rx_daemon radiates one copy -> image_tx_daemon
-> MQTT -> camera_service back-dates tile_last_seq past the escalation
horizon, so the existing age-escalation sort front-runs the marked tiles
into the next scheduled frame at zero extra image airtime. Keyframe
requests remain for cold start / base_seq mismatch / mode change only;
the reassembly-timeout pokes are now gated behind
LIFETRAC_KF_ON_REASM_TIMEOUT (default off).

Tests: proto roundtrip + wire-cost pin + base-side scan semantics
(10 new). Full suite 1089 passed / 2446 subtests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…, F10

Batch 1 section updated from "awaiting flash" to merged + accepted with
the five checklist items checked against
bench-evidence/FW_BATCH1_acceptance_2026-07-30/RESULTS.md. New
post-batch1-cleanup section records the RS-5.8 budget decision
([MM_APP_BASE, MM_CFG_BASE) = 184 KB), the F9 default flip, and the F10
stale-tile implementation with its open on-air verification item.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 1, 2026 21:22
web_ui connects to the MQTT broker at import time and raises after 30 s
if none is listening, so the bare import passed on the bench (local
mosquitto) but broke CI collection with ConnectionRefusedError. Use the
same mock-patch + importlib.reload pattern as the other web_ui tests,
plus their fastapi/paho skip guard for pure-firmware checkouts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Post–Batch-1 cleanup that (1) fixes the Murata L072 size-budget enforcement under the unified flash layout, (2) flips the diagnostic register-write gate default to production-safe, and (3) replaces the measured-harmful “keyframe on reassembly timeout” behavior with a low-airtime receiver-driven stale-tile report (0x6C) wired end-to-end.

Changes:

  • Re-anchor RS-5.8 flash budget enforcement to [MM_APP_BASE, MM_CFG_BASE) and align the Makefile “Region budgets” banner accordingly.
  • Flip HOST_ALLOW_REG_WRITE_DIAG default to 0 and update design/status documentation to reflect Batch 1 on-air acceptance.
  • Add CMD_OP_TILE_STALE (0x6C) protocol + base-side stale scanning and plumb the report through base → tractor → camera_service, with new unit tests.

Reviewed changes

Copilot reviewed 10 out of 14 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
LifeTrac-v25/DESIGN-CONTROLLER/TODO.md Updates Batch 1 status/acceptance notes and closes RS-5.8 item with decision context.
LifeTrac-v25/DESIGN-CONTROLLER/firmware/tractor_x8/image_tx_daemon.py Handles CMD_OP_TILE_STALE and forwards raw args to camera_service via MQTT.
LifeTrac-v25/DESIGN-CONTROLLER/firmware/tractor_x8/camera_service.py Applies stale-tile marks by back-dating tile_last_seq to front-run repairs.
LifeTrac-v25/DESIGN-CONTROLLER/firmware/murata_l072/tools/check_size_budget.py Fixes size-budget enforcement to end at MM_CFG_BASE under unified layout.
LifeTrac-v25/DESIGN-CONTROLLER/firmware/murata_l072/Makefile Updates printed region budget banner to match the unified-layout budget.
LifeTrac-v25/DESIGN-CONTROLLER/firmware/murata_l072/config.h Flips HOST_ALLOW_REG_WRITE_DIAG default to 0 with updated rationale/commentary.
LifeTrac-v25/DESIGN-CONTROLLER/firmware/murata_l072/build/firmware.map Updates the tracked map output to reflect the new build state.
LifeTrac-v25/DESIGN-CONTROLLER/base_station/web_ui.py Adds periodic stale-tile scanning and publishes packed stale reports to MQTT.
LifeTrac-v25/DESIGN-CONTROLLER/base_station/tests/test_tile_stale.py Adds protocol roundtrip + stale-scan semantic tests for F10.
LifeTrac-v25/DESIGN-CONTROLLER/base_station/lora_proto.py Defines CMD_OP_TILE_STALE and adds pack_tile_stale / parse_tile_stale.
LifeTrac-v25/DESIGN-CONTROLLER/base_station/image_rx_daemon.py Radiates CMD_OP_TILE_STALE one-shot commands and gates reasm-timeout keyframes behind env.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +24 to +26
from image_pipeline.canvas import Canvas # noqa: E402
from web_ui import compute_stale_tiles # noqa: E402

Comment on lines +1111 to +1115
body = pack_tile_stale(base_seq, stale, n_tiles)
mqtt_client.publish(TILE_STALE_TOPIC, body, qos=0)
except Exception:
# Advisory path: never let a transient error kill the worker.
pass
Comment on lines +1051 to +1063
def _on_tile_stale_msg(self, _client, _userdata, msg) -> None:
# F10: level-triggered advisory — radiate as a ONE-SHOT single
# copy. A lost report is superseded by the next periodic one, so
# no pending/retry machinery and no extra copies. Body arrives
# pre-packed from web_ui (u16le base_seq + bitmap).
body = bytes(msg.payload or b"")
if len(body) < 3 or len(body) > 200:
return
try:
self._ctrl_out.put_nowait(
pack_command_frame(CMD_OP_TILE_STALE, body))
except queue.Full:
pass
@dorkmo
dorkmo merged commit 0697e5d into main Aug 1, 2026
10 of 22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants