#86 shipped the refuse half of #80's fix#1; this completes the other half:
"refuse … or automatically select a non-colliding subnet". The bare
setup-host that used to stop an agent mid-drill inside a box now decides
the subnet itself. choose_subnet is the one place that decision happens,
before any mutation — BOX_GW, the bridge, the ACL carve-out, the firewall
and the doctor's expectations all derive from its answer. Four deliberate
cases:
1. explicit BOX_SUBNET — honored or refused, never silently overridden:
a script that says 10.90 gets 10.90 or a loud stop. The claimant
refusal and the bridge agree-gate are unchanged in spirit.
2. no pin, boxnet exists — converge to the bridge's own subnet: the
bridge IS the pin (boxes hold leases on it). The old agree-gate
refusal on a bare re-run against a moved bridge becomes convergence,
announced when the bridge is off-default, silent on the plain
default-host re-run. A FOREIGN claimant on the bridge's own subnet
still refuses — that is #80's poisoned state, and converging would
rebuild the blackouts; the refusal names the bridge move instead.
3. no pin, no bridge, 10.88.0.0/24 free — the default, as always.
4. no pin, no bridge, default claimed — the nested case (a drill or
rehearsal inside a box, whose own uplink owns 10.88): scan
10.89.0.0/24 … 10.127.0.0/24 in order, take the first free candidate,
announce the pick, the claimant and the BOX_SUBNET pin for scripts;
refuse only when every candidate is claimed. The scan only ever runs
bridge-less — an existing bridge is case 2, which precedes it.
Pure over ip (via subnet_claimant and the bridge read), so test/cli.sh
drives every case against canned tables with the shim ip.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>