Hardware-found: toggling two device rows' Active and refreshing showed only the
second toggle landed. The 2026-06-12 capture explains it - 16 burst channel
writes drew only 2 Z1 ACKs; the station processes one inbound frame at a time
and silently drops the rest. The legacy never burst: MakeCommand only queued,
and each inbound Z popped exactly one command (Main.frm SendCommand).
The controller now queues commands and keeps one in flight: Z1 confirms and
advances; Z0 retransmits; a Z2 idle while unconfirmed means the frame was
dropped, so it retransmits (all queued commands are idempotent - this improves
on the legacy, which lost silent drops), capped at 3 transmissions then dropped
with errorOccurred. Link frames (data-frame ACKs, idles) bypass the queue. The
queue is cleared on stop() so a closed port cannot wedge it; a refresh queued
behind writes re-arms the load-settle timer at transmit time.
pendingWritesChanged(depth) drives a new footer WORKING dot - the modern
version of the legacy "Working n" status caption, per user request: lit while
settings changes await the station's acknowledgement.
The simulator now Z1-ACKs every command frame like the real station (applying
the command BEFORE acking, so synchronous pumping cannot reorder same-field
writes) - its perfect burst handling is exactly why this bug was sim-invisible.
CIM_DA07_CAPTURE now also records outbound frames ("> "-prefixed) so the next
hardware session sees both sides of the handshake.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Found on real hardware (first DA-07 + one pod): every edit reverted within
seconds once the STATUS column populated. The DA-07 never echoes a settings
write back (the legacy VB6 grid WAS the model and kept the edited cell), but
the controller only updated its models from inbound frames - so the periodic
~H/~G/~F frames rebuilt the tabs from the stale model and reverted the edit.
The simulator masked it: it only sends ~H during a refresh, never periodically.
Every controller set_* now applies the value to its local model right after
sending and emits the matching *Changed signal (set_channel_active also emits
devicesChanged for the Devices-tab roll-up; remove_device drops the device and
its channels locally). The station stays the source of truth - the next
Refresh overwrites local state with whatever it actually stored.
Hardware findings are now logged in docs/DA07-FIELD-NOTES.md (newest first),
cross-linked from HARDWARE-VERIFICATION.md and CLAUDE.md. Open follow-ups
recorded there: confirm writes survive a Refresh on hardware, and check
whether DA-12 has the same latent bug.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Inno Setup script hardcoded AppVersion '0.1.0' - a stale second source of truth that would ship the wrong version in Add/Remove Programs. It now takes the version at compile time via /DAppVersion (0.0.0-dev sentinel if omitted), and the README/CLAUDE.md build commands derive it from cim_suite.__version__. Also documents the full release sequence and notes-before-bump ordering in the README.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Conventional-Commit-driven version bumps (scripts/bump.py), a commit-msg validation hook, and a /release-notes workflow that produces a human-reviewed CHANGELOG.md. Adds an in-app version badge + 'What's new' dialog on the launcher. The version is single-sourced from pyproject.toml (cim_suite.__version__), and the deterministic bump backbone lives in scripts/release/ with tests in tests/release/. Marks the 1.0.0 baseline.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The DA-07 rebuild loaded nothing from real hardware: unlike the DA-12 (which
streams its reply unsolicited), a real DA-07 runs a polled handshake -- it sends
a refresh one frame per ACK and interleaves Z2 idle polls, advancing only when
the tool answers both. The rebuild sent "A" once and answered nothing, so the
station stopped after frame one. The simulator hid this by dumping every frame
at once.
Fix (hardware-verified on a real DA-07, COM5):
- controller._process now ACKs (Z1) every inbound data frame to pull the next.
- controller._handle_poll answers the station's Z2 idle polls (and Z0 NAK ->
resend). Idle-polling turned out to be required for the initial load, not just
live mode -- an ACK-only build deadlocked partway through the stream.
- SimulatedStation models the frame-per-ACK stream (handshake=True); the
frame-content round-trip tests opt out with handshake=False.
A full Refresh now loads config + 45 device types + 28 settings + present
devices + channels (~120 frames over ~20s) where the broken build loaded 1.
Loading overlay (requested): a blocking, dimmed, gradient-blue panel covers the
DA-07 window during the multi-second refresh and clears when it completes.
- New reusable core/ui/loading_overlay.py (LoadingOverlay + animated gradient bar,
colors from theme tokens) and a SCRIM token + QSS rules.
- Controller emits loadStarted/loadProgress/loadFinished; completion is inferred
by a 1.5s idle-settle timer (no end marker on the wire; 1.5s clears the real
~0.55s max inter-frame gaps with margin). Verified: clears 1.5s after the last
frame, no premature clear.
233 tests pass (new test_handshake.py, test_loading.py), ruff clean. Docs updated
(CLAUDE.md, HARDWARE-VERIFICATION.md, BACKLOG.md, REBUILD-STATUS.md).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Python rebuild was promoted to main on 2026-06-02; main is no longer the
VB6 baseline (that lives per-module under cim_suite/modules/<id>/legacy/).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Update all five named files to reflect the completed suite-monorepo reshape:
run/entry commands point at cim_suite.shell.app; architecture tables and paths
updated to cim_suite/core + cim_suite/modules/da12 + cim_suite/shell; SUITE-
ARCHITECTURE.md status changed from "not yet built" to done with actual layout
and updated Module contract; REBUILD-STATUS.md records 88 tests and Phase 5;
BACKLOG.md marks reshape done and adds BL-S1/S2/S3 follow-ups including the
config/data-path migration caveat; transport __init__ docstring points to
cim_suite.core.transport.serial_transport for the real transport.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Establish the suite-wide visual design system as a self-contained theme
layer and apply it across the DA-12 module.
- theme package (da12_service/ui/theme): tokens -> fonts -> stylesheet ->
apply_theme; no app dependencies, shaped to move into core/ui/theme.
- Palette/fonts derived from the SmartScan brand: azure #0096FF accent on
cool-slate surfaces, Lato bundled (OFL) and loaded at runtime.
- Applied app-wide: branded toolbar header + primary Connect action,
underline tab bar, banded gridless tables, themed inputs/buttons/dialogs,
status-bar connection pill, primary-button variants.
- Alarm row colors now come from SEVERITY tokens (alarms red / warnings
amber) instead of hardcoded pastels.
- Packaging ships fonts in both the PyInstaller spec and the wheel.
Documented as the binding suite standard in docs/DESIGN-SYSTEM.md (rules +
per-module adoption checklist), cross-linked from SUITE-ARCHITECTURE.md and
CLAUDE.md. Backlog: BL-3 DONE, BL-4 (dark variant) and BL-5 logged.
ruff clean; 73 tests pass; app boots with --simulate.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>