Skip to content

feat: promote Expert 2K-FA Third Series fan profile - #34

Draft
FtlC-ian wants to merge 4 commits into
mainfrom
feat/issue-5-third-series-production
Draft

feat: promote Expert 2K-FA Third Series fan profile#34
FtlC-ian wants to merge 4 commits into
mainfrom
feat/issue-5-third-series-production

Conversation

@FtlC-ian

@FtlC-ian FtlC-ian commented Aug 24, 2026

Copy link
Copy Markdown
Owner

Summary

  • promotes the Expert 2K-FA Third Series fan transaction from D1 report 00acd527…b715
  • binds production to model EXPERT 2K-FA, the exact Third Series topology, and the operator-declared actual firmware allowlist Rel.26_03_24_A or Rel.08_06_26_A
  • maps hardware QUIET to logical Normal and hardware NORMAL to logical high cooling (contest)
  • preserves all 23 distinct raw states from the original Rel.26_03_24_A reversible report with machine-readable provenance
  • records that Rel.08_06_26_A has a separate read-only confirmation of the unchanged setup grid and FAN NOISE cursor topology, without claiming a second reversible report
  • fails closed for empty, mistyped, case-variant, or other firmware values
  • enforces fresh checksum-valid STANDBY/RX evidence and rejects ambiguity, replay, reconnect, TX, OPERATE, or identity mismatch
  • never sends DISPLAY or OPERATE/STANDBY for Third Series

Verification

  • go test ./...
  • go test -race ./internal/fanpolicy ./internal/server
  • go vet ./...
  • targeted fan-policy tests for both accepted firmware values and rejection cases
  • OpenAPI JSON validation and private/public parity checks
  • independent exact-SHA safety review approved c6bee25

Merge gate

Draft only. Do not merge until Justin validates the actual production path on Rel.08_06_26_A while the amplifier remains in STANDBY/RX. OPERATE-to-STANDBY automation remains explicitly out of scope.

Related to #5.

@w9fyi

w9fyi commented Aug 26, 2026

Copy link
Copy Markdown

Attempted the hardware checklist — blocked before step 2, but found the likely root cause

