Skip to content

fix(radio): one PTT builder, and the four documented backends ARDOP silently dropped (#1258) - #1286

Merged
dc0sk merged 2 commits into
mainfrom
fix/1258-shared-ptt-builder
Sep 6, 2026
Merged

fix(radio): one PTT builder, and the four documented backends ARDOP silently dropped (#1258)#1286
dc0sk merged 2 commits into
mainfrom
fix/1258-shared-ptt-builder

Conversation

@dc0sk

@dc0sk dc0sk commented Sep 6, 2026

Copy link
Copy Markdown
Owner

Closes #1258.

The defect

ARDOP's build_ptt accepted vox, rigctld and none only. rts, dtr, cm108 and gpio — all
documented in [modem], captioned "Modem defaults shared by all TNC binaries", and all supported
by the daemon — fell through to NoOpPtt. An operator who validated ptt_backend = "cm108" against
the daemon and then started openpulse-tnc on the same config got an unkeyed rig.

My inventory was wrong twice, and the review caught both

I reported three builders and seven backends.

  • There are four. I missed the cross-band repeater's rig_b PTT (server.rs:330, rigctld-only,
    NoOpPtt on failure) because I grepped for build_ptt_controller and it carries no such name.
  • The CLI handles eight. It alone has a generic arm (GenericSerialCat,
    cfg(all(unix, feature = "generic-serial"))), absent from every other site and from its own
    --ptt help.

Committing a census-from-a-grep error while fixing a defect caused by duplication is the part worth
remembering.

openpulse-radio, not "reuse the daemon's"

Both issue bodies said reuse the daemon's builder; that is the wrong home. Verified there is no
layering problem provided it takes plain strings: openpulse-radio depends on neither
openpulse-config nor anything modem-side, and adding that edge to get a typed field would invert
the layering for no gain. Hence PttSpec, flattened, with the CLI's --rig overloading kept at its
own call site.

Zero #[cfg] in the builder — the non-obvious half

A #[cfg(feature = "serial")] inside a function living in openpulse-radio resolves against
radio's feature, which would put the switch in a crate whose feature every caller must forward.
Instead SerialRtsDtrPtt gained a feature-off open returning PttError::Config, mirroring
GpioPtt, and SerialPin became unconditional — it is a plain two-variant enum that never needed
the feature, and gating the type is exactly what made that open unwritable before.

So the not-compiled-in path is now ordinary code that runs, and is testable, in the
--no-default-features gate.

Measured: cargo check -p openpulse-kiss --features serial failed with "the package does not
contain this feature"
before this change and succeeds after. Forwarding is necessary and not
sufficient
— no documented build recipe names serial or gpio for any binary, the daemon
included, so those backends are inert on every shipped recipe. Recorded, not fixed here.

PttError::Config

New variant, so "this backend cannot be honoured" (unknown name, feature absent, missing device
path) is distinguishable from "the backend is real and the attempt failed". That distinction is
what #1285 needs to decide whether to refuse startup. Additive — no crate outside openpulse-radio
matches PttError exhaustively.

Semantics preserved exactly — and a test caught me failing to

The daemon's "none" arm returned Some(NoOpPtt), not None, and SharedPtt is handed the
result, so the two are not interchangeable. My first adapter passed the builder's Ok(None) straight
through and none_and_vox_build_a_controller failed — which is exactly what that test is for. A
deduplication must not quietly alter a caller's contract.

Tests

Four in ptt_builder, and the second is the gate:

  • only "none" yields Ok(None);
  • the four backends ARDOP dropped are recognised — asserted as "not reported as unknown", so it
    tests the defect rather than requiring the hardware;
  • an unknown name is a Config error that names the alternatives;
  • rts with no device path is a config error, not an open attempt on "".

Sabotage: restoring the pre-fix coverage fails it with `rts` is a documented backend and must not be reported as unknown — that is the #1258 defect: it fell through to NoOpPtt. Restored by
sha256sum.

GATE: PASS 322f8b16ab9c30198cc24cbfb610602386a04085 clean 20260906T163740Z
        suites=324 tests_passed=2462 tests_failed=0

The first gate run on this branch failed with TRACE: FAIL — NEW-ORPHAN on the new
ptt_builder.rs — 2462 tests passing and the gate still red, because a new production file that no
requirement claims is the membership hole #1268 closed. Claimed under CAP-74.

Doc bug, by omission

ptt_backend's doc listed six of seven backends — gpio was missing. Traced: #875 wrote that line
for cm108; #876 added gpio, touched 15 files, and never touched openpulse-config. Fixed, with
ptt_device's missing chip:line spec and a (vox/rts/dtr) aside.

Split out

#1285 — the daemon's fail-open on an unknown backend, with the maintainer's ruling: refuse to
start on a config error, keep warn-and-continue for a connect failure. Deliberately not in this
PR. #1259 (KISS) builds on this branch.

🤖 Generated with Claude Code

https://claude.ai/code/session_0188ATCj6DZ9aRVQ2vSirua6

dc0sk and others added 2 commits September 5, 2026 21:06
…ilently dropped (#1258)

ARDOP's build_ptt accepted vox, rigctld and none only, so rts, dtr, cm108 and gpio — all documented
in [modem], captioned "shared by all TNC binaries", and all supported by the daemon — fell through
to NoOpPtt. An operator who validated ptt_backend = "cm108" against the daemon and then started the
TNC on the same config got an unkeyed rig.

My inventory was wrong twice and the review caught both. I reported three builders and seven
backends; there are FOUR (I missed the cross-band repeater's rig_b PTT, which carries no
`build_ptt_controller` name to grep for) and the CLI handles EIGHT (it alone has a `generic` arm,
absent from its own --ptt help). Committing a census-from-a-grep error while fixing a defect caused
by duplication is the part worth remembering.

The builder goes in openpulse-radio, not "reuse the daemon's" as both issues said. Verified no
layering problem provided it takes plain strings: openpulse-radio depends on neither
openpulse-config nor anything modem-side, and adding that edge for a typed field would invert the
layering for no gain.

Zero #[cfg] in the builder, which is the non-obvious half: a #[cfg(feature = "serial")] inside a
function living in openpulse-radio resolves against RADIO's feature, putting the switch in a crate
whose feature every caller must forward. Instead SerialRtsDtrPtt gained a feature-off `open`
returning PttError::Config, matching GpioPtt, and SerialPin became unconditional — gating the type
is what made that `open` unwritable before. `cargo check -p openpulse-kiss --features serial`
failed with "the package does not contain this feature" before and succeeds now.

PttError::Config added so "cannot honour this backend" is distinguishable from "the backend is real
and the attempt failed" — the distinction #1285 needs.

Semantics preserved exactly, and a test caught me failing to: the daemon's "none" arm returned
Some(NoOpPtt), not None, and SharedPtt is handed the result. My first adapter passed Ok(None)
straight through and none_and_vox_build_a_controller failed, which is what that test is for.

Results: 62 radio tests pass; the four-backend test FAILS against the restored pre-fix coverage,
restored by sha256sum. Daemon ptt_selector 4/4, ARDOP green, clippy clean, REACH: PASS.

Doc bug fixed by the way: ptt_backend listed six of seven backends. #875 wrote that line for cm108;
#876 added gpio, touched 15 files, and never touched openpulse-config.

Implements: REQ-PTT-01

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0188ATCj6DZ9aRVQ2vSirua6
TRACE: FAIL — NEW-ORPHAN on crates/openpulse-radio/src/ptt_builder.rs, a new production file no
capability claimed. CAP-74 is 'PTT backends', which is what it is.

Note the shape: 2462 tests passed and the gate still failed. A new production file that no
requirement claims is the membership hole #1268 was about, caught on the first file added after
that landed.

Implements: REQ-PTT-01

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0188ATCj6DZ9aRVQ2vSirua6
@dc0sk
dc0sk merged commit d25b350 into main Sep 6, 2026
7 of 8 checks passed
@dc0sk
dc0sk deleted the fix/1258-shared-ptt-builder branch September 6, 2026 17:23
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.

ARDOP build_ptt silently degrades rts/dtr/cm108/gpio to NoOpPtt while the config documents them as shared

1 participant