review: structured code review of PR #596 (feat: add PRT3 ASCII serial connection type)#597
review: structured code review of PR #596 (feat: add PRT3 ASCII serial connection type)#597Copilot wants to merge 1 commit into
Conversation
Agent-Logs-Url: https://github.com/ParadoxAlarmInterface/pai/sessions/94454ed5-3add-486f-ae3e-ad5f8295702c Co-authored-by: yozik04 <2420038+yozik04@users.noreply.github.com>
|
|
Thanks for the structured review — the multi-agent framing surfaced several things worth fixing. Before acting on the findings I traced each one against the code so we'd target the right things. Below is what I plan to act on, where I'd push back, and what I'd defer. Findings I'll act on
Findings whose impact I read differentlyBug #1 — For binary users only The architectural critique is solid though, and I'd happily take it further: register Bug #2 — The synthetic- That said, the suggested fix (skip replay when Bug #3 — severity scoping Real defect, agreed on the fix. The Bug #4 — severity scoping Real defect, agreed on the fix. The "crashing the run loop" framing isn't quite right: the MQTT path is wrapped by Minor #3 — The orchestrator catches it: DeferringArch #1 (LSP) and Arch #2 (string guards) — agreed in principle, both worth doing. The concrete behaviour issues they cite are mitigated by Minor #3 above, so I'd fold the cleanup into a follow-up rather than the v1 merge: extract Heads-upWhile integration-testing on a live EVO192 panel over the last day, three orthogonal arm/disarm state bugs surfaced and are fixed in
Architecture notes on the three-source state model (RA polling, G-events, command echo) added to |
- Bug ParadoxAlarmInterface#2: mqtt/core.py skip on_connect replay when _last_connect_args is None - Bug ParadoxAlarmInterface#3: panel.py _prt3_send_wait fast-fails on PRT3BufferFull instead of full timeout - Bug ParadoxAlarmInterface#4: PRT3_USER_CODE validated at config load; _build_partition_cmd catches ValueError - Minor ParadoxAlarmInterface#1: PRT3_SERIAL_BAUD set validator [9600, 19200]; config validator extended for int lists - Minor ParadoxAlarmInterface#2: G065 per-N overrides in EVENT_MAP number_overrides; from_prt3 uses generic lookup - Minor ParadoxAlarmInterface#4: handle_prt3_event_message catches expected exceptions explicitly - Minor ParadoxAlarmInterface#5: slice comment in retry warning; expanded PRT3_USER_CODE security doc - Minor ParadoxAlarmInterface#6: tests for buffer-full retry, malformed code, PRT3_USER_CODE/baud validation 1369 tests pass. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Fixed have been pushed to #596 per comments above. |



Purpose
This PR adds a formal structured code review of PR #596 (
feat: add PRT3 ASCII serial connection type) to the repository asdocs/pr596-prt3-review.md.The review was produced by a multi-agent analysis (advocate, sceptic, architect) examining the full diff (5,595 additions, 33 files).
Findings Summary for PR #596
🔴 Blocking Bugs (must fix before merge)
Bug #1 —
async_message_manager.py: Assertion removal weakens all callersassert isinstance(data, Container)inEventMessageHandler/ErrorMessageHandlerwas replaced withreturn False. This removes the contract enforcer for the entire binary-protocol pipeline — mis-routed objects now silently drop instead of crashing loudly. PRT3 dataclasses should travel through a dedicated handler, not through binary-protocol handlers with a softened guard.Bug #2 —
mqtt/core.py: Late-registraron_connectpassesNoneargs to all MQTT handlersWhen
_last_connect_argsisNone, every late-registering interface getsreason_code=None. Any handler callingreason_code.is_failurewill throwAttributeError. Only replayon_connectwhen_last_connect_args is not None.Bug #3 —
panel.py:PRT3BufferFull(!) has no fast-fail in_prt3_send_waitA panel buffer overflow emits
!(PRT3BufferFull). The_prt3_send_waitpredicate only matchesPRT3CommandEcho, so the overflow is ignored and the caller waits the fullIO_TIMEOUT. Duringload_labelswith default config (96 zones + 32 users + 8 areas = 136 polls), a single overflow causes a multi-minute startup freeze before failing._prt3_send_waitneeds a secondary predicate that returnsPRT3BufferFullimmediately.Bug #4 —
panel.py+paradox.py: InvalidPRT3_USER_CODEcauses unhandledValueErrorat arm timeencoder._validate_code()raisesValueErrorfor non-numeric or >6-digit codes. Neither_build_partition_cmd()norparadox.py'scontrol_partition()catches it. SettingPRT3_USER_CODE = "abc"and issuing a disarm command crashes the async task. ValidatePRT3_USER_CODEformat at config load time.🟡 Architecture Issues
Arch #1 —
PRT3Panel(Panel)violates Liskov Substitution PrinciplePanelcarries binary-protocol surface (_eeprom_*, binaryparse_message/get_message) that is dead weight in PRT3.control_zones()andcontrol_outputs()raiseNotImplementedErrorinstead of returningboolas the interface contract requires. Suggested fix: extract aBinaryPanelintermediate class.Arch #2 — 9
CONNECTION_TYPE == "PRT3"string guards erode Panel polymorphismThe existing codebase has zero
== "Serial"/== "IP"guards in the orchestrator. Five of the nine new guards are candidates for Panel interface methods (supports_time_sync,send_close_connection,register_protocol_handlers,get_utility_key_configs,send_utility_key) — reducingparadox.pyto the 3 genuinely structural connection-selection branches.Arch #3 —
runtime.pyplaceholder should be deletedEmpty file with "reserved for future" comment — delete until it has content.
Arch #4 —
mqtt/core.pylate-registrar fix should be a separate commitLegitimate general bug fix, unrelated to PRT3, cannot be cherry-picked as bundled.
🟢 Minor Issues
PRT3_SERIAL_BAUDrange(2400, 115200)accepts hardware-invalid values[9600, 19200]exit_delay,entry_delay) bypassEVENT_MAPcontrol_zones()/control_outputs()raise instead of returningFalseFalse+logger.warningexcept Exceptioninhandle_prt3_event_messagehides storage inconsistencyconnection.write()✅ Strengths
parser.py/encoder.py/adapter.pyseparationSerial/IPconnection typesPRT3_USER_CODEin confighandlers.pyAttributeErrorguard is elegantFull review:
docs/pr596-prt3-review.md