docs: name the model in every serial example now that the factory refuses to guess - #3416
Conversation
…uses to guess Owner ruling R59 (#3411) removes the serial backend's implicit IC-7610 default, so a documented serial invocation with no --model will exit 1 once that lands. Every serial example in docs/ now names its radio. The LAN examples in README.md and the CLI parser epilog were already broken before R59: LAN discovery returns only a host, and IcomRadio's resolve_radio_profile() raises without a model or radio_addr. Verified: create_radio(LanBackendConfig(host=...)) with no model raises ValueError on this branch's base (695e56d). Those two now pass --model. profiles/LAYER.md described the resolution precedence as ending in an "IC-7610 default"; resolve_radio_profile's docstring says it raises. No behaviour change — the epilog is a help string. Every rewritten invocation was parse-checked against _build_parser(). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…stake hint
Two blocks of the previous commit's class survived in files it touched.
README's library quickstart built LanBackendConfig(host=, username=,
password=) with no model. Executed verbatim on this branch, the
construction raised ValueError ("Cannot resolve a radio profile"), six
lines below the shell line the previous commit fixed. It now passes
model="IC-7610"; the construction was executed again from the fixed
README and returns an IcomRadio.
_print_common_cli_hint is the parser epilog's twin: three distinct
invocations printed to a user who has just made a mistake. Each was run
through cli.main's _run with LAN discovery stubbed to return a host and
create_radio wrapped to stop the run on success: all three now get past
create_radio, and all three fail with the resolve error once --model is
removed. Its sentence claimed 'web' can auto-discover the radio; the
narrower claim is that discovery finds the IP and the model must be
named.
profiles/LAYER.md said the resolve_radio_profile docstring documents a
precedence order ranking profile against model. It does not: it says a
RadioProfile or a non-blank profile/model name is a caller override that
wins over radio_addr. LAYER.md now says that.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Agent Review: BLOCKED 47176af Head confirmed equal to the PR head: BLOCKING: the sweep missed two copyable documented serial examples (the PR's central claim)The PR claims "every documented serial invocation now names the model" / "names the radio in all of them". Two complete, copyable Python serial invocations still name no model, and both break when the sibling #3411 lands — including one in a file this PR edits.
config = SerialBackendConfig(
device="/dev/cu.usbserial-111120",
baudrate=115200,
radio_addr=0x98,
rx_device="IC-7610 USB Audio",
tx_device="IC-7610 USB Audio",
)
radio = create_radio(config)
serial_config = SerialBackendConfig(
device="/dev/cu.usbserial-111120",
baudrate=115200,
)
radio = create_radio(serial_config)At this head both still silently build the removed default — exactly what R59/#3411 deletes: And #3411's factory hunk raises for any absent/blank So the The same block at Abbreviated siblings ( What checked out1. Merge brought only this PR's change. 2. CLI serial class complete. After joining backslash continuations, a docs/README sweep for No model env var, config-file 3. Edited commands work. Printed epilog and both hint branches now carry The ic7300 example's parse failure is pre-existing and correctly reported — 4. The three LAN cases were already broken on 5. 6. Code/tests/lint. 7. Guardrails. First body line states the count; head is exactly 10 files ( 8. CI as found. REQUIRED BEFORE MERGE (prose/body only)
|
The ic7610-usb-setup.md Python factory example and the radio-protocol.md LanBackendConfig/SerialBackendConfig factory example constructed backend configs without model=; create_radio will refuse a SerialBackendConfig with no model once the sibling validation lands, so every documented construction now names IC-7610 explicitly. The two low-baud scope fragments in ic7610-usb-setup.md and troubleshooting.md name the model as well. Part of MOR-2425 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Agent Review: BLOCKED c407b90 Head confirmed: BLOCKING:
|
A keyword argument followed by the ... placeholder is a SyntaxError (positional argument follows keyword argument). Move the placeholder to the front of the four serial config snippets and the LAN migration snippet so every abbreviated call compiles. Part of MOR-2425 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Agent Review: PASS 23ac194 Head confirmed: 1. The syntax regression is fixed; I reproduce the 24/0 sweepCompiling the exact lines read from the files (not retyped): Independent sweep over 2. The fifth site
3. Prose still matches
4. The rewritten correction section: every number verified
5. Counts and ceiling
6. CI as found
REQUIRED BEFORE MERGE (prose only)
|
PR #3411 removed the factory's implicit radio guess, and PR #3416 fixed the serial examples; the LAN Python examples were left behind, so a reader copying one hit "Cannot resolve a radio profile" at create_radio. A CI-V address is a factory default the operator can change, so it does not identify a radio either: every runnable LanBackendConfig construction in docs/ and README.md now names its model (README already did). Each distinct rewritten shape was executed through create_radio and returns an IcomRadio; the pre-fix shapes were executed and raise ValueError. Two preset examples pass radio_addr from get_civ_addr() deliberately and are unchanged. docs/api/rigctld.md and docs/guide/ic7610-usb-setup.md remain for a second PR (10-file ceiling). Part of MOR-2425 Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2 code (cli hint + its test) + 8 docs = 10 files, at the hard file ceiling and not over it (the coordinating session records this under the owner's 2026-09-03 delegation: every documented serial invocation and the CLI's own printed examples are one change)
Why
Owner ruling R59 (PR #3411) removes the serial backend's implicit
IC-7610default:
create_radioon aSerialBackendConfigwith no model refusesinstead of guessing. Every serial invocation documented without
--modeltherefore stops working the moment #3411 lands. This PR names the radio in all
of them, ahead of that merge.
Three already-broken LAN cases are fixed in the same pass, because they are the
same defect — a documented invocation that identifies no radio:
README.md'sshell quickstart,
README.md's Python quickstart, and the CLI parser's ownprinted examples (
_build_parser's epilog and_print_common_cli_hint).Those never worked on the LAN path either. Measured on this branch's base
(
695e56d87),create_radio(LanBackendConfig(host=…, username=…, password=…))with no
model=:_auto_discover_lan(cli/__init__.py) returns only the host IP — it neversupplies a model — and
_runcatches thatValueErrorand returns 1.Grep, before and after
Both numbers re-derived at head
3b724c15a(the "before" viagit grepat themerge base):
695e56d87): 37 linesdocs/guide/ic705-usb-setup.md(147, 151, 239, 260)Those four are false positives of a line-based grep, not defects: each is the
first line of a backslash-continued invocation whose
--model IC-705sits onthe next line (read at head). Left untouched deliberately — see "Not fixed".
Files
docs/guide/cli.md--model IC-7610docs/guide/configuration.mddocs/guide/ic7300-usb-setup.mdaudio bridgeexample gains--model IC-7300docs/guide/ic7610-usb-setup.mddocs/guide/troubleshooting.mddocs/radio-protocol.mdREADME.md--model IC-7610; the Python quickstart'sLanBackendConfig(...)gainsmodel="IC-7610"src/rigplane/cli/__init__.py_build_parser's epilog and_print_common_cli_hint: the printedweb/serveexamples gain--model IC-7610, and the hint's sentence narrows from "webcan auto-discover the radio" to discovery finding the IP with the model still named. Both are help/hint strings; no behaviourtests/test_cli.pyTestPasswordResolution::test_discover_web_common_mistake_gets_human_hintasserts the new hint stringsrc/rigplane/profiles/LAYER.mdresolve_radio_profiledocstring documents a precedence order ranking profile against model. It does not: it says aRadioProfileor a non-blank profile/model name is a caller override that wins overradio_addr, and thatValueErroris raised when none of the three identifies the radio. The bullet now says thatVerification
form starts with
rigplane, joined across backslash continuations);33 parse under
_build_parser().parse_args(...). The one failure isdocs/guide/ic7300-usb-setup.md:174, for a pre-existing reason this PR doesnot introduce: the global
--serial-portsits after the subcommand, andaudio bridgehas no--loopback. Each cause was confirmed on its own —rigplane audio bridge --serial-port Xandrigplane audio bridge --loopback Yeach give
error: unrecognized arguments— andgit show origin/main:docs/guide/ic7300-usb-setup.mdshows the base line already hadboth.
_print_common_cli_hintprints wasscraped from the hint's own stderr (not transcribed) and driven through
cli._run, with_auto_discover_lanstubbed to return a host andcreate_radiowrapped to stop the run the moment it returns. All three getpast
create_radio. Control: the same three with--modelremoved eachreturn 1 with the resolve
ValueError.create_radio(LanBackendConfig(...))call wasextracted from the file and executed — construction only, nothing connects:
it returns an
IcomRadio. The same extraction against the pre-fix blockraised the resolve
ValueError.uv run pytest tests/test_cli.py tests/test_cli_model.py tests/test_cli_coverage.py -q --tb=short --timeout=300 --timeout-method=thread— 245 passed, 0 failed.
uv run ruff check src/ tests/— All checks passed.uv run ruff format --check src/ tests/— 742 files already formatted.tests/test_cli.py:470fails (1 failed, 244 passed). (b)model="IC-7610"removed from the README block → the extraction check raises the resolve
ValueError. Both reversed;git diffbefore the final gate run showed onlythe intended change.
(
.github/workflows/doc-citation-gate.yml) triggers only on changes to.github/scripts/check-doc-citations.shor to itself, and this PR touchesneither. The script would not run locally either — it uses
declare -Aandthis machine has bash 3.2. Verified by hand instead:
git diff 695e56d87...HEAD -- '*.md'adds no relative markdown link and nofile:linecitation, so neither gate's input changed.Not fixed — reported instead
The earlier sweep of the class "documented
rigplaneinvocation thatidentifies no radio" was markdown-only. Re-run over Python sources, the
class has these members in
src/rigplane/cli/__init__.py:_print_common_cli_hint— fixed in this PR, see above.Usage:block at the top ofthe file): 17 lines, every one
modelless (
rigplane status [--host HOST] …). Not fixed — it belongs withthe LAN-docs follow-up (T213). Two of its forms were measured as real
subprocesses at head:
rigplane --host <ip> statusandrigplane --host <ip> audio rx --out … --seconds 1each exit 1 with theresolve error.
_warn_web_host_ambiguityquotes three forms in its warning text.As subprocesses, the two it recommends (
rigplane web --radio-host <ip>,rigplane --host <ip> web) exit 1 with the resolve error; the third is theform it warns against, and on this machine it fails earlier, at LAN
discovery. Same follow-up.
Example: rigplane audio …strings in theaudiosubparserdescriptions (
rx,tx,loopback,bridge) are modelless. Samefollow-up.
Not a member: the epilog's
rigplane discoverline needs no model —discoveris dispatched in
main()and never reaches_run, socreate_radiois nevercalled.
Markdown members remaining:
--model/--radio-addr.git grep -nE '^\s*rigplane ' -- '*.md', minus lines matching--modelor--radio-addr, minus the commands that never reachcreate_radio(
discover,proxy,diagnose,validate,radio-validate,convert,--list-audio-devices,--version,--help,audio caps,audio probe-profile), leaves 147 lines across 19 files, 104 of them indocs/guide/cli.md. That is a line count — a backslash-continuedinvocation counts once, on its first line — and I did not execute them.
Ticket T213.
docs/PROJECT.md— the 2026-03-23 architecture note describesIC-7610 → Icom7610SerialRadio (default); the parenthetical goes stale withfix: refuse to build a serial radio without an explicit model #3411.
docs/guide/ic705-usb-setup.mdCLI section — broken for an unrelatedreason: its examples use
--device,--baudrate,--rx-audio-device,--tx-audio-deviceand the subcommandsreplandrigctld, none of whichthe parser defines. Different class, left untouched — which is also why the
four residual grep lines above stay.
docs/guide/ic7300-usb-setup.md:174— see Verification. The model fixis correct and independent of the two parse defects, and is kept.
Different class, reported only:
src/rigplane/profiles/LAYER.mdsays"adding a new radio is a TOML file plus zero Python changes" (Charter) and
"No Python changes required" (Common operations, "Add a new radio"). That is
false for
serial.
backends/factory.pydispatches on five model names and raisesotherwise;
rigs/x6100.tomlandrigs/tx500.tomlexist with no branch.Measured:
Guardrails
Census at
3b724c15a0dcf525bf01874a1cd5c224a0db5321(
git diff --numstat origin/main...HEAD): 10 files, 64+/57- = 121 changedlines.
Over the 6-file soft threshold and at the 10-file hard ceiling; under both line
limits. One unit of work: a single mechanical substitution driven by one owner
ruling, applied to every place the removed default was relied on, plus the two
blocks of the same class that review found in files this PR had already
touched.
internal-identifier self-check — empty.
Linear: MOR-2425
🤖 Generated with Claude Code
Correction after review (
c407b902)The first review found the sweep incomplete:
docs/guide/ic7610-usb-setup.mdanddocs/radio-protocol.mdstill built aSerialBackendConfigwith no model, and both would have broken the moment #3411 lands.c407b902namesIC-7610in each — the radio both documents are about — and in four adjacentallow_low_baud_scope/ migration fragments, which live indocs/guide/ic7610-usb-setup.mdanddocs/guide/troubleshooting.md.23ac194ethen fixed a regressionc407b902introduced: it had writtenSerialBackendConfig(model="IC-7610", ..., …), and in Python a bare...after a keyword argument is a positional argument, so three snippets that compiled before stopped compiling. All abbreviated calls now put the placeholder first —SerialBackendConfig(..., model="IC-7610", …)— including oneLanBackendConfigcall inic7610-usb-setup.mdthat had the same defect before this PR. Verified at the head by compiling every one-line backend-config snippet indocs/**andREADME.md: 24 checked, 0 broken. Docs-only edits; the file count stays at 10.Not fixed here, and not because they work. Of the thirty
LanBackendConfig(...)constructions indocs/**andREADME.md, twenty-three pass neither a model nor an address and two passradio_addr=0x42; all twenty-five refuse today, before #3411 changes anything — measured at0a5bd7f6:One example resolves by address (
radio_addr=0xA4→ IC-705) and two pass a variable address. That path is not a substitute for naming the radio: those addresses are factory defaults and the operator can change them in the radio's own menu, which is exactly what the two0x42examples document.They are left out because this PR is already at the 10-file hard ceiling, and they are recorded as follow-up work on MOR-2425.