Repository navigation
Add preset screenshot support - #49
tony-xmelon wants to merge 42 commits into
Conversation
…shot # Conflicts: # changelog.md
…-control # Conflicts: # changelog.md # docs/ADR.md # docs/STEERING.md
|
Synced with upstream �2331cc at head |
jonathanstokes
left a comment
There was a problem hiding this comment.
Thank you for coming back to this after the profile work landed.
Three things here are good and I want to keep them. The observations are specific: a bare Screenshot{READ} got no reply in twelve seconds, two slots returned different images, and the reply echoed the request id and the address. The name preset_screenshot is right, and you explained why. The hardware test checks that the loaded preset does not change. That is the most important thing to know about a message nobody had tried before. I want this in the library.
The multi-profile design did not exist when you wrote this PR. I merged it afterwards. So you could not have followed it. Anything below that looks wrong was the correct choice against the old code. That is my fault, not yours.
Two documents explain the new design. ADR-0020 is the decision. contributing.md has a section called "Adding a device profile". The short version: each client class is one device profile. QuadCortex is CorOS 4.0.1. Anything measured on another firmware belongs on that firmware's class.
My comments are at the lines they belong to. Two are marked critical. Two more points have no line in this diff:
docs/architecture.md lists Screenshot under "Types not in the registry at all". That section says nothing there has been seen on the wire. Your PR makes that false. Please remove Screenshot from the list.
Screenshot should also join _BY_NAME in pyquadcortex/protocol/registry.py. Today it works through the _auto_registered() fallback. That fallback exists so the RX thread can decode traffic. _BY_NAME is the table of message types this project has actually used. This one now belongs there.
What I will do: I will run pytest tests/hardware --hardware on my own 4.0.1 unit before I merge. I will write what I see next to your record, not over it. That run decides whether preset_screenshot can also live on the base class. Your 4.1.0 result stays as your evidence.
Last point. This is more work than you agreed to, and the reason is a design I merged under you. If you do not want to do it, say so. I will write the profile parts myself on top of your commits and keep you as the author. You can also close the PR. Either answer is fine.
Stokes
|
@jonathanstokes, thank you again for the detailed review. The profile migration and every requested inline change are complete at final head |
…-control # Conflicts: # tests/test_client.py
|
Synced with upstream main at 640a8e9 (including #63–#65). New head: e552790. Offline suite excluding the three #42-owned Windows portability checks: |
|
Direct HID validation on CorOS 4.1.0, exact PR head 8425260: the read-only test |
|
Correction and follow-up on the preset-screenshot hardware result: the earlier timeout was for slot 52, which is empty (the UI shows “Unsaved”). An occupied preset in the same setlist is the valid address. I changed the hardware test to prefer the loaded slot when it is occupied, otherwise select an existing stored slot without recalling it; it skips only if the setlist has no saved presets. On CorOS 4.1.0, direct HID, exact head 8abdbab: The initial empty-slot timeout is not evidence against the operation. The full hardware suite on the final head and the maintainer's requested 4.0.1 run are still outstanding; I have kept the PR draft and unsynced. |
|
@jonathanstokes The remaining base-profile/report issue is addressed at Full offline suite: 3,014 passed, 7 skipped; mypy and diff check are clean. QC Remote reports disconnected, so I have not attempted reconnect or claimed a fresh hardware run. The PR remains draft pending the promised 4.0.1 run and exact-head 4.1 validation. |
|
Status correction for the hardware note: QC is connected through the shared Rust broker. A read-only QC Remote |
|
Updated at 781d57d per the empty-slot screenshot feedback: the 4.1 profile now requests the addressed preset screenshot directly, without first reading the preset. If the request times out and that exact slot is already loaded, it returns a live 800×480 screen capture instead; it does not misreport the current screen for another slot. Verification: python -m pytest tests/test_client.py -q -k screenshot (20 passed), python -m mypy pyquadcortex (46 source files clean), and git diff --check (clean). A full-suite run in this local Python 3.13 environment had 101 unrelated import failures because an installed site-packages ests package shadows this checkout's ests directory; 2,913 passed and 7 skipped. No direct-HID test was run. |
|
Follow-up: reran the complete suite with the checkout's tests directory explicitly selected to avoid the local Python 3.13 site-packages name collision. Final result: 3,014 passed, 7 skipped in 38.63s. The earlier 101 import failures were solely that test-package resolution issue; no suite failures remain. |
|
At head d183668, I changed the loaded-slot path to capture the live display immediately instead of probing the addressed stored Screenshot and waiting for its timeout. That makes empty-slot capture independent of a saved preset. Requests for a different slot still use the addressed preset screenshot to avoid returning the wrong display. Focused: 17 passed; full suite: 3,014 passed, 7 skipped; mypy clean (46 files); diff check clean. No direct-HID hardware run is claimed. I will wait for exact-head CI and hardware evidence before requesting re-review. |
|
Exact-head hardware evidence is now in the description. On d183668, the direct-USB pyquadcortex test passed on CorOS 4.1.0 with the empty loaded slot: 1 passed, 0 failed/skipped; it returned the live 800x480 frame and confirmed the loaded folder/position/factory flag were unchanged. The normal ctypes hid package could not load its DLL here, so I injected the installed Windows Cython HIDAPI extension and ran the same protocol test over direct HID. The shared Rust broker is restored, and a fresh read confirms slot 52 remains empty and clean. This does not replace the requested 4.0.1 maintainer measurement. Please re-review the now-tested 4.1 behavior when convenient. |
…shot # Conflicts: # docs/ADR.md # docs/STEERING.md
|
Synced current upstream Validation on pushed head
CI is triggered by the push. I am keeping this draft pending exact CI and any remaining maintainer response. |
|
Thanks for the precise review. I have synchronized with current The full exact-head CorOS 4.1.0 hardware gate on I also confirmed My Presets 7E was empty and ran the exact-head screenshot hardware test against that loaded slot: 1/1 passed. It returned the live 800×480 framebuffer, with the slot address unchanged, rather than issuing a stored-preset screenshot request that would wait for a reply the empty slot cannot provide. The device is back on the original clean Fender Deluxe 37 at 1A, and the shared broker is ready. The two issues you called out on 4.0.1 are addressed in code: the base screenshot operation now uses the guarded support path, so |
|
Thanks for calling out the 4.0.1 profile guard. I fixed it on I also rechecked the empty-current-slot case: the loaded-slot |
|
I tightened the current-slot test to require an 800 x 480 live-screen PNG, then ran it on exact head |
|
I added separate hardware assertions for the two documented paths and completed them on exact head |
|
I ran the required full hardware suite on exact head The one failure is |
|
Follow-up: the sparse-preset run above was a coverage-precondition failure, not a protocol mismatch. I reran the complete suite on the same exact head with the saved high-coverage slot required by this test. Result: CorOS 4.1.0 / |
Summary
preset_screenshoton the base operation surface as a structured CorOS 4.0.1 refusalQuadCortex41and add it to that profile'sVERIFIEDset"28C"ScreenshotErrorfor missing PNG, invalid signature, or wrong echoed addressScreenshotand remove it from architecture's unobserved listTesting
Final head:
2be9d1c09a3f5d61d5c390160072ea3a27f8a85bpython -m pytest tests/test_client.py tests/test_profiles.py tests/test_session.py tests/test_registry.py tests/test_docs.py -q:635 passed, 1 skipped2708 passed, 3 skipped, 3 failed; the three failures are the known Windows path/line-ending fixtures fixed in Expose protocol operations and make the test suite portable #42python -m mypy: passedgit diff --check: passedCoverage pins base-profile refusal fields, the QuadCortex41 implementation, timeout forwarding, display-slot conversion, all three named reply failures, and profile guard state.
Hardware
Exact final head
55a0350, 2026-09-08, standard Quad Cortex on CorOS 4.1.0:The test addressed the loaded folder by display name, validated PNG signature, 800 x 384 IHDR dimensions and terminal IEND, and confirmed the loaded preset address did not change.
A maintainer with CorOS 4.0.1 should run the complete hardware suite before merge; that result determines whether the base refusal can later become an implementation.
Checklist
Upstream sync — 2026-09-12
Merged current upstream main at 640a8e9 (including merged PRs #63–#65) into this branch. Current head:
e552790Offline suite excluding the three #42-owned Windows portability checks:
2606 passed, 3 skipped; full run:2733 passed, 3 skipped, 3 known portability failures; mypy: clean across 46 source files;git diff --check: clean.The three excluded Windows checks are unrelated path-separator and CRLF-fixture assertions fixed within #42; they remain isolated there rather than being copied into this PR.
Upstream sync - 2026-09-19
Merged upstream main at
cfe39cc6592148d4196fa3028c2776f6618c41b6(including maintainer PRs #70-#75) and resolved overlaps in favor of the current upstream API and evidence model.Current head:
2be9d1c09a3f5d61d5c390160072ea3a27f8a85bFocused validation: 15 screenshot tests passed; writing-policy checks passed (18 passed, 1 skipped).
GitHub CI on this exact head is green: build + twine check, mypy, and tests on Python 3.11, 3.12, and 3.13 all pass.
No new maintainer comments were posted during this sync. This PR is ready for maintainer re-review; the displayed CHANGES_REQUESTED decision is the earlier review and has not yet been refreshed.
Review response (2026-09-26, head 8425260): preset_screenshot now uses the shared profile guard, so the hardware suite's Support.EXPERIMENTAL connection can exercise it on CorOS 4.0.1; default support still refuses this unmeasured base-profile operation. Restored the dated 4.1 evidence in the method and documentation, clarified folder_name as a display name, and removed the misleading architecture clause.
Offline validation on 8425260: 2,982 passed, 7 skipped; mypy clean across 46 files; git diff --check clean. Hardware remains pending on the final head; the connected QC Remote device session is currently disconnected. The PR remains draft and is not yet synced to main, per the requested order (hardware run first, sync last).
Maintainer follow-up — 2026-09-27
Current head:
3bbd8239866f2edb44410da361ebdb7906526b34.The 4.1 empty-loaded-slot path now uses
RemoteControlcapture when the preset has no name or its liveRecallPresetread times out; it does not issue the addressed stored-preset request in that case. The shared base implementation remains guarded:Support.EXPERIMENTALcan reach the 4.0.1 wire path for measurement, while the base profile reports the operation as unverified. The hardware reporter now removes guarded base methods fromEVERYTHINGbefore classifying candidates.Screenshotis in_BY_NAME, and the docs retain the dated address/name/evidence details.Validation: full offline suite 3,014 passed, 7 skipped; focused hardware-report/client/profile suite 469 passed; mypy clean (46 source files); diff check clean. No exact-head hardware run is claimed. QC is currently connected through the shared Rust broker. A read-only QC Remote
capture_screencall on the empty loaded slot returned an 800 x 480 PNG, corroborating that the live-screen route works independently of a stored preset; this is not a direct-HID pyquadcortex run. The 4.0.1 maintainer measurement and a fresh final-head 4.1 hardware run remain outstanding, and the shared broker was left running. PR remains draft.Maintainer follow-up — 2026-09-27 (empty-slot behavior)
Current head:
781d57d18e90b24c0062d0f13a914866850c95db.The 4.1 screenshot method now sends the addressed
Screenshotrequest directly and does not gate onread_current_preset()or a saved preset name. If that request times out for the exact slot currently loaded, it returns aRemoteControllive-screen capture; it still propagates a timeout for another slot so it cannot return the wrong screen. The fallback can therefore capture an empty/unsaved loaded slot without a preset-read timeout first.Validation on this head: full offline suite 3,014 passed / 7 skipped; focused screenshot tests 20 passed; mypy clean across 46 files;
git diff --checkclean. An exact-head CorOS 4.1 direct-HID run is now recorded below. The 4.0.1 maintainer measurement and re-review remain pending.Empty loaded-slot capture — 2026-09-27
At head
d18366824fab811a905e08854c749f2bafd1ad4f,preset_screenshotnow checks the live slot address first and directly captures the physical display when the requested slot is already loaded. It does not send an addressed stored-preset request first, so an empty loaded slot cannot cause that request to time out before the screenshot is taken. For a different slot, the stored-preset screenshot path remains in use to avoid returning the wrong screen.Validation: focused screenshot/loaded-slot tests 17 passed; full offline suite 3,014 passed, 7 skipped; mypy clean across 46 files;
git diff --checkclean. No direct-HID hardware run was performed. Waiting on exact-head CI and hardware evidence remains outstanding.Empty-slot hardware confirmation — 2026-09-27
Exact PR head
d18366824fab811a905e08854c749f2bafd1ad4f, connected Quad Cortex on CorOS 4.1.0, loaded empty slotMy Presets/7E(index 52), clean before the run:The test returned a valid 800 x 480 live-screen PNG and verified the loaded folder, position and factory flag were unchanged. The Python package's ctypes
hidbinding could not load a native DLL in this Windows environment; the run used the installed Windows Cython HIDAPI extension (hid.cp313-win_amd64.pyd) injected as thehidmodule, over direct USB HID. The shared Rust broker was restored after the test; a fresh state read still shows slot 52 clean and empty. This is direct-HID evidence for pyquadcortex on 4.1.0, not a 4.0.1 measurement.Current synced status — 2026-09-27 (head
dbfbb17)This supersedes the earlier head and validation above. Merged upstream
main(d8d40f3) indbfbb17, preserving both ADR records and their STEERING entries.3,020 passed, 7 skipped; exact-head CI run36289187395is green (build/twine, mypy, Python 3.11–3.13).2 passed(preset_screenshotandcapture_screen, 13.18s). The earlier empty-loaded-slot direct-HID check confirms that this case captures the live display instead of waiting on a stored image.The complete exact-head hardware result is now recorded below.
Final exact-head hardware result — 2026-09-27 (
dbfbb174)Full CorOS 4.1.0 gate,
pytest tests/hardware --hardware --profile QuadCortex41, run on the savedShine On Crazy Synthpreset after read-only catalog inspection showed 114 fixed-list positions:The full suite passed
capture_screenandpreset_screenshot; all 23 claimed operations passed. The 8 skips are profile/fixture preconditions, not operation failures. Exact-head CI run36289187395is green on build/twine, mypy, and Python 3.11–3.13; the offline suite is3,020 passed, 7 skipped.The empty-loaded-slot path was also exercised on this exact head. I confirmed My Presets slot 7E / index 52 was empty, loaded it without saving, and ran
tests/hardware/test_screenshot.py::test_current_preset_screenshot_has_the_observed_png_dimensionsover direct HID:It returned the live framebuffer without waiting for a stored preset image, and the test confirmed the loaded folder, position, and factory flag were unchanged. The device was restored to clean
Fender Deluxe 37at 1A and the shared broker is ready/synchronized. The Windows run used the compatible Cython HIDAPI extension because the pure-Pythonhidwrapper could not findhidapi.dll; this was only a test-environment workaround.This supersedes the earlier note that the full suite had not run on the synced head. The PR remains draft pending the maintainer's 4.0.1 run/re-review and the #44 profile sequencing.
Final exact-head recheck — 2026-09-27 (30facc6)
Fixed the hardware-test profile guard called out in review: it now requires
4.1.0inprofile.MEASURED_ONbefore checking operation membership. This avoids treating the 4.0.1Support.EVERYTHINGsentinel as evidence that the CorOS 4.1 screenshot operation is drivable.Validation on exact head
30facc61f018e01187696ff145e3db65ec0ab8cc:3,020 passed, 7 skipped in 42.05s.117 passed, 0 failed, 0 errored, 8 skipped in 291.42s; all 23 claimed operations passed with no operation failures or skips.36293799745: build/twine, mypy, and Python 3.11/3.12/3.13 all pass.Fender Deluxe 37at 1A; synchronized, master volume 97.The empty-current-slot test on 7E is still valid for the unchanged implementation: it returned the live 800 x 480 display and left the loaded address unchanged. The current-slot path does not request a stored preset PNG, so empty occupancy is not a reason to wait or time out.
Final exact-head capture verification — 2026-09-27 (5d5fff5)
The current-slot hardware assertion now requires the live-display dimensions
(800, 480), so a stored preset image(800, 384)cannot satisfy it. On exact head5d5fff5eb62447932e4cc6a11416fd67ddb2411b:1 passed in 12.51s; the loaded address remained 7E.117 passed, 0 failed, 0 errored, 8 skipped in 291.19s; all 23 claimed operations passed.3,020 passed, 7 skipped; mypy clean across 46 files.36294673750: build/twine, mypy, and Python 3.11/3.12/3.13 all pass.Fender Deluxe 37at 1A, synchronized, master volume 97.This directly demonstrates that an empty loaded slot does not make screenshot capture time out: the method uses the live screen and the test now requires its full-screen dimensions.
Exact-head verification of both screenshot paths — 2026-09-27 (ef7b5fe)
The hardware suite now separately exercises both contracts:
1 passed in 12.41s, address unchanged.1 passed in 22.36s, loaded address unchanged.On exact head
ef7b5fe943b5336687122c37bbfd3673778c8347, the complete CorOS 4.1.0 suite passed:118 passed, 0 failed, 0 errored, 8 skipped in 302.15s; 23/23 claimed operations passed, no operation-level failures/skips. Offline suite:3,020 passed, 7 skipped; mypy clean. Exact-head CI run36295204934is green for build/twine, mypy, and Python 3.11/3.12/3.13. QC was reconnected and restored to clean Fender Deluxe 37 at 1A, synchronized, master volume 97.This removes the earlier ambiguity: a screenshot of the physical display does not depend on any saved slot, while a request for a different occupied slot returns that slot's saved rendering.
Exact-head full-suite rerun — 2026-09-28
Reran
pytest tests/hardware --hardware -qon unchanged headef7b5fe943b5336687122c37bbfd3673778c8347against CorOS 4.1.0 /QuadCortex41, using the saved high-coverage preset at 12E required by the option-position test:118 passed, 0 failed, 0 errored, 8 skipped in 301.84s. All 23/23 claimed hardware operations passed; no operation-level failures or skips. The original active preset was restored cleanly; the shared broker is ready/synchronized and master volume remained unchanged at 100.