Built exact commit c6bee25896aefdfa85487067a6addfc1be7d549c on the Pi and worked through the checklist. Config: fanPolicyFirmwareVersion: Rel.08_06_26_A, automaticFanPolicyEnabled: false, no manual override, overtemperatureStandbyArmed: false. Amp confirmed EXPERT 2K-FA, STANDBY, tx: false throughout. (I also cherry-picked my not-yet-upstream local read-watchdog patch onto this build purely to rule out a read-hang — it doesn't touch anything this PR changes, and turned out not to be the issue; noting it for transparency.)

What blocked step 2

POST /api/v1/fan-policy/override {"mode":"normal"} was accepted but sat at state: unavailable, reason: "recent protocol-native temperature status is unavailable". CurrentProtocolNativeWithContact()'s 5-second freshness window (internal/runtime/status_state.go) essentially never opens on this rig: the amp only ever emits a genuine AA AA AA 43 ... status-format frame as a spontaneous reaction to a real physical OPERATE/STANDBY toggle, never as an answer to the server's own periodic 55 55 55 01 90 90 status-poll write. In a clean 15s wire capture with the poll write firing every ~250ms as expected, 0 of the 5 frames that came back from the amp were status-format — all 5 were 371-byte display frames.

Once I did toggle OPERATE→STANDBY physically, the queued override did fire, navigated set → right → right → right, and correctly failed closed when the manual toggle invalidated the STANDBY/RX precondition mid-navigation — that part worked exactly as designed. But the failure left navigation.mayBeInMenu: true, and POST /api/v1/fan-policy/recover requires that same protocol-native freshness to clear the latch — which is just as unreachable, so recovery couldn't be confirmed through the API. I verified the amp was actually back home via GET /api/v1/display/text directly instead (screen text matched the standby splash, no highlighted selection).

Root cause

Compared against the LAN-unit's own web client (/script.js). It never interleaves status and display polling — "TERM" (display mirror) mode only ever sends the display-poll command on its own 250ms loop, and status/dashboard data lives in a separate mode that fully tears down and reconnects the WebSocket (wsClient.disconnect()) before switching over. The two are mutually exclusive in the vendor's own client, and its button-send logic is notably more conservative than ours (queues ≤4 keys, waits for a frame boundary, won't send mid-frame, 120ms pacing).

Tested the hypothesis live: with pollingMode still both, a 15s capture showed 5 display frames. Switching to pollingMode: display only, the same 15s window showed 77 display frames (~250ms cadence, matching the vendor client) with zero status-poll writes going out. I then manually replayed the exact same navigation profile this PR encodes (set, then right toward FAN NOISE) via raw POST /api/v1/actions/button, verifying via display/text after each press — under both mode presses had batched unpredictably (one gap swallowed 4 presses and only resolved as a delayed burst, overshooting the intended stop). Under display-only, three consecutive presses each landed correctly within 1 second, no drops, no overshoot.

Conclusion

I don't think this is a bug in this PR's navigation/fan-policy logic — the profile itself (expert-2k-fa-third-series-fan-normal-quiet-v1, set → right ×N) drove correctly step-by-step when I replayed it manually. The blocker is that pollingMode: both appears to make the amp/LAN-unit link unreliable in a way the vendor's own client avoids entirely by never mixing status and display polling, which in turn means the fan-policy override's protocol-native freshness gate can't realistically be satisfied on this rig as currently configured. This may be the same class of problem as #14 ("rate-aware polling").

Given display-only mode has no protocol-native telemetry at all, it doesn't unblock the checklist as designed either — just isolates the cause. Happy to re-run the full checklist once there's a polling strategy that doesn't interleave status and display in the same session, or to help test one. Let me know how you'd like to proceed — the amp is currently back at STANDBY/home, tx: false, no changes made to the physical amp configuration.

73,
Justin — AI5OS

@w9fyi

w9fyi commented Aug 26, 2026

Copy link
Copy Markdown

Follow-up: checked the manuals — nothing missed, they actually confirm the diagnosis

Wanted to rule out a documented handshake, session setup, or rate limit before assuming the polling-interleave theory from my last comment was right. Pulled both official docs fresh from spetlc.com:

Application Programmer's Guide (the documented STATUS (0x90) telemetry command): described as a simple, stateless request/reply — "can be requested several times every second," no handshake, no session setup, nothing about exclusivity with anything else. If anything this says our ~250ms status-poll rate should be completely fine on its own.

Main user manual, §15.1 Remote Control: describes SPE's own official app (Term_2K_USB/Term_2K_232) — and it is only a display + keyboard mirror. No telemetry, no STATUS string, nothing. It's covered in a completely separate section from the Programmer's Guide's STATUS command, for what reads as a completely separate use case.

Put together: the display-mirror protocol (the AA AA AA .. /371-byte frames, 0x80 poll) isn't in the Programmer's Guide at all — it belongs entirely to Term_2K's world, which the manual never describes running alongside the STATUS telemetry command. Nowhere in either document is there any mention of using both in the same session. That lines up exactly with what the SPE-LAN-UNIT's own web client does in practice (my previous comment) — it doesn't compromise between the two, it treats them as genuinely separate modes and tears down/reconnects when switching.

So this looks like a real firmware/protocol limitation rather than a client-side rate or ordering bug expert-amp-server could tune its way around within a single combined-polling session — SPE's own software never attempts that combination at all, in either direction. A strategy that cleanly time-slices between pure-status and pure-display windows (rather than interleaving individual polls) might still work, since that's closer to what the vendor's own client does, but true simultaneous status+display polling in one session may just not be something the amp's firmware supports.

Manuals used (fetched fresh):

  • https://www.spetlc.com/images/download/SPE_Application_Programmers_Guide.pdf
  • https://www.spetlc.com/images/download/2K-FA/EXPERT2K-FA-EN-rev.3.0.pdf

73,
Justin — AI5OS

@w9fyi

w9fyi commented Aug 26, 2026

Copy link
Copy Markdown

Correcting my earlier diagnosis, and the real blocker

My two comments above blamed pollingMode: both interleaving status and display polls. That was wrong, and I'm retracting it. I re-ran everything with tcpdump on the LAN unit's TCP link so the evidence is independent of this server's own decoders, and the picture is both simpler and worse.

0x90 is never answered on this transport

Not slow, not intermittent — never.

condition status polls sent status frames back ACKs back
both, 4 min continuous 670 0 0
status only, no display polls at all 106 0 0
fresh LAN-unit session, different transport 84 0 0

Across every capture, every AA AA AA sequence in the amp→host stream was a display-frame header. Not one AA AA AA 43 status frame, and not one ACK, in any run.

Interleaving is not the cause. In both mode the display path is completely healthy — 109 display polls produced 113 display frames, each arriving 66 ms after its poll, which is exactly 371 bytes of wire time at 57600 baud. Display polling works fine alongside status polling. Status polling simply returns nothing.

The amp is reachable — it's 0x90 specifically

Worth being precise, because it rules out the obvious explanations:

  • host→amp works. A serial OPERATE (0x0D) toggled the amplifier from STANDBY to OPERATE and back, confirmed physically. Commands arrive and execute.
  • the amp does emit ACKs. I captured AA AA AA 01 0C 0C on the wire — the documented ACK for a DISPLAY keystroke.
  • amp→host works. Display frames flow continuously at ~5.4/s.

So the link is bidirectional and the amp's command parser is reachable. 0x90 alone produces neither the documented STATUS string nor an ACK.

I checked the Programmer's Guide for a missed precondition and there isn't one: §5 documents 55 55 55 01 90 90 exactly as implemented here, describes it as stateless, and says it "can be requested several times every second". No handshake, no session setup.

What that does to this PR's checklist

Both entry points depend on protocol-native status, so both fail closed. Run on exact SHA c6bee25896aefdfa85487067a6addfc1be7d549c, amplifier in STANDBY/RX, automaticFanPolicyEnabled: false, no manual override, overtemperature standby disarmed, fanPolicyFirmwareVersion: Rel.08_06_26_A:

Step 2 — POST /api/v1/fan-policy/override {"mode":"normal"}HTTP 202

state:   unavailable
reason:  recent protocol-native temperature status is unavailable
recentContact: false    provenance: display-frame
supportedModes: []      actionAvailable: false

Menu Debug armHTTP 409 "fresh protocol status is required"

standby                          false   <- needs protocol-native status
rx                               false   <- needs protocol-native status
recentContact                    false   <- needs protocol-native status
standbyHome                      true
automaticFanPolicyDisabled       true
overtemperatureStandbyDisarmed   true
actuationLeaseAvailable          true

Four of seven prerequisites pass. The three that fail are exactly the three that require a 0x90 reply — while standbyHome being true shows the server can see the amplifier sitting on its standby home screen the whole time.

Both refusals sent zero bytes to the amplifier. The wire during each attempt shows only display polls: 180 polls / 176 frames for the override, 143 / 139 for the arm. The fail-closed behaviour is working exactly as designed — I want to be clear that nothing in this PR's navigation logic misbehaved. It never got the chance to run.

Two client-side findings, unrelated to the above

1. The poll scheduler can silently stop sending one poll type entirely. In serialPollScheduler.advance(), both mode alternates on halfInterval, and the catch-up loop flips phase once per skipped slot. When the read loop settles into a cadence that is an even multiple of halfInterval — which happens naturally when reads time out at serialReadTimeoutMs — the phase returns to the same value every iteration and locks there.

I observed it lock in both directions on different sessions:

session A:  109 display polls,  59 status polls   (drifting)
session B:  143 display polls,   0 status polls   (locked to display)
session C:    3 display polls,  84 status polls   (locked to status)

Session C is the damaging one: display updates dropped to ~3 per 21 s because the entire poll budget went to a command that never answers. This is independent of the amplifier — it's purely the scheduler's arithmetic.

2. A button write landing right after a 0x90 appears to stall the link for ~12.8 s. In six presses under both mode I saw three stalls, and the split was clean: all three stalls were preceded by a 0x90, all three clean presses by a 0x80. Each stall ended precisely when the delayed ACK arrived (silence 24.604 → 37.346 s, ACK at 37.346 s, frames resumed immediately). Two presses under display mode produced no stall.

I'm flagging this one as provisional. A browser tab was open on the LAN unit's own web UI during part of that testing, which puts a second client on the box, and I can't fully exclude its influence. The adjacency correlation is not something a second client would produce, but it deserves a clean re-run before you act on it. If it holds, it would explain the press batching I described in my first comment far better than my interleaving theory did.

I also previously believed the LAN unit free-runs its own display broadcast at 317 ms. That was an artifact of that same open browser tab and I retract it too — with polling off and no other client, the box sends only a null keepalive every ~5 s.

Where this leaves things

I don't think this is a defect in PR #34. The profile, the gates and the fail-closed behaviour all did the right thing. The problem is that the reviewed production path requires protocol-native status within a 5 s window, and on this hardware path that window can never open.

Some options, in the order I'd weigh them:

  1. Allow display-derived STANDBY/RX evidence for this profile. The checksum-valid LCD flag word decodes tx, operate, set and tune reliably, standbyHome already passes, and the Third Series profile is display-verified at every step anyway. This is the smallest change that makes the path reachable.
  2. Treat "status poll unanswered" as a first-class transport state rather than as stale telemetry, so the server can say so plainly instead of reporting a freshness failure that looks like a timing problem.
  3. Leave it blocked and document that this transport can't run the production fan path. Defensible, but it means the feature is unreachable for anyone on an SPE-LAN-UNIT.

Happy to test whichever direction you prefer, and happy to run the clean re-test of finding 2 on request.

One transport note for context: this amplifier now reaches the server through an SPE-LAN-UNIT over its virtual-COM port rather than direct USB. Status telemetry did work here before 2026-08-18, but the transport moved to the LAN unit and the amplifier was flashed to Rel.08_06_26_A on the same day, so I can't attribute the change to either one from software alone. Separating them needs a direct-USB A/B, which I'll run when I can and report separately.

73,
Justin — AI5OS

@FtlC-ian

FtlC-ian commented Aug 26, 2026

Copy link
Copy Markdown
Owner Author

Thanks for the careful correction and packet captures. The evidence separates two issues cleanly.

The scheduler starvation is a real client bug and is being fixed independently under #38. That fix preserves bounded one-poll dispatch and fair status/display alternation after delayed reads; it does not weaken any PR #34 safety gate.

PR #34 remains draft and blocked. We will not accept display-derived STANDBY/RX as a substitute for protocol-native status based only on the SPE-LAN-UNIT path. Please run the direct-USB versus LAN-unit 0x90 A/B when practical, without attempting the fan transaction. For each transport, a bounded status-only capture reporting status polls sent, valid status frames received, ACKs, firmware, baud, and transport is enough. Please also keep your local read-watchdog patch out of the eventual production-path validation build unless it lands and is reviewed separately.

The scheduler fix is now merged under #39. This branch will remain unrefreshed and blocked until the transport evidence supports a safe reachable gate; only then will it be brought onto current main, re-reviewed, and given a new hardware checklist.

@w9fyi

w9fyi commented Aug 26, 2026

Copy link
Copy Markdown

Direct-USB vs SPE-LAN-UNIT 0x90 A/B test — results

Ran both legs as bounded, status-only captures (pollingMode: status, display polling off), same amplifier, same firmware, same session. Built from clean main at 3b60dc6 with no local patches (read-watchdog excluded per your request) for the LAN-unit leg; the USB leg used a standalone raw-serial probe independent of expert-amp-server's own decoder entirely, so neither leg depends on server-side parsing.

Amplifier: EXPERT 2K-FA, firmware Rel.08_06_26_A, baud 57600 throughout. Amp confirmed STANDBY, tx: false before and after each leg; no button presses sent in either leg.

SPE-LAN-UNIT (WiFi, 192.168.50.116:7388)

  • Capture: 64.4s, tcpdump on the Pi's eth0, filtered to the LAN unit's host:port — independent of expert-amp-server's decoder.
  • Status polls sent (55 55 55 01 90 90): 258
  • Valid status frames received (AA AA AA 43…): 0
  • ACKs (AA AA AA 01 xx xx): 0

New finding vs. my earlier report: this is not silence. 200 of 258 polls (~78%) got a reply, arriving 100–350ms later with normal request/response timing — but every reply is a fixed 6-byte payload of all zeros (00 00 00 00 00 00), not the documented STATUS string and not the documented ACK format. My earlier informal captures apparently missed this because I was only checking for the AA AA AA prefix. So the correct characterization is "answered with a malformed/sentinel reply," not "command ignored."

Direct USB (/dev/ttyUSB1, FTDI AB0PKCHL)

  • Capture: 64.4s, standalone Python/pyserial probe sending only the documented 0x90 poll at ~250ms cadence, logging every raw byte with a timestamp. No DTR/RTS assertion (parity with serialAssertDTR/RTS: false).
  • Status polls sent: 258
  • Valid status frames received: 258/258 — 100%
  • Round-trip latency: ~50ms, consistent every time.
  • Sample decoded reply: ,20K,S,R,x,2,01,2a,0r,M,0000, 0.00, 0.00, 0.0, 0.0, 86, 84, 84,N,N,i\r,\r\n — matches the documented format exactly (model 20K, Standby, temps 86/84/84°F, consistent with known idle readings).

Conclusion

Same amp, same firmware, same baud — only the transport changed, and the result flips from 0/258 to 258/258. This isolates the cause: it's the SPE-LAN-UNIT, not the Rel.08_06_26_A firmware. The LAN unit's virtual-COM bridge either mishandles 0x90 specifically or substitutes a canned/error response rather than passing it through to the amp's UART — the amp itself answers correctly and immediately when reached directly.

Given this, I'd guess the LAN unit's own firmware (confirmed Espressif/ESP32, app version 1.0.3, all under a different vendor's control than yours) doesn't forward the STATUS command class, or has a bug specific to it, while DISPLAY/keystroke commands pass through fine (matches everything I've seen elsewhere — display polling and button presses both work normally over this same link). This looks like an SPE-LAN-UNIT firmware issue outside expert-amp-server's control, not something to work around on the client side.

Happy to send this upstream to SPE/the LAN-unit vendor if that's useful, or to re-run either leg with different parameters if you want additional data before deciding how PR #34 should proceed.

73,
Justin — AI5OS

@w9fyi

w9fyi commented Aug 26, 2026

Copy link
Copy Markdown

Retracting the "0x90-then-button stalls ~12.8s" finding — clean re-run, no other client, 0/40 stalls

Follow-up to my provisional note above. Re-ran it properly this time: single exclusive raw TCP client to the LAN unit's port 7388 (expert-amp-server stopped, no browser tab open anywhere, nothing else touching the box), 40 trials total — 20x 0x90 (status poll) → 50ms gap → DISPLAY key (0x0C), 20x 0x80 (display poll) → same gap → DISPLAY key, trial order randomized. Independent tcpdump running the whole time for confirmation. Amp stayed STANDBY, tx: false, throughout; DISPLAY is inert to RF/PA/mode.

Result: 0/40 stalls. Max gap observed was 0.49s, nothing remotely close to 12.8s. The original finding doesn't reproduce — it was the open browser tab I flagged as an uncontrolled variable, not a real bug. Please disregard that part of my earlier comment.

One smaller pattern survived the clean re-run and might be worth a note: replies following a 0x90 poll were consistently slower (mean ~0.40s) than replies following a 0x80 poll (mean ~0.11s), and in every 0x90→DISPLAY trial, only the DISPLAY key's own ACK (AA AA AA 01 0C 0C) ever came back — the 0x90 poll's own malformed all-zero reply (from my A/B test above) never showed up at all, not even in a 1.5s post-trial drain. So 0x90 still seems to consume some processing time on the LAN unit's side even though it doesn't cause a lasting stall. Minor, not blocking anything, just flagging in case it's a useful data point for whoever ends up debugging the LAN unit's 0x90 handling.

Side note for anyone else testing on this hardware: I'd assumed DISPLAY was side-effect-free since it doesn't touch RF/PA, but it's actually a real front-panel button that cycles a 3-screen info carousel (Standby → SN:xxxx → CAL:000 → repeat). 40 rapid presses left my amp's LCD sitting on the SN screen afterward — harmless, but not literally a no-op. Cycled it back to Standby manually after the test.

@FtlC-ian

Copy link
Copy Markdown
Owner Author

Thanks again for isolating this so cleanly. The direct-USB A/B gives us enough evidence to keep PR #34's safety gate intact and treat the LAN unit as the transport limitation.

A few broader questions, since you may be the first person to run expert-amp-server through an SPE-LAN-UNIT in earnest:

  1. What motivated you to put the LAN unit between the amplifier and the Pi/server instead of continuing with direct USB? Is there a production advantage in your station, or were you primarily evaluating the newly released unit?
  2. What is your overall impression of the LAN unit and its web software? Are there useful capabilities or interaction patterns it has that you think expert-amp-server should consider adding?
  3. Have you found any indication that its firmware is actively maintained or field-updatable? Since you already offered, would you be willing to send the vendor/SPE the bounded 0x90 A/B results and let us know what they say? The key evidence is simply that the same amp and firmware returned 258/258 valid status frames over direct USB and 0/258 through LAN-unit app version 1.0.3, with malformed six-zero-byte replies on the latter.

No need for more amplifier testing from our side right now. We are not going to weaken the protocol-native STANDBY/RX requirement to accommodate this transport.

@w9fyi

w9fyi commented Aug 27, 2026 via email

Copy link
Copy Markdown

@FtlC-ian

Copy link
Copy Markdown
Owner Author

Justin, I do not think there is one magic bullet, but aggressive common-mode choking made the biggest difference in my shack. Every HF feed line has four to six mix-31 ferrites between the grounded window-entry panel and either my MFJ-998 tuner or the amp. I keep those inside coax runs short and direct, with all coax shields bonded at the entry panel. I also use high-power 1.5–3 kW common-mode chokes outside at the antenna feed points.

Before doing that, I had certain band and antenna combinations where transmitting more than about 15 watts would make speakers buzz somewhere in the house. The additional choking drastically reduced the stray RF, although it is not quite perfect. I still need to finish bonding the radio, tuner, and amp chassis to the station ground bus, and I have not tested every band with every antenna combination yet.

For comparison, my ANAN G2 is connected to the SPE Expert amp with an ordinary six-foot USB-A-to-USB-B printer cable. It is nothing special and has been completely reliable. I put a couple of generic snap-on ferrites on it, but I do not even know what mix they are.

For your USB issue, I would try a shorter, well-shielded cable and known mix-31 snap-ons near both ends. If the cores are large enough, pass the cable through them multiple times. I would not simply coil excess USB cable into an ugly balun without a suitable ferrite core because the choking impedance would be unpredictable.

My overall recommendation is known mix-31 choking on the feed lines at the shack entrance, a properly rated choke at each antenna feed point, short inside coax runs, and good single-point chassis bonding. Then test each band and antenna combination individually. At QRO, especially with multiple home-built or experimental antennas, RF control is an ongoing battle. When common-mode RF gets into the shack, practically every USB, Ethernet, audio, power, and control cable can become an antenna.

73,
Ian

@w9fyi

w9fyi commented Aug 30, 2026

Copy link
Copy Markdown

The firmware pin now excludes the only 2K-FA on the bench — and the page it protects is byte-identical across both firmwares

I went to capture a fresh menu report for you and found I can't run one at all. Worth reporting, along with the evidence I gathered instead.

The blocker

internal/menudebug/controller.go:800 pins the Third Series profile to an exact firmware:

"expert-2k-fa-third-series-fan-normal-quiet-v1": {
    capability: CapabilityFan, model: "EXPERT 2K-FA", firmware: "Rel.26_03_24_A",
},

and :806 refuses anything else:

if profile.firmware != "" && profile.firmware != strings.TrimSpace(p.ExpectedFirmware) {
    return errors.New("plan does not use the exact reviewed firmware profile")
}

I flashed this amplifier to Rel.08_06_26_A on 2026-08-18, so the session arms and then fails at plan validation with the message from internal/server/menu_debug.go:1235. The only other reviewed profile is expert-1.3k-fa-first-series-bank-ab-v1, a different model — so there is currently no menu-report capability this amplifier can run.

I want to be clear that I am not asking you to relax a safety gate on my say-so, and I did not work around it. firmwareVersion on the arm request is caller-supplied, so I could have declared the old string and sailed through. Doing that would defeat the exact thing the gate exists for — guaranteeing the reviewed button sequence matches the firmware it was reviewed against, on hardware where a wrong press is real RF.

The evidence, since I couldn't run your workflow

Instead I re-ran my own guarded read-only capture of the Set-mode sub-pages, the same one I used for the 2026-08-13 baseline: only items whose legend reads [SET]:CONFIRM are entered, nothing is pressed inside a sub-page except the exit its own legend indicates, and the standby splash is verified against the baseline after every item. Amplifier in STANDBY, not transmitting, 32.8 °C throughout. Nothing was changed.

Comparing raw cell bytes rather than decoded text, across Rel.26_03_24_ARel.08_06_26_A:

sub-page raw grid identical
CONFIG yes
CAT yes
DISPLAY yes
RX ANT yes
FAN NOISE yes
ANTENNA row 6 only
TUN ANT row 6 only

The two exceptions differ only in row 6, the dynamic context-help line, and only because a different band/antenna is selected today than in August — SET 2nd ANTENNA ON 80m BAND vs SET 1st ANTENNA ON 20m BAND. Not firmware.

FAN NOISE — the page this PR's profile actually drives — is byte-for-byte identical:

            POWER-SUPPLY FAN

   [ ] QUIET  MODE (SSB ONLY)
   [✓] NORMAL MODE (ALL MODES)   SAVE


    ALWAYS SPINNING WHILE IN OPERATE
 [◄▲][▼►]:SELECT          [SET]:CONFIRM

Same title, same two options in the same order, same SAVE, same legend. Row 3's raw cells are [96, 96, 96, 59, 174, 61, 96, 46] on both firmwares.

One thing that initially looked like a change was not. The decoded text differs between my two captures because your glyph decoder improved between v0.4.6+pr26 and v0.4.7: 0xAE now renders as instead of [[AE]], and the BACKLIGHT/CONTRAST bar glyphs 0x920x9E now render as bars instead of hex escapes. Nice improvement, and it briefly fooled me — the underlying cells are unchanged. Worth knowing if you ever diff decoded captures across server versions.

What I'd suggest, entirely your call

The pin is doing real work for firmwares that genuinely move the menu. But as written it excludes the only 2K-FA available to test this PR, to protect a page that measurably did not change. Some options:

  1. Widen the profile to accept both Rel.26_03_24_A and Rel.08_06_26_A, on the strength of the byte-level comparison above.
  2. Keep the pin and add Rel.08_06_26_A as a separately reviewed profile — happy to capture whatever additional evidence you want first.
  3. Leave it as is, and this PR stays unrunnable on my hardware. Defensible; I just wanted you to know that is the current state rather than have it look like I stopped testing.

Separately, and unrelated to the pin: I filed #44 for an ABBA deadlock I reproduced on clean main, which wedges all actuation including overtemperature safety. Menu debug is one of the three participants, so it is worth a look before anyone runs more menu-debug sessions.

Captures are in my own repo and I can attach the raw state.json files, the decoded text, or the PNG renders in whatever form is most useful.

73,
Justin — AI5OS

@w9fyi

w9fyi commented Aug 30, 2026

Copy link
Copy Markdown

Captures are public now if you want to look at the raw evidence rather than take my word for the byte comparison:

https://github.com/w9fyi/expert-amp-a11y/tree/main/captures — commit 5315ed8.

Each sub-page directory has state.json (raw cell values, which is what I diffed), decoded.txt, and a PNG render. The pre-flash Rel.26_03_24_A versions are the parent commit 51a4fc9, so git diff 51a4fc9..5315ed8 -- captures/ shows exactly what I described — and is also a neat illustration of the decoder-vs-firmware trap, since the decoded text moves while the cells do not.

menu-report-third-series-2026-08-13.json in the repo root is the last full menu report the workflow produced, on the old firmware.

73,
Justin — AI5OS

@FtlC-ian

FtlC-ian commented Sep 1, 2026

Copy link
Copy Markdown
Owner Author

I reviewed the proposed Rel.08_06_26_A binding and the current branch shape.

The implementation is appropriately narrow: it adds the newer firmware as a second exact, case-sensitive allowlisted value for the same model and captured Third Series topology. Empty, mistyped, case-variant, and unlisted values remain blocked before any amplifier command. The original Rel.26_03_24_A 23-state reversible report also keeps its original provenance; the newer observation is not represented as a second apply/restore report.

The raw-evidence review is blocked at the moment. https://github.com/w9fyi/expert-amp-a11y currently returns not found, and neither exact commit 51a4fc9 nor 5315ed8 is resolvable through GitHub. Please restore access or attach the old/new raw state.json files for the top setup grid and FAN NOISE page. Once they are available, I will compare the cell and attribute arrays directly, rather than decoded text or PNGs, then refresh this conflicting branch onto current main and re-run the safety review.

No additional amplifier transaction is requested for this evidence check.

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