Skip to content

fix: partial-upload guards — MONO-only, 8-align, NACK fallback, etag commit (M1/M2/M10)#101

Merged
g4bri3lDev merged 1 commit into
OpenDisplay:mainfrom
balloob:fix/partial-upload-guards
Jul 5, 2026
Merged

fix: partial-upload guards — MONO-only, 8-align, NACK fallback, etag commit (M1/M2/M10)#101
g4bri3lDev merged 1 commit into
OpenDisplay:mainfrom
balloob:fix/partial-upload-guards

Conversation

@balloob

@balloob balloob commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Summary

Three related partial-update correctness fixes.

M1 — partial only where firmware allows it, and fall back instead of aborting (🟠)

Firmware only allows partial refresh when getBitsPerPixel() == 1 (MONO) and requires x/width multiples of 8. Previously BWRY/BWGBRY/GRAYSCALE_16 proceeded and aligned to 4/2 pixels, guaranteeing ERR_RECT_ALIGN/ERR_PARTIAL_UNSUPPORTED; and every non-etag NACK raised ProtocolError, losing the upload.

  • compute_partial_region now restricts to ColorScheme.MONO and aligns to 8 pixels.
  • Every pre-refresh 0x76/0x71 NACK is treated as fallback-to-full (firmware aborts the partial on such a NACK, so a full upload is safe).

M2 — don't commit an etag the device never stored (🟠)

The etag is only committed via END-with-etag, skipped when the firmware auto-completes. Previously PartialState.etag was populated unconditionally → uncompressed-only devices got permanent ERR_ETAG_MISMATCH fallback, and a stale etag could render against the wrong old plane. _execute_upload/_dispatch_upload now report whether the etag was committed; state stores it only then, otherwise invalidates so the next upload goes full.

M10 — respect the encrypted payload budget on partial START (🟠)

build_direct_write_partial_start now takes max_start_payload, threaded with ENCRYPTED_CHUNK_SIZE when a session is active (like the compressed START), instead of the 200-byte default that could overflow the ~185-byte encrypted envelope.

Test plan

  • uv run pytest -q → 443 passed (new + two updated tests)
  • ruff, mypy clean

🤖 Generated with Claude Code

@balloob balloob requested a review from g4bri3lDev as a code owner July 4, 2026 07:03
@codecov

codecov Bot commented Jul 4, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 86.66667% with 4 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/opendisplay/device.py 85.18% 4 Missing ⚠️

📢 Thoughts on this report? Let us know!

…commit (M1/M2/M10)

M1: firmware only allows partial refresh when getBitsPerPixel()==1 (MONO) and
requires x/width multiples of 8. Restrict compute_partial_region to MONO and
align to 8 pixels (BWRY/BWGBRY/GRAYSCALE_16 previously proceeded and aligned to
4/2, guaranteeing ERR_RECT_ALIGN/UNSUPPORTED). Treat every pre-refresh 0x76 and
0x71 NACK as fallback-to-full instead of raising ProtocolError and losing the
upload; the firmware aborts the partial on such a NACK, so a full upload is safe.

M2: the etag is only committed on the device via END-with-etag, which is skipped
when the firmware auto-completes the upload. Thread an "etag committed" result
out of _execute_upload/_dispatch_upload and only populate PartialState.etag when
it was committed; otherwise invalidate partial state so the next upload goes full
(prevents ERR_ETAG_MISMATCH churn and rendering against a stale old plane).

M10: encrypted partial START now caps its inline payload at ENCRYPTED_CHUNK_SIZE
(threaded max_start_payload), matching the compressed START, instead of the
200-byte default that could overflow the ~185-byte encrypted envelope.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JRrm95f1qNZzDM9r2SB6KW
@balloob balloob force-pushed the fix/partial-upload-guards branch from d8e5c81 to 63730ee Compare July 4, 2026 11:41
@g4bri3lDev g4bri3lDev merged commit ce931ed into OpenDisplay:main Jul 5, 2026
6 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