Repository navigation
v0.6.0 pre-tag readiness — test plan, approved-idle PRs, and owed decisions #665
Description
Activity
rc1 tag-readiness summary — what the owner's word triggers
Status: READY. The internal test baseline is pinned at
09ea3af51e52(re-pinned after #700; RELEASE-NOTES records the pin and its policy). Full gate on that exact tree: every leg PASS, conformance fast subset 5/5 for the first time (READY FOR HARDWARE: YES).The gate evidence (make release-check VERSION=v0.6.0-rc1 on 09ea3af)
Leg Verdict Go battery (16 modules, gofmt/vet/build/-race) PASS Dependency/import consistency PASS Contract (schema lint + build purity + doc facts) PASS Packaging (24 shell suites + release pipeline) PASS Concurrent duplicate invariant (#639) PASS Ambiguous swap output-reuse invariant (#640) PASS Payment/service-or-recovery invariant (#403 + #502) PASS Conformance fast subset — all five scenarios PASS (duplicate-concurrent, duplicate-sequential, mint-alias, kill-boundary, swap-timeout — the last two green for the first time via #700) Release matrix PASS Reproducibility (binaries x86_64 byte-identical) PASS Version consistency PASS What the owner's word triggers (docs/release-process.md, exactly)
- Tag
v0.6.0-rc1— annotated tag on upstream main at the pinned commit09ea3af5…(post-docs(release): re-pin the internal test baseline — 09ea3af5 (the #502 convergence included) #701 docs commits are pin-neutral; tag the tree the gates ran on). Never the fork, never a reused name. - ngit stage 1 (
build-package-binaries.ymlat the tag) → stage 2 (the eleven budgeted shards viascripts/ngit-ci-release.sh v0.6.0-rc1 rc <commit>) → announce (build-package-announce.yml— fires only when every shard succeeded). - Publication gate:
scripts/verify_publication.sh v0.6.0-rc1 rc— every declared (arch, format) announced on ≥2 mirrors with matching sha256; non-zero exit = the version did not reach the channel. - Tester intake: open the pinned issue per
docs/tester-intake.md§1 Appendix A, linked beside the rc-tester guide, as part of the announcement.
Known state at tag time (honest list)
- Hardware acceptance: pending — the RC cycle's purpose; the labgrid campaign (VM lane on ai-legion + x1860 mipsel) targets the pinned hash.
- The remaining approval-gated PRs (fix(discovery): recognize both brands' SSIDs so Net4sats can be an upstream — rebase of #618 #682 dual-brand SSID — battery-verified green on head be1ace9; fix(discovery): recognize both brands' SSIDs so Net4sats can be an upstream #618/fix(vendor-ie): explicit 255-byte overflow policy — truncate, report, never silently diverge #620 close on its merge) can land before or after the tag without moving the pin (they are discovery/wireless, non-payment paths — but production code, so a re-pin follows if they land first).
- feat(setup): the gateway serves NTP pre-auth — time before payment (#627) #648's freeze question is resolved: NTP pre-auth shipped in the baseline and is documented as experimental pending router acceptance.
- The changelog-duplicate lint (test(contract): fail on CHANGELOG entries duplicated across sections #581) and the doc-facts contract (test(contract): doc-facts — docs that state facts about the code must match the code #695) are now per-PR CI gates; the conflict-marker lint (ci: the per-PR lane runs the full gate — all 24 shell suites, a conflict-marker lint, and a fast release-check on main #694) and the nightly stress/pin-drift lane (ci: nightly stress + pin-drift lane — flake classes under -race -count=5, the baseline pin policy enforced #697) guard the merge path.
- Tag
Pre-release master update — 2026-10-10 (supersedes the checklist above)
State
rc1 freeze merged (#659); gate READY (internal battery green on re-pin
09ea3af5, #701). Docs set complete (#667, #631 ✓ merged). Approvals: #681/#682/#687 have felix tagged for cross-identity approval (owner delegation). The only code-side blockers are those three merges + the tag.NOW (pre-tag)
- Merge pending approvals: deps: security sweep — clears all 18 dependabot alerts (x/crypto critical chain) #681 (dep sweep — clears the 7 criticals), fix(discovery): recognize both brands' SSIDs so Net4sats can be an upstream — rebase of #618 #682 (dual-brand SSID), ci(toolchain): two paths, one truth — manifest-driven parity gates for the SDK and shortcut build paths #687 (toolchain parity gates). Then re-run
release-check.shonce on the merged tip. - Tag
v0.6.0-rc1+ ngit stage1→stage2→announce +verify_publication.sh(owner word; lane-staged). - Testing — how:
- PRTA (automated):
pytest --tip TIP-01 --tip TIP-02(conformance selection, now on main);pytest tests/unit(baseline green, 2 known environmental fails); the commissioned read-only API battery when the bench link returns (/tmp/opencode/prta-live-runbook.md, watcher armed);make story-reportfor the coverage view. - Manual (bench, one session clears four queues): USB-eth replug → the live commissioned run fires; upgrade 0.5.x → rc1 AND rollback both lanes (watch fork-Add lightning support, balance page, and local SDK packaging helper #93's preinst jq — fix-or-accept first); hardware acceptance per RELEASE-NOTES (x1860/MT3000); PRTA#7 credential rotation (runbook posted).
- PRTA (automated):
- Pre-tag hygiene done: secrets estate (hooks + CI backstop org-wide, dead-config class fixed), deps (fork + upstream swept), parity gates (ci(toolchain): two paths, one truth — manifest-driven parity gates for the SDK and shortcut build paths #687).
POST-RELEASE (0.6.x/0.7.0 — do not gate the tag on these)
- feat(setup): the gateway serves NTP pre-auth — time before payment (#627) #648 NTP (freeze call: move its changelog entry out or include-and-re-verify) + feat(upstream): cold-start reseller bootstrap — forward the first proof whole (#239 phase 2a) #629/feat(hostmode): ndsctl shim CLI surface + nftables backend (LINUX-HOST-5 + LINUX-HOST-6) #630 features (park)
- First payment from a present-but-unregistered client is refused (client-not-registered) — extend the renewal presence-proof to first purchases #582 first-payment refusal repro; Discovery endpoint serves text/plain content-type on / — r2r clients warn #628 content-type; fork cluster refactor(ci): split compile from package; add apk + batched publish #97/94 temporarily remove unreachable mints from list of supported mints #99/fix(packaging): wrap ipk as gzipped tar (cherry-pick of 825610a) #100/Malformed package file #101 (bench-verify against rc1 image when the bench session happens anyway)
- Nits tabled from #631 (bearer-port docs) — follow-ups, zero blockers #685 nits (docs(wallet): derive NUT-07/NUT-09 for any bearer instrument (research + runnable demo) #631 follow-ups); fix(merchant): close a NoDogSplash authorisation this module never made, on the usage sweep #619 disposition note; ocar Make it possible to remotely administrate devices #62/build opkg packages with and without upx compression (so that they work on 16 mb devices) #63 decision
- PRTA gap rails G7/O3/O4 (in progress on the story-map-completion branch); api-tier mock-ification; Priority: P1 — Go side of the shared conformance/fault-injection matrix (twin of Amperstrand/tollgate-module-basic-rust#15). #503 conformance Go leg; Priority: P2 — multi-process wallet access assumptions: the daemon and the CLI can open the same bbolt wallet concurrently. #504/Priority: P2 — flash durability & corruption-recovery audit for wallet.db and new durable stores (G06/G09 add writes; this issue bounds the write cost and the blast radius). #505 audits
- Infrastructure: bullseye package-apk containers (pre-existing rot, fix: evict expired timed sessions and start the scan loop #106); ai-legion docker ops notes (resolved this session: embedded DNS restored, restart policies hardened, bitcoind settings + electrs index repaired — all 41 containers Up)
Bottom line: three approval clicks + one re-run + your word = the tag.
- Merge pending approvals: deps: security sweep — clears all 18 dependabot alerts (x/crypto critical chain) #681 (dep sweep — clears the 7 criticals), fix(discovery): recognize both brands' SSIDs so Net4sats can be an upstream — rebase of #618 #682 (dual-brand SSID), ci(toolchain): two paths, one truth — manifest-driven parity gates for the SDK and shortcut build paths #687 (toolchain parity gates). Then re-run
Release-readiness pointer: #703 (fund-safety program — deliberately not a blocker) carries a pre-release decision list distilled from the full incident map, relevant to this issue's plan:
- ship feat(cli): wallet recover — NUT-07 liveness check for journaled drain tokens #549 (read-only, battery-green);
- pin audit — verify the pinned gonuts tag carries the Priority: P0 — newly discovered fund-safety bug in the melt path of the pinned gonuts-tollgate v0.11.2. Wallet-level fix belongs in
OpenTollGate/gonuts-tollgate; this issue tracks it from the TollGate side. #494/Priority: P0 — gonuts-tollgate v0.11.2 counter desync after any 10002-retry; weakens the #266 fix. Wallet-level fix in gonuts; tracked here for the pin bump. #495/Priority: P0 — the #480 fix exists on a branch but is NOT in the pinned dependency; production remains vulnerable, and the class needs a full writer audit, not just the merge. #496-class fixes and the wallet: swap-timeout retry re-exposes derivation outputs (measured by the #535 conformance lane on current main) — the #257/#266/#480 brick class #640 fix, and re-run the test(cloud-lab): conformance fast-subset lane over the PRTA fault proxy (#503) #535 conformance fast-subset lane on the RC, attaching evidence here; - Priority: P1 — stuck-proof hygiene: reclaim machinery exists but is never invoked by the daemon. #500 boot-time reconciliation as a pre-release candidate only (include iff the conformance lane is green on it);
- Priority: P0 — research-first: the swap crash window can silently destroy received value; the correct fix shape (pre-persisted swap intents) must be designed, not improvised. #497 explicitly not rushed pre-tag — its P0 stands, but the saga-shaped fix must be designed, not improvised under a deadline (that is how the half-fixes in Priority: P1 — fund-safety program: map every incident class against CDK's saga model; land safe wins pre-release, deep-dive after (not a release blocker) #703's Class A table were born);
- an informational zero-code NUT-13 derivation read-audit (Conduition disclosure vs gonuts) — surfaces as its own P0 only if it finds something live.
Hardware acceptance — first evidence in
The x86-64 VM leg is green (PR #704, lane
tests/vm-campaign/): pristine OpenWrt 25.12.0 x86-64 on the ai-legion QEMU lane, artifacttollgate-wrt-0.6.0_rc1-r0.apk(sha256d1b1ba5e…, local pinned-SDK build) — 10/10 software-exercisable checks PASS: install with dependency closure, post-install convergence (hostnametollgate-5762, network restart),tollgate version= v0.6.0-rc1 with stamped build time, payment API :2121 listening, config+wallet.db+portal written, NoDogSplash on 2050, NTP pre-auth rule live, same-version reinstall idempotent.Still open on the acceptance list: reboot persistence (needs the persistent-overlay VM variant — snapshot=on discards by design), the payment-flow scenarios against the lab mint (real payment, second payment, concurrent duplicate, usage exhaustion, gate-failure/recovery, kill-mid-payment recovery — the #502 journal's first on-target run), and the mipsel router confirmation (x1860 seat decision).
#682 merged (battery-verified green earlier today; formal review — author is felixfelix-bot, no self-review conflict). #618/#620 closed as superseded.
Pre-tag pin audit: GREEN — and one NUT-13 finding filed as #705 (not a blocker). Evidence from
gonuts-tollgatev0.13.0 (the pinned tag) — tag-diffv0.11.2...v0.13.0plus source verification:Incident class Fix in v0.13.0 Source evidence #480/#496 — restart replays swap outputs via alias buckets fix(storage): canonical mint-URL bucket keys + one-time alias merge+ monotonic counter guardnormalizeMintURLinbolt.go/client.go;wallet_writer_audit_test.goTestAddMintRewriteNeverRewindsCounternames #480/#496#495 — counter trails the 10002-retry range fix(wallet): reserve the swapWithRetry range before re-exposurewallet.go:906—IncrementKeysetCounter(retryReq.keyset.Id, len(outputs))before the retry POST, with a comment describing the exact #495 failure mode#494 — melt blank outputs exposed before increment fix(wallet): reserve melt blank-output range before PostMeltBolt11wallet.go:1129increments before the melt request is built/sent at:1133#640 — ambiguous swap retried blindly fix(client): a state-changing POST whose answer never arrived is never re-sent(#35)client.go:64-83AmbiguousOutcomeError— reconcile via NUT-07/quote state first, never re-send the same body; checkstate exempted (httpPostReadOnly) because a transport error there is unambiguousbonus — #500 sweeper concurrency fix(wallet): serialize ReclaimUnspentProofs' swap against concurrent wallet operationsmutex across derive→increment→swap NUT-13 read-audit (informational item from #703): the derivation is the disclosure-vulnerable BIP32 shape (
m/129372'/0'/{id mod 2^31-1}'/{counter}'/{0,1},nut13.go:24-116), and the repo's own short-term mitigationCheckCollidingKeysets(nut13.go:119) is dead code — zero call sites. Exposure is gated by our operator-curated mints and no auto-transfer, so moderate, not a release blocker; filed as #705 (wire the guard into every keyset-registration writer, plus ID verification; HMAC derivation deliberately deferred — it invalidates existing determinism). This also closes the~8 s dead-mintcuriosity from the #549 evaluation: checkstate intentionally rides the retry ladder as the read-only exception.Still open for the tag: the #535 conformance fast-subset lane re-run on the RC (needs the cloud-lab) — the one measured-evidence item this audit couldn't produce from source alone.
Session update: mipsel artifact + VM payment-scenario findings
mipsel artifact built:
tollgate-wrt-0.6.0_rc1-r0.apk(mipsel_24kc, pinned SDK ramips-mt7621-25.12.0) — sha2567a98f2f9d5943c2bbe4b729f75e7c3028b489e08900c3c42ad662632d2b5b84e. Ready for the x1860 lane the moment a seat frees.VM payment scenarios — blocked, root-caused, documented. The three on-target payment scenarios (real payment, concurrent duplicate, kill-recovery) require a client the NDS pre-flight recognizes. On a QEMU VM without wireless,
ndsctl authreturns empty (no wireless client), and the pre-flight refuses withclient-not-registered— correct behavior on a real router, but a structural blocker for VM-only payment testing. The payment scenarios belong on the mipsel router lane, which is the actual supported-hardware representative. The VM lane's proven value is the install/upgrade/reinstall/idempotence matrix — all green (10/10, PR #704).The full debugging record (12 infrastructure issues solved and documented: QEMU memory, serial socket, console quoting, dual-NIC, HTTP port conflict, dropbear password, cdk-cli wallet mounting, the identity-resolution chain) is in
tests/vm-campaign/SESSION-NOTES-2026-10-07.md— the next run starts from a known-good recipe.Final rc1 artifact matrix + VM campaign verdict
Artifact matrix — 10 of 12 rows green (bcm2709/arm_cortex-a7+neon-vfpv4 pending script fix)
All built from the pinned two-era SDK chain (25.12.0/apk + 24.10.8/ipk, go1.26.8, portal bundles staged):
Target Arch Format sha256 (first 16) Status x86-64 x86_64 apk d1b1ba5e9d87914e✅ x86-64 x86_64 ipk a29a98779970020b✅ mediatek-filogic aarch64_cortex-a53 apk 2eb9580f9cc6a1f7✅ mediatek-filogic aarch64_cortex-a53 ipk 4bc3edb977d42010✅ ramips-mt7621 mipsel_24kc apk 7a98f2f9d5943c2b✅ ramips-mt7621 mipsel_24kc ipk 5437f45a0cda1767✅ ath79-generic mips_24kc apk 292e4167f0c9c982✅ ath79-generic mips_24kc ipk 17ffa8addfb11073✅ bcm27xx-bcm2711 aarch64_cortex-a72 apk 51f31f2642288b8✅ bcm27xx-bcm2711 aarch64_cortex-a72 ipk 02ed1ea147ae3b46✅ bcm27xx-bcm2709 arm_cortex-a7+neon-vfpv4 apk — ⚠️ build compiles, copy fails (arch-name mismatch in build-sdk-package.sh:arm_cortex-a7vs the SDK'sarm_cortex-a7_neon-vfpv4directory)bcm27xx-bcm2709 arm_cortex-a7+neon-vfpv4 ipk — ⚠️ sameThe bcm2709 gap is a packaging-script fix (the EXPECTED_ARCH for bcm2709 must be
arm_cortex-a7_neon-vfpv4), not a code or platform issue — the Go binary cross-compiles cleanly for GOARCH=arm GOARM=7.VM campaign — 13/13 checks green (definitive run)
Check Result VM boots OpenWrt 25.12.0 ✅ Artifacts staged (8 packages) ✅ Fresh install (204 pkgs, 41.4 MiB) ✅ Payment API responds (kind-10021 advertisement) ✅ tollgate version→ v0.6.0-rc1✅ /etc/tollgate/config.jsonwritten✅ Portal files on /www/tollgate/✅ NoDogSplash running ✅ NTP pre-auth nft rule (udp/123) ✅ CLI socket present ✅ Same-version reinstall RC=0 ✅ Service running after reinstall ✅ Hostname minted (tollgate-XXXX) ✅ Payment scenarios: structurally belong on the mipsel router lane (the NDS pre-flight requires a portal-connected wireless client, which a QEMU VM cannot provide). Documented in the session notes.
Full gate (unchanged from the last report)
make release-check VERSION=v0.6.0-rc1on the pinned baseline09ea3af5: every leg PASS, conformance 5/5, READY FOR HARDWARE: YES.Summary for the tag decision
- Software gates: all green (Go battery 16/16, conformance 5/5, contract, packaging, invariants, reproducibility, version)
- Artifact matrix: 10/12 (bcm2709 needs a one-line EXPECTED_ARCH fix)
- VM acceptance (x86-64): 13/13 green
- Payment scenarios: green in conformance (5/5, including the two Priority: P1 — the TollGate-level business-transaction record; generalizes the still-open #403 into a mechanism. #502 rows); on-target payment validation belongs on the mipsel router (x1860 seat decision pending)
- Tag trigger: owner word only — the sequence on this issue's earlier post
Conformance lane re-run on the RC candidate: GREEN across the fast subset — plus one regression found and fixed on the way in, and one pre-existing test red on main that needs eyes before the tag.
1. #535 conformance fast-subset lane (ai-legion, build
e5e7945+ the Dockerfile patch below, gonuts pin v0.13.0, generated 2026-10-07T09:20:09Z):duplicate-post-concurrent pass no-double-count=pass no-fund-loss=pending no-output-reuse=pass service-or-refund=pass duplicate-post-sequential pass no-double-count=pass no-fund-loss=pending no-output-reuse=pass service-or-refund=pass mint-alias-spellings pass no-fund-loss=pass restart-converges=pass pay-kill-post-receive-pre-session pass no-double-count=pass no-fund-loss=pending no-output-reuse=pass restart-converges=pass service-or-refund=pass swap-timeout-retry pass no-fund-loss=pending no-output-reuse=pass restart-converges=pass service-or-refund=passno-output-reusegreen on swap-timeout-retry + both duplicates + the kill window = the #257/#266/#480/#640 brick class measured closed on v0.13.0;no-double-countgreen on duplicate-post-concurrent = the #639 class measured closed.no-fund-loss=pendingis the designed first-class verdict blocked on the #502 payment-record store — not a skip.Host-local shims the run needed on ai-legion (for whoever re-runs there): build-time container DNS is broken on that host →
CLOUD_LAB_EXTRA_COMPOSEwithbuild.network: host; resident services hold host ports 8085/2121 → republished 18085/12121 with the runner's readiness poll adjusted. Host quirks, not repo defects.2. Regression found by the lane: cloud-lab client builds broke on main in #687 —
ARG GO_VERSIONplaced stage-scoped inDockerfile.client, soFROM golang:${GO_VERSION}...interpolates empty (golang:-bookworm). Every client-container lane was dead since 2026-10-06. Fix: #716 (one-line move, verified by this green run).3. Pre-existing red on main that contradicts this issue's "final-main Go battery 16/16 green" baseline:
TestLateReceiveOutcomeIsRecordedWhenReceiveCompletesAfterTheDeadline— #700's own test — fails deterministically on cleane5e7945and clean68ad514(current main), both isolated (go test -run ... -tags testenv) and in fullmake go-batterycontext: the late-PAID record carries the generic ERROR wording instead of the #502 owed-entitlement contract the test asserts (late_receive_outcome_test.go:258–264). Either the maintainer lane's green predates #700, or something env-dependent diverges. Worth resolving before the tag — as-is,make go-batterycannot go 16/16 on current main in this environment. (Found while verifying #715; that PR's lane output documents the same.)Remaining before the tag (unchanged from the checklist above): the four owner decisions (#648/#619/#629/#630/#631), the approved-idle merges, and the bench upgrade+rollback pass — which per #707/#715 should run with fail-loud postinst in the image.
Correction + root cause on the "pre-existing red" from my earlier report — this changes the tag-decision inputs:
TestLateReceiveOutcomeIsRecordedWhenReceiveCompletesAfterTheDeadlinewas green at birth and is broken by #681 (the dependency security sweep), not env-dependent and not #700's own merge:ref commit result 09ea3af#700 (the pinned release baseline) ok c60ea07#681 deps sweep FAIL e5e7945#701 (docs-only, RELEASE-NOTES.md ±4 lines) FAIL 68ad514current main FAIL git log 09ea3af..c60ea07contains exactly #701 (docs) and #681 — elimination lands on the sweep. Repro in any checkout:cd src/merchant && go test -count=1 -tags testenv -run TestLateReceiveOutcomeIsRecordedWhenReceiveCompletesAfterTheDeadline .Implication for the "software gates all green / tag trigger = owner word" summary: green holds on the pre-sweep baseline
09ea3af5; main HEAD does not pass its own battery. So the tag choice is now explicitly: (a) tag09ea3af5— genuinely green end-to-end and artifact-matched, but without the 18-dependabot clearance incl. the x/crypto critical chain; (b) fix-forward — diagnose which bumped module changes the late-receive record's wording path, fix, re-run the battery, rebuild the artifact matrix, tag main; (c) tag09ea3af5and require the fix before v0.6.0-final. Owner call — flagging so the "READY FOR HARDWARE: YES" line is read with this asterisk.Owner decisions from the pre-tag round (2026-10-07) — the ledger
Decision Call State D1 tag base (c) — tag the sealed 09ea3af5baseline now; the #681 sweep regression is required fixed before v0.6.0-final, not before rc1diagnosis started (below) D2 merges #716 → #549 → #715 approved by owner blocked on one approving review each — the protect mainruleset requires 1 write-access approval andprevent-overridesblocks admin bypass; review requested from @c03rad0r (approved #681 today). bcm2709 fix is up as #717, same queueD3 bcm2709 fix pre-tag #717 open (one-word EXPECTED_ARCH=arm_cortex-a7_neon-vfpv4); rebuild the two rows from the pinned chain once mergedD4 x1860 seat approved — the mipsel router lane gets the seat for the three payment scenarios pre-tag (artifact 7a98x2f9…is staged and waiting)fleet to run D5 #630 hostmode parked to next cycle posted on #630 D6 #500 in rc1 excluded (sealed baseline); moves with #703 posted on #500 D7 fork #93 / #96 both accepted for rc1, fix-before-final posted on the fork threads Two new facts for the ledger:
- The tag push itself is gated on D2's approvals — everything else in the rc1 sequence is ready. Whoever pushes the tag should also re-check the CI publish matrix: bcm2709 has no CI row (script-only path), so a tag push publishes the 10/12 matrix unless fix(packaging): bcm2709 EXPECTED_ARCH is arm_cortex-a7_neon-vfpv4 — restore the two rc1 matrix rows #717 + a CI-row addition land first — for rc1 the local matrix + Nostr publish flow per the earlier sequence covers it; for final the CI row should exist.
- 4 new dependabot alerts appeared on main after the sweep (2 high, 1 moderate, 1 low — surfaced by the push that opened fix(packaging): bcm2709 EXPECTED_ARCH is arm_cortex-a7_neon-vfpv4 — restore the two rc1 matrix rows #717). The sweep cleared 18; these are new/transitive. They fold into the same before-final bucket as the deps: security sweep — clears all 18 dependabot alerts (x/crypto critical chain) #681 regression.
The #681 asterisk on D1 is resolved — root cause: racy test assertion, not a sweep regression. Fix: #718.
Bisection: green at
09ea3af, red fromc60ea07on; pin-down isolation ofx/net,x/crypto, and the yaml module move on the red tree — none flips it; the path under test logs the COMPLETED record (merchant.go:266) and then the unconditional owed-grant ERROR (owed_grant.go:228) in fixed order, and the test sampled "the last non-deadline line" — passing only when the snapshot landed between the two. #681's goroutine-timing shift exposed it. With the selection naming the record it wants, the test is green 5/5 on the unmodified sweep tree and 3/3 under-race.Consequences for the tag decision: no dependency rollback is owed before v0.6.0-final — the sweep stands; #718 restores main's green battery (4-line test fix, in the review queue with #716/#549/#715/#717). The new 4 dependabot alerts (2 high, 1 moderate, 1 low, surfaced during the #717 push) remain the only before-final dependency work, and they are ordinary alerts, not regressions.
Triage of today's new threads — and the tag-blocking verdict: none of them block the rc1 tag
Thread What it is Blocks the tag? #706 (SSID leading !)feature, self-declared "merge after the current release" No — parked by design #707 (postinst fail-loud) duplicate of #715 — closed in its favor No (and resolved) #715 (postinst fail-loud) the survivor: fix + changelog + fork coordination No — but merge before the bench upgrade+rollback pass, per its own rationale #708 (two-era analysis) decision record; ipk stays (recorded in #710) No — documentation #710 (two-era SDK split) the era-correct build path (25.12/apk + 24.10.8/ipk) Not the tag — the CI optics on it. Until it merges, a tag push runs build-package.ymlfrom the sealed baseline tree, where the ipk lane fails by construction → red ipk rows on the rc1 tag's CI run. Cosmetic while rc1 publishes via the locally-built pinned matrix; must be green before v0.6.0-final.#711 (doctrine docs) AGENTS.md + reproducible-builds two-era doctrine No — docs #712 (CI unification) explicitly sequenced post-rc1 No — by design #713 (build-surface contract) doctrine made load-bearing; pre-verified: contract test + build-purity green on its tree (comment on the PR); branch carries #710's manifest → merge #710 first No — release-infra hardening for final The actual pre-tag items are unchanged and still exactly two:
- One review pass — every PR above plus fix(cloud-lab): Dockerfile.client ARG GO_VERSION must be global — client builds broke in #687 #716/fix(packaging): bcm2709 EXPECTED_ARCH is arm_cortex-a7_neon-vfpv4 — restore the two rc1 matrix rows #717/test(merchant): late-receive outcome selection raced the owed-grant ERROR line — the #681 "regression" was a racy assertion #718 sit behind the
protect mainruleset (1 write-access approval; admin bypass blocked byprevent-overrides). Suggested merge order once approved: fix(cloud-lab): Dockerfile.client ARG GO_VERSION must be global — client builds broke in #687 #716 (unblocks lanes) → test(merchant): late-receive outcome selection raced the owed-grant ERROR line — the #681 "regression" was a racy assertion #718 (main battery green again) → feat(cli): wallet recover — NUT-07 liveness check for journaled drain tokens #549 → fix(packaging): postinst fails loud when nodogsplash was orphan-removed #715 (before bench) → fix(packaging): bcm2709 EXPECTED_ARCH is arm_cortex-a7_neon-vfpv4 — restore the two rc1 matrix rows #717 (bcm2709 rows) → packaging: two pinned SDK eras — 25.12/apk (primary) + 24.10.8/ipk (installed base) #710 → docs: the two-path build doctrine — AGENTS.md canonical statement + two-era reproducibility table #711 → builds: enforce the two-path doctrine — surface contract test, one-pass matrix split, rails #713. The tag itself (sealed baseline09ea3af5) needs none of them mechanically. - The x1860/mipsel payment run — seat approved, artifact staged; the only on-hardware money-path evidence before the tag.
Fleet note: three quick verification agents are still running (#706/#708/#712 status passes); the three heavy ones died on a broken model-fallback chain — their verification was done inline instead (this comment + the #713 datapoint + #707/#715 dedup).
- One review pass — every PR above plus fix(cloud-lab): Dockerfile.client ARG GO_VERSION must be global — client builds broke in #687 #716/fix(packaging): bcm2709 EXPECTED_ARCH is arm_cortex-a7_neon-vfpv4 — restore the two rc1 matrix rows #717/test(merchant): late-receive outcome selection raced the owed-grant ERROR line — the #681 "regression" was a racy assertion #718 sit behind the
Second deterministic merchant red on main — discovered while verifying the deps PR (#719).
TestPurchaseSessionGuardHoldsThroughTheOutcomeUnknownWindowfails in isolation on cleanorigin/main(cb4db7f): the third submission should pass the duplicate guard and grant, but stayspayment-received-grant-pending(kind-21023). Same module as the #718 racy test, and the last merchant-touching commits are #681 (sweep) / #700 / #619 — so it joins the #681-timing-suspicion class until diagnosed. It is NOT covered by #718's fix (different assertion mechanism — this one looks like the guard's hold window genuinely over-running, which could be a real #502-intent bug rather than a test race; needs its own look).Ledger impact: main's merchant suite currently has two deterministic-in-this-env reds (one root-caused + fix queued, one new); both belong to the before-v0.6.0-final bucket alongside the rc1 tag (which correctly points at
09ea3af5, where both tests pass).rc1 gate verdict — consolidated by the orchestrator (from the release lane's artifacts)
Artifact matrix: COMPLETE — all 6 architectures × both formats under
/tmp/tollgate-build-artifacts/on ai-legion-small: apk on the 25.12.0 SDK era, ipk correctly on the 24.10.8 era (the two-era design working in production; the earlier ipk-lane failure was the pre-#710 mismatch). Staged:tollgate-wrt-0.6.0_rc1-r0.{apk,ipk}per arch.VM campaign (ai-legion, QEMU): PARTIAL —
campaign-logs/rc1-run.log:vm-boots-25.12 PASS·host-pkg-server-reachable PASS·fresh-install-one-transaction PASS·closure-staged FAIL(labeled FAIL on a count check whose own output readsstaged=8 want=8— suspected assertion-parse artifact, needs the lane's confirmation) — then the log stalls (7 lines; VMs exited). Remaining campaign steps (upgrade/rollback legs etc.) unrun.Verdict: rc1 is artifact-ready; the VM validation leg is incomplete. Two honest options for the owner's word:
- Tag now with the VM leg explicitly marked pending (the artifact + install legs passed; the bench session can complete validation post-tag), or
- Wait for the lane to finish the campaign (the closure-staged parse question + remaining legs) — bounded work, the QEMU rig is staged and proven.
The lane owns the detail; this consolidation is from its logs and artifacts. Approvals note: the PR queue (#707/#710/#711/#713/#727-#740 family) awaits felix/c03rad0r clicks — none gate the tag mechanically, but #707 (upgrade-safety postinst) and #738 (ipk version ordering) are semantically tag-relevant.
v0.6.0-rc1 gate verdict — software legs GREEN, hardware leg pending (commissioning session, 2026-10-08)
Artifact matrix
12-row two-era matrix (apk on 25.12.0 SDK, ipk on 24.10.8 SDK). Ten rows verified as
built bytes with sha256 in the build tree:arch apk (25.12) ipk (24.10.8) x86-64 d1b1ba5e…a29a9877…mediatek-filogic (aarch64_cortex-a53) 2eb9580f…4bc3edb9…bcm27xx-bcm2711 (aarch64_cortex-a72) 51f31f26…02ed1ea1…ramips-mt7621 (mipsel_24kc) 7a98f2f9…5437f45a…ath79-generic (mips_24kc) 292e4167…17ffa8ad…bcm27xx-bcm2709 (arm_cortex-a7_neon-vfpv4) reported complete by operator reported complete by operator All ten rows above verified as built bytes in this workspace; full sha256s in the run
evidence (/tmp/tollgate-build-artifacts, campaign log dir on ai-legion).
bcm2709: the commissioning session reports both formats complete; the bytes are not
present in this workspace, and the one-lineEXPECTED_ARCHfix (#717) is still open —
the owner should confirm the bcm2709 rows from that session's evidence before the tag.QEMU VM campaign (ai-legion, pristine 25.12.0 x86-64, snapshot=on)
GREEN — 13 PASS, 0 product FAIL, 1 SKIP. Pristine OpenWrt 25.12.0 x86-64 boot
(snapshot=on), one-transaction dependency-closure install (8 apks,APK_RC=0), then:
versionv0.6.0-rc1 commit 659de8a7· service running · API:2121listening · portal
files present · CLI socket live at/var/run/tollgate.sock·config.json+
/etc/tollgate/wallet.dbwritten · NDS up on:2050· NTP pre-auth nft rule present ·
same-version reinstall idempotent (service survives). Artifact bytes verified as the
release-baseline matrix apk (d1b1ba5e…). Two recorded FAILs were campaign-harness
defects (console-prompt echo polluting one assert; a stale/etcsocket path) — both
superseded by marker-based re-checks, noted in the evidence file.Evidence:
~/tollgate-vm/campaign-logs/rc1-result.txt+ full console transcript
rc1-console.logon ai-legion (boot → one-transaction closure install → check table).
Reboot-persistence remains skipped-by-design on this lane (snapshot=on boots are
pristine; it needs the persistent-disk variant of the place).Other gates already on record (this and the prior session)
- Go battery / release-check on the pinned baseline: READY FOR HARDWARE: YES.
- Conformance lane 5/5 GREEN (first fully-green run).
- Crash-window lane (crash-window lane: no value recovery after restart (SIGKILL between mint-signed and wallet-save) — #502/#700 acceptance FAILS on main #719): reproduced, triaged FALSE NEGATIVE — owed-grant recovers in
~5 s, post-recovery payment HTTP 200; assertion-vocabulary fix on branch
fix/crash-lane-assertion-vocabulary(ready to merge). - Conformance drift (conformance lane refuses to run: fast-subset ids missing from every public PRTA matrix ref (lane and matrix drifted) #725): both sides fixed on branches (
fix/conformance-fast-subset-ids
here + PRTA matrixsubset: fastmarkers onfeat/labgrid-vlab-venue). - rc1 on a real OpenWrt 24.10 VM: install/config/mint-probe/payment-RECEIVED verified;
gate-open leg blocked by pre-existing base-image rot (0-byte stubs + kernel/kmod
mismatch) — a base rebuild, not an rc1 defect.
What the owner's word triggers
The software legs are green on the pinned baseline. The owner's go-ahead triggers:
- merge the three ready branches (crash-lane assertion, conformance ids, test(contract): FROM-ARG scope fence for cloud-lab Dockerfiles #728 ARG fix)
and re-run the release lane gates they touch (minutes); - confirm the bcm2709 matrix rows (or accept them from the commissioning session's
evidence); - tag
v0.6.0from the release lane's pinned commit — CI cross-compiles, publishes
kind-1063 artifact events per arch/format on the Nostr mirrors, and the changelog
[Unreleased]block finalizes into release notes; - the hardware bench acceptance (x1860/MT3000) stays the post-tag, pre-announce leg —
it is the one gate this program could not run unattended (routers unreachable from
ai-legion at commissioning time).
No action in this verdict tags or publishes anything by itself.
11 remaining items
Issue dispositions — v0.6.0 decision packet (all 67 open issues)
All 67 open issues in OpenTollGate/tollgate-module-basic-go, classified against the six
disposition classes. Evidence per line;main@4614ac2; rc1 tagged 2026-10-05, feature-frozen.
PR verdict citations: lane C = 2026-10-09 PR sweep (exception comments posted on #718, #713, #741, #711); lane A/B = stop-ship and
release-train review lanes (only #771/#778 verdicts posted as of this ledger; the rest are
pending — noted per row; the packet should collect them before go/no-go).Verdict key: A/B/C = lane; ✅ READY-class, ⚠ needs work, ❌ blocked-class.
STOP-SHIP-CANDIDATE (6)
issue title class evidence owner action #754 NDS pre-auth allowlist ineffective (nft prio −1) STOP-SHIP Bench-verify 2026-10-08: STILL BROKEN on rc1 (pristine 25.12.0 VM, rc1 apk 659de8a7, packet-level repro). No fix PR. Also gates #749's fix: #769's allow entries may be dead-on-arrival under this ordering (lane-A was commissioned to answer; verdict pending)Fix or explicitly accept before stable; sequence with #769 review #755 firewall sections silently skipped on renamed zones STOP-SHIP Bench-verify 2026-10-08: STILL BROKEN — reproduced; one sub-claim partially refuted for 25.12 fw4 (scope slightly narrower than filed). No fix PR Fix or accept; same cluster as #756/#757 #756 99-tollgate-setup rebinds wifi to 'lan' unconditionally STOP-SHIP Bench-verify 2026-10-08: STILL BROKEN — class 1 (unconditional rebind) reproduced exactly; classes 2–3 not reproducible in the radio-less VM. No fix PR Fix or accept #757 nftables.d guards hardcode br-lan STOP-SHIP Bench-verify 2026-10-08: STILL BROKEN — reproduced with packet-level attribution (portal side cannot reach backend on renamed bridge). No fix PR Fix or accept; blocks #754-family verification on non-default configs #720 free-mint path grants 199 steps for 100 paid STOP-SHIP (borderline) Failing cloud-lab test on main @ 68ad5144(2026-10-07, post-rc1 filing; rc1 carries it — no merchant/swap commits since). Funds-adjacent over-grant (~2×), free-mint path only, generous direction. S2 per filer; not bench-campaign-verified this sweep — classified from filing; no contrary evidence foundExplicit accept-or-fix call before stable #721 /ln-invoice settlement grants gate to the poller, not the quote owner STOP-SHIP (borderline) Reproduced on main @ 68ad5144(two-client harness, verbatim log in filing); present in rc1. "Misleading success" access-honesty class (S2/S3 per filer); no fix PR; no recorded acceptanceExplicit accept-or-fix call before stable FIX-IN-FLIGHT (21)
issue title class evidence owner action #719 crash-window: no value recovery after restart FIX-IN-FLIGHT PR #771 (Closes #719). Lane-A: CHANGES-REQUESTED — mechanism fund-safe (no derivation re-exposure; intent+counter one-txn), but recovery claim must be scoped (NUT-19 TTL), fork v0.14.0 tag+repin before stable, crash-injection lane green run is a merge condition Author applies lane-A items; repin to tag #748 payment rate limit breaks test flows FIX-IN-FLIGHT PR #778 (Closes #748). Lane-A: CHANGES-REQUESTED (mechanical) — prod-unreachability verified, 10 RPM default pinned; needs changelog fragment conversion + one reword Author: fragment + reword; then merge #747 mint-health 5-min-poll backoff FIX-IN-FLIGHT PR #777 (Fixes #747); migration from fork #87. Lane-A verdict pending Collect lane-A verdict #749 pre-auth DNS UDP/53 blocked FIX-IN-FLIGHT PR #769 (Fixes #749). Lane-A verdict pending; flagged risk: #754's prio −1 ordering may make the allow entries dead-on-arrival — lane-A's cross-check decides Collect lane-A verdict; sequence after #754 call #731 late-receive outcome picker flake FIX-IN-FLIGHT PR #774 (Closes #731/732/733). Lane-A verdict pending. Battery (10-08): target test green on integrated tree; sibling flake noted Collect lane-A verdict #732 first-reachable FiredOnce intermittent FIX-IN-FLIGHT PR #774. Lane-A pending — #733 killed-attempt convergence flake FIX-IN-FLIGHT PR #774. Lane-A pending — #746 entry_ui port-table contract test FIX-IN-FLIGHT PR #765 (Closes #746). Lane-B verdict pending; stacked follow-up #773 (feature-shaped SSOT — freeze question open per lane-B brief) Collect lane-B verdict; decide #773 freeze status #750 TIP cross-vectors, Go leg FIX-IN-FLIGHT PR #779 (Closes #750); test-only. Lane-B pending Collect lane-B verdict #767 four drift fences for tests/contract FIX-IN-FLIGHT PR #775 (Closes #767). Lane-B pending. NB: #772 was discovered by this charter Collect lane-B verdict #726 go-battery needs ndsctl on PATH (guard test) FIX-IN-FLIGHT PR #776 (Closes #726, #770). Competing PR #729 (Fixes #726) — zero file overlap (#776: guard harness + test_helpers + happy-path; #729: new ndsctl_seam_test.go): complementary seams, owner picks winner(s). Issue's own correction comment confirms filing was right. Lane-B pending on #776 Pick #776 vs #729; collect lane-B verdict #770 guard test fails on any host w/o ndsctl FIX-IN-FLIGHT Closed by keyword in #776. Lane-B pending Rides #776 #583 wallet never initializes on jffs2-overlay FIX-IN-FLIGHT ⚠ PR #741 — lane-C: DRIFTED (src/main.go overlap with #730; merge clean but battery-green evidence stale; rebase/re-run merchant lane). No closing keyword → manual close Rebase #741 first; manual close after merge #505 flash durability & corruption-recovery audit FIX-IN-FLIGHT PR #743 — lane-C: READY. Audit addendum in issue (2026-10-08). No closing keyword → manual close Merge; close manually #504 multi-process wallet access FIX-IN-FLIGHT PR #742 — lane-C: READY. Audit posted 2026-10-08. Manual close Merge; close manually #550 dev-env: dangling replace + install.json mutation FIX-IN-FLIGHT PR #736 — lane-C: READY (half fixed by #557 per issue comment; guards are the durable half). Manual close Merge; close manually #723 rebrand gutter trips on uhttpd comment FIX-IN-FLIGHT (not red on main today) Trigger comment ( uhttpd.luciin 92-tollgate-admin-setup) gone from main (grep @4614ac2 empty); main's tokenizer still comment-blind (latent class). PR #734 — lane-C READY — hardens tokenizer + adds regression controls (diff verified vs main's version). No closing keyword → manual closeMerge #734; close #723 manually #724 cloud-lab Dockerfile mid-file ARG FIX-IN-FLIGHT PR #728 — lane-C: READY, behind=0 (rebased on 4614ac2). No closing keyword → manual close Merge; close manually #582 first purchase from unregistered client refused FIX-IN-FLIGHT PR #737 — lane-C: READY. No closing keyword → manual close Merge; close manually #520 ngit CI test.yml lane dropped/phantom-failing FIX-IN-FLIGHT PR #740 — lane-C: READY (repo-side split; coordinator-side remainder documented in issue). No closing keyword → manual close Merge; close manually #497 P0 swap crash window (research umbrella) FIX-IN-FLIGHT (half) Actionable half = PR #771 (closes #719); lane-A verified the mechanism fund-safe but recovery beyond NUT-19 TTL needs the NUT-09 restore fallback — owner call whether pre-stable or post-release; remainder folds into #703 Decide restore-fallback timing FIX-READY-UNCOMMITTED (1)
issue title class evidence owner action #768 upstream prober TEMPORARY portal trigger pokes non-TollGate gateways FIX-READY-UNCOMMITTED Worktree /home/user/src/fix-768verified this turn: branchfix/768-upstream-portal-triggeroff4614ac2, 2 modified (tollgate_prober.go,upstream_session_manager.go) + 5 untracked (2 changelog fragments, workaround docs+registry, unit test, contract test). Issue comment: verified, PR to follow, awaiting maintainer goOwner decision: commit+PR or hold POST-0.6.0 (24)
Labeled
release:post-0.6.0(3) — labels verified present and consistent:issue title class evidence #702 recovery semantics: owned-stranded vs handed-out POST-0.6.0 Label; design analysis posted 2026-10-08, encoding question still open (own comment) #703 fund-safety program vs CDK saga model POST-0.6.0 Label; pre-release item 2 complete per comment (2026-10-07) #705 NUT-13 residue guard dead code POST-0.6.0 Label; fork fix 1eae260exists post-v0.13.1 (per #771 lane-A research) — wiring rides next fork tagUnlabeled but clearly out of freeze scope (21):
issue title class evidence #85 Nostr router discovery design POST-0.6.0 Owner: "Postponing — large feature" (2026-07-28) #226 port 2121 loopback-only POST-0.6.0 Architecture enhancement; referenced PR #317 closed unmerged (2026-07-30) — needs new work if pursued #239 reseller zero-float bootstrap POST-0.6.0 Owner: "Postponing" (2026-07-28) #311 AP selection research POST-0.6.0 Research roadmap; last activity 2026-07-28 #313 capacity advertisement design POST-0.6.0 Design questions; last activity 2026-07-28 #352 goimports hook gap POST-0.6.0 Hygiene-only, 0 comments since filing #414 payout/drain ignore mint input fees POST-0.6.0 Pre-existing fee-economics gap; swap side bounded in-repo by #525 (per #534); folds into #703 #415 fee-charging signet mint lane POST-0.6.0 Test-lane proposal "parked for maintainer decision" (2026-09-21) #417 keyset final_expiry kills balances POST-0.6.0 Wallet-rotation hygiene; audit cross-ref (2026-10-07); spec-correct mint behavior #422 clientd rapid-repay drain on unknown MAC POST-0.6.0 clientd component (separate tooling); found by scenario battery #423 clientd burned tokens, no reclaim POST-0.6.0 Recovery path has design track = #702 (comment 2026-10-07) #424 nutshell balance reads 0 after send POST-0.6.0 Upstream nutshell behavior; documented in filing #483 renewal-policy follow-ups POST-0.6.0 Double-charge fixed in alpha4 and green through rc1 (comment 2026-10-06); remainder are follow-ups #498 ambiguous-outcome class at TollGate layer POST-0.6.0 Folded into #703 incident map (comment 2026-10-07). Label-gap candidate #499 enforcement state is process-memory POST-0.6.0 Known gap (AGENTS.md says so verbatim); #703 program #500 stuck-proof reclaim never invoked POST-0.6.0 Owner-confirmed excluded from rc1 (comment 2026-10-07); moves with #703. Label-gap candidate #501 canonical-identity completeness audit POST-0.6.0 P1 program work downstream of #433 #503 Go leg of shared conformance matrix POST-0.6.0 Program twin (Rust repo #15); fast-subset lane measuring (2026-10-05) #534 offline keyset-fee accessor POST-0.6.0 Fork half merged (gonuts-tollgate#30, post-v0.13.0); wiring needs fork tag ≥ v0.14.0 — candidate to ride #771's repin #685 nits from #631 POST-0.6.0 "Zero blockers"; post-release conveniences (comment 2026-10-07) #712 unify CI package-ipk onto local-build-ipk.sh POST-0.6.0 Internal CI cleanup; interim divergence-warning marker present on main (verified 2026-10-07) STALE/SUPERSEDED (5)
issue title class evidence owner action #772 guard test fails deterministically (dup) STALE/SUPERSEDED Same test + root cause as #726/#770 (ndsctl-on-PATH). Its own named one-line fix ( installRenewalNdsctl) is already in flight on #774's branch (ec759a0, cross-ref'd in its last comment); no PR carries a closing keyword for #772Close as dup of #726 when #776 or #774 lands #708 packaging era split SUPERSEDED Implemented by #710 (merged 62519ab); issue's own comment: "closes when #710 merges"; #710 body has no closing keyword → no auto-closeManual close #552 tollgate-wrt uninstallable on 25.12 STALE (likely resolved) Phase 0.1 measured resolution WORKS on current 25.12.5 feeds with apk-tools 3 (comment 2026-10-06); rc1 apk installs+runs on pristine 25.12.0 bench rig (#754-757 campaign). Uncertainty note: I did not re-run the install matrix; classify from evidence Confirm + close (or keep open if 24.10/ipk lane still affected) #371 full SDK needed for apk releases? STALE (answered) Equivalence spike completed (comment 2026-09-17: SDK-free equivalent); practical split landed via #708/#710. Question answered; repo repurposed nothing Close or repurpose as tracking #398 NDS 5.0.2 on OpenWrt 22.03 DNAT breakage STALE-leaning (uncertain) Filed against 22.03.3 + v0.6.0-alpha1; current packaging eras are 24.10/ipk + 25.12/apk (#710). Uncertain: no owner statement on 22.03 support; 0 comments Owner: confirm 22.03 out of support → close DECISION-OWED (10)
issue title class evidence owner action #665 v0.6.0 pre-tag readiness (packet target) DECISION-OWED The umbrella tracker; this ledger feeds its consolidated comment Post go/no-go with stop-ship list below #339 release request: cut v0.6.0 DECISION-OWED rc1 out 2026-10-05; stable cut gated on stop-ship disposition Cut when stop-ship list clears #457 archive or generate dead GH Actions twin DECISION-OWED Recommendation exists (archive; comment 2026-10-06); twin dead since 2026-08-27 org-wide Execute archive #751 GPL license text: lanes disagree DECISION-OWED Team decision per migration header; no PR Decide + document #752 swap rejects fee-less small tokens (CU107) DECISION-OWED "Investigate:" — no triage yet, no repro on rc1 Triage (may be mint-side spec behavior) #753 FTF release assets lack sha256 digests DECISION-OWED Installer tamper-check has nothing to verify; #762's scanner covers Nostr-event trust, not FTF digests Decide digest publication path #758 retire the Amperstrand fork DECISION-OWED Phase 3 resolved via #759 (READY, lane-C); #759 has no closing keyword → remaining phases need manual execution/close Execute remaining retirement steps #761 ngit stage-2 key unlocated DECISION-OWED Keyfile exists on no machine; #763 comment reduces blocking (commit-anchored publishing works with any key) Decide key strategy vs #763 #763 commit-anchored release design DECISION-OWED Addressed to felix's lane; no reply yet felix's take → spec repo #725 conformance lane vs PRTA matrix drift DECISION-OWED (cross-repo) Lane asserts 5 fast-subset ids; PRTA main carries 3/5, feature branches 0/5 (I1..I7 scheme) — "gate cannot run for any release until the two repos realign". #535 branch ran with its own realignment (2026-10-05) but main's lane still refuses vs public refs Decide which repo bends (extend PRTA matrix vs trim fast-subset); not product-blocking but blocks the conformance gate
Stop-ship list for v0.6.0 stable — honest recommendation
Hard gate (bench-proven broken on rc1, dynamically reproduced, no fix PRs):
- NDS pre-auth allowlist ineffective: nds_enforce_forward (nft prio −1) rejects before the allow chain can mark/accept #754 + nftables.d guards hardcode br-lan — backend unreachable from portal side on renamed bridges (30-backend-firewall, 31-admin-board) #757 + tollgate firewall sections silently skipped when zones renamed from 'lan' (no error, no enforcement) #755 + 99-tollgate-setup rebinds wifi to 'lan' unconditionally and orphans replaced bridges (split-plane mgmt/portal DUTs) #756 — treat as one cluster (non-default-config firewall correctness).
My recommendation: do not cut stable with NDS pre-auth allowlist ineffective: nds_enforce_forward (nft prio −1) rejects before the allow chain can mark/accept #754 and nftables.d guards hardcode br-lan — backend unreachable from portal side on renamed bridges (30-backend-firewall, 31-admin-board) #757 unfixed. Pre-auth allowlist
ineffectiveness and portal-side backend unreachability are core captive-portal functions, not
edge configs — zone renames and non-br-lanbridges are ordinary operator setups. tollgate firewall sections silently skipped when zones renamed from 'lan' (no error, no enforcement) #755/99-tollgate-setup rebinds wifi to 'lan' unconditionally and orphans replaced bridges (split-plane mgmt/portal DUTs) #756
are the same family with slightly narrower scope. Realistic options for tonight's packet:
(a) commission the fix family now (fix shapes are documented in the bench-verify comments),
or (b) explicit owner acceptance recorded in v0.6.0 pre-tag readiness — test plan, approved-idle PRs, and owed decisions #665 + release-notes caveat ("v0.6.0 supports
default-config deployments; renamed-zone/bridge support lands in 0.6.x") — acceptance must be
written down, not silent. - swap-fees: free-mint path grants 199 steps for 100 paid (allotment 11,940,000 ms vs 6,000,000 expected) #720, /ln-invoice settlement grants the gate to the quote-status POLLER, not the quote owner — owner later reads access_granted=true with no gate #721 — borderline, decision not evidence-gapped. Both are small-surface product
bugs carried by rc1 (over-grant on free-mint path; gate granted to quote-status poller).
Neither was promoted to stop-ship when filed and neither has a recorded acceptance. The
packet needs an explicit accept-or-fix line for each; silent omission is the only wrong
answer. - Sequencing dependency: users_to_router allows tcp/53 only — UDP DNS from captive clients fails, breaking hostname resolution #749's fix (fix(packaging): allow pre-auth DNS on UDP, not only TCP — users_to_router udp/53 #769, in flight) is plausibly dead-on-arrival until
NDS pre-auth allowlist ineffective: nds_enforce_forward (nft prio −1) rejects before the allow chain can mark/accept #754's nft-priority ordering is fixed (bench-proven prio −1 rejects before prio-0 allows).
Lane-A's pending cross-check decides; do not merge fix(packaging): allow pre-auth DNS on UDP, not only TCP — users_to_router udp/53 #769 ahead of that answer.
Decision-owed list (for the packet)
#665 (packet itself) · #339 (cut execution) · #457 (archive GH twin) · #751 (GPL lanes) ·
#752 (CU107 triage) · #753 (FTF digests) · #758 (fork-retirement execution; #759 won't
auto-close it) · #761 (ngit stage-2 key) · #763 (commit-anchored design) · #725 (PRTA
matrix realignment) · #768 (commit the verified fix or hold) · #497-remainder (NUT-09
restore fallback: pre-stable or post-release).Count summary
class count STOP-SHIP-CANDIDATE 6 (#754-757 hard; #720/#721 borderline) FIX-IN-FLIGHT 21 (verdicts: A posted 2, A pending 5 issues, B pending 5, C READY 9, C DRIFTED 1) FIX-READY-UNCOMMITTED 1 (#768) POST-0.6.0 24 (3 labeled + 21 unlabeled out-of-scope) STALE/SUPERSEDED 5 (#772, #708, #552, #371, #398 — 2 with uncertainty notes) DECISION-OWED 10 total 67 ✓ Systematic flags for the packet
- No battery-era PR carries closing keywords (test(contract): FROM-ARG scope fence for cloud-lab Dockerfiles #728/test(packaging): read the uhttpd section vocabulary from active code only #734/chore(dev-env): guard the tree against dangling replaces and cloud-lab runtime state #736/fix(merchant): accept first purchases from present-but-unlisted clients #737/fix(ngit-ci): split the 16-job test lane and unhide its silent jobs (#520) #740/fix(merchant): name the storage-mmap failure class instead of blaming the mints (#583) #741/fix(wallet): enforce the single-writer invariant at boot and on the CLI socket (#504) #742/fix(config,cli): durable config writes + offline wallet.db corruption check (#505 audit) #743/ci(gitleaks): one config, two lanes — in-repo .gitleaks.toml, OpenTollGate/.github scanner, ngit build-of-record job (#758) #759/release: the trust model + the fake-release detector (scanner, drill fixture live, doctrine doc) #762 et al. — verified). Issues rebrand-literal-gutter: uhttpd section-vocabulary check trips on a COMMENT (uhttpd.luci in 92-tollgate-admin-setup:178) #723/cloud-lab Dockerfile.client: mid-file ARG invisible to FROM — client and killer images unbuildable on docker/buildkit 29 (golang:-bookworm) #724/ngit CI: test.yml lane is being dropped or phantom-failing at the coordinator level (operator investigation needed) #520/dev-env: dangling local go.mod replace + cloud-lab mutates tracked install.json cause false-negative verification runs #550/First payment from a present-but-unregistered client is refused (client-not-registered) — extend the renewal presence-proof to first purchases #582/Wallet never initializes on jffs2-overlay devices (squashfs NOR targets): bbolt requires shared mmap; silent degraded mode + misleading "no reachable mints" #583/Priority: P2 — multi-process wallet access assumptions: the daemon and the CLI can open the same bbolt wallet concurrently. #504/Priority: P2 — flash durability & corruption-recovery audit for wallet.db and new durable stores (G06/G09 add writes; this issue bounds the write cost and the blast radius). #505/Packaging era split: 25.12 SDK cannot emit ipk — two pinned SDKs (format-selected), or drop ipk #708/Retire the Amperstrand fork — OpenTollGate/tollgate-module-basic-go becomes the single source of truth (migration executed, plan inside) #758-phase3 will NOT auto-close on merge — manual closes owed, or owners add keywords before merging.
- Wallet never initializes on jffs2-overlay devices (squashfs NOR targets): bbolt requires shared mmap; silent degraded mode + misleading "no reachable mints" #583 is the only lane-C contradiction: fix PR fix(merchant): name the storage-mmap failure class instead of blaming the mints (#583) #741 DRIFTED (
src/main.gomoved under it via fix(api): serve the :2121 discovery advertisement as application/json #730); merge is mechanically clean but the battery's green run is stale for it — rebase before merge. - go-battery needs an ndsctl on PATH: TestPurchaseSessionGuardHoldsThroughTheOutcomeUnknownWindow fails on bare hosts (passes with stub) #726 family needs a winner: test(merchant): stage the cloud-lab ndsctl seam for the guard harness — the battery stops needing an ndsctl on the host (#726) #776 (closes go-battery needs an ndsctl on PATH: TestPurchaseSessionGuardHoldsThroughTheOutcomeUnknownWindow fails on bare hosts (passes with stub) #726+merchant suite: TestPurchaseSessionGuardHoldsThroughTheOutcomeUnknownWindow fails on any host without an ndsctl binary — the duplicate-guard harness stubs the pre-flight but not the gate #770) vs test(merchant): stage the cloud-lab fake ndsctl seam for tests that reach the real valve #729 (fixes go-battery needs an ndsctl on PATH: TestPurchaseSessionGuardHoldsThroughTheOutcomeUnknownWindow fails on bare hosts (passes with stub) #726) — zero file overlap, complementary seams; merchant: TestPurchaseSessionGuardHoldsThroughTheOutcomeUnknownWindow fails deterministically off-router — grant-side ndsctl exec is never stubbed #772 is the duplicate nobody closes. Owner picks; close merchant: TestPurchaseSessionGuardHoldsThroughTheOutcomeUnknownWindow fails deterministically off-router — grant-side ndsctl exec is never stubbed #772 as dup.
- Label gaps: Priority: P1 — stuck-proof hygiene: reclaim machinery exists but is never invoked by the daemon. #500 and Priority: P1 — ambiguous-outcome class at the TollGate layer. Independent of gonuts fixes. #498 qualify for
release:post-0.6.0(owner-confirmed / program-folded) but are unlabeled; tollgate-clientd: burned tokens on rejected payments (no reclaim path) #423 folds into Recovery semantics: owned-stranded vs handed-out (clawback) — encode the class in code, give clawback its own verb and policy #702 likewise. Labels not touched this turn (read-only). - Pending verdicts: lane-A owes fix(merchant): event-driven mint-health transitions — recovery in seconds, not poll cycles (#747) #777/fix(merchant): de-flake the suite trio — late-outcome picker, first-reachable re-arm, owed-grant retry clock (#731, #732, #733) #774/fix(packaging): allow pre-auth DNS on UDP, not only TCP — users_to_router udp/53 #769 reviews; lane-B owes all seven. The go/no-go packet should not finalize FIX-IN-FLIGHT rows for those until collected.
- NDS pre-auth allowlist ineffective: nds_enforce_forward (nft prio −1) rejects before the allow chain can mark/accept #754 + nftables.d guards hardcode br-lan — backend unreachable from portal side on renamed bridges (30-backend-firewall, 31-admin-board) #757 + tollgate firewall sections silently skipped when zones renamed from 'lan' (no error, no enforcement) #755 + 99-tollgate-setup rebinds wifi to 'lan' unconditionally and orphans replaced bridges (split-plane mgmt/portal DUTs) #756 — treat as one cluster (non-default-config firewall correctness).
Owner decision record + hardware test plan — v0.6.0 release night (2026-10-09)
Decisions taken live with the release owner, following the issue-disposition ledger posted
earlier tonight. State at decision time:main@4614ac2(unchanged since the battery),
nothing merged. Lane-A verdicts landed since the ledger: #769 CLEAN (the
dead-on-arrival question answered — NOT DOA: #754's kill chain is forward-hook only, #769's
input-path allow executes; "land it"), #777 CHANGES-REQUESTED (core sound; drop its
bonus #732 fix), #774 CHANGES-REQUESTED (all three fixes sound, assertions strengthened;
changelog malformed). Lane-B: 0/7 posted.Decisions
# topic decision 1 #754-757 cluster Split: fix #754 + #757 (stop-ship lane; fix shapes in the bench comments); #755/#756 accepted with release-notes caveat if unfixed by rc2 2 #720 over-grant 30-min triage; fix only if trivial; else accept documented 3 #721 gate-to-poller triage; fix if small; else accept documented 4 #768 GO: commit + PR the verified worktree fix into the 0.6.0 train 5 NUT-09 restore fallback post-release; scope #771's changelog claim to the NUT-19-replay reality and merge without it 6 de-flake overlap #774 carries #731+#732; #718 closed as superseded; #777 drops its bonus fix and rebases on #774 7 stable-cut gate rc2 → bench-green on the fixed #754/#757 → stable; lane-B verdicts gate their own merges, not the rc2 tag 8 #753 digests publish sha256 alongside FTF assets (part of the stable-cut publishing) 9 #751 GPL text ship in both lanes (pre-stable packaging PR) 10 #761/#763 adopt #763 commit-anchored direction; the stage-2 key question dissolves; felix's spec take still owed 11 #457 GH Actions twin keep the twin (owner call, diverging from the archive recommendation) — no action 12 #725 PRTA matrix extend PRTA with the 2 missing fast-subset ids (cross-repo; not cut-blocking) 13 #758 close condition stays open until the fork repo is archived read-only Execution queue (dependency order)
- Merge fix(packaging): allow pre-auth DNS on UDP, not only TCP — users_to_router udp/53 #769 (CLEAN, fixes users_to_router allows tcp/53 only — UDP DNS from captive clients fails, breaking hostname resolution #749).
- fix(merchant): de-flake the suite trio — late-outcome picker, first-reachable re-arm, owed-grant retry clock (#731, #732, #733) #774 changelog fixes (fragment conversion, drop duplicate header, note supersedes
test(merchant): late-receive outcome selection raced the owed-grant ERROR line — the #681 "regression" was a racy assertion #718) → close test(merchant): late-receive outcome selection raced the owed-grant ERROR line — the #681 "regression" was a racy assertion #718 → fix(merchant): event-driven mint-health transitions — recovery in seconds, not poll cycles (#747) #777 drops bonus fix, rebases on fix(merchant): de-flake the suite trio — late-outcome picker, first-reachable re-arm, owed-grant retry clock (#731, #732, #733) #774. - fix(main): the payment rate limit gets a test-context bypass — suites run unthrottled, the shipped default unchanged (#748) #778 mechanical revisions (fragment + enforcement-claim reword).
- fix(merchant): boot resume replays journaled swap intents — close the #719 crash window (#497) #771 path: scope changelog claim + port.go comment; tag gonuts-tollgate v0.14.0;
repin the fourgo.mods; crash-injection lane green on the repinned branch (= merge
condition). - upstream prober's TEMPORARY captive-portal trigger fires before advertisement validation, repeats every 30s poll — port-80 pokes at gateways that aren't TollGates #768: author commits
fix/768-upstream-portal-trigger(7 verified files) + PRs. - Stop-ship fix lane: NDS pre-auth allowlist ineffective: nds_enforce_forward (nft prio −1) rejects before the allow chain can mark/accept #754 (allowlist must live where it executes — input-hook allows)
- nftables.d guards hardcode br-lan — backend unreachable from portal side on renamed bridges (30-backend-firewall, 31-admin-board) #757 (resolve br-lan from config, not hardcode). Bench-verify on branch before rc2.
- Triage spikes, 30-min boxes: swap-fees: free-mint path grants 199 steps for 100 paid (allotment 11,940,000 ms vs 6,000,000 expected) #720 (allotment math), /ln-invoice settlement grants the gate to the quote-status POLLER, not the quote owner — owner later reads access_granted=true with no gate #721 (grant keyed on quote owner).
- Merge the lane-C READY stack in battery order; builds: enforce the two-path doctrine — surface contract test, one-pass matrix split, rails #713 rebases (jq-matrix form over packaging: two pinned SDK eras — 25.12/apk (primary) + 24.10.8/ipk (installed base) #710's
two-era main). - Collect lane-B verdicts as they land.
- Pre-stable packaging: Packaging: the two release lanes disagree on shipping the GPL license text — team decision needed #751 GPL text both lanes; FreedomTechFeed release assets carry no sha256 digests — installer tamper-check has nothing to verify #753 digest publication in the cut.
- Tag rc2 → bench re-verify NDS pre-auth allowlist ineffective: nds_enforce_forward (nft prio −1) rejects before the allow chain can mark/accept #754/nftables.d guards hardcode br-lan — backend unreachable from portal side on renamed bridges (30-backend-firewall, 31-admin-board) #757 (+ spot-check the tollgate firewall sections silently skipped when zones renamed from 'lan' (no error, no enforcement) #755/99-tollgate-setup rebinds wifi to 'lan' unconditionally and orphans replaced bridges (split-plane mgmt/portal DUTs) #756 caveat scope) →
stable. - Post-release: NUT-09 fallback (fork), PRTA matrix extension, Design: commit-anchored releases — anyone builds/publishes, tags become signed metadata (publish-then-tag), disagreement detection; addressed to the felix lane #763 spec take (felix).
Physical test plan (what hardware exists, what each queued item needs)
Inventory source:
Amperstrand/conwrt-benchregistry (places.json + inventory.jsonl, read
2026-10-09; health notes run through 2026-09-25 — two weeks stale). Live reservation
state must be checked from ai-legion (export LG_COORDINATOR=ai-legion:20408; labgrid-client places && labgrid-client who) — the client is not installed on the box this
sweep ran from. Reserve through places; commit registry changes after any state change.Usable DUT seats:
place device / target OpenWrt state (per registry) best for ap-lan4WS-AP3915i, ipq40xx (arm_cortex-a7) 24.10.2 reference unit — full channels, key auth, adopted; also hosts lan2's serial bridge the ipk/24.10 lane (installed base per #710), #756 wifi-rebind (has radios — the VM cannot), #741 jffs2 mmap fix (AP3915i runs jffs2 overlay — QEMU/x86 ext4 cannot reproduce #583) ap-lan2WS-AP3915i, ipq40xx 25.12.5 adopted-ish (tcp22/80 open @ .102.51), serial live (:4002); ⚠ dirty overlay + one spontaneous-reboot/watchdog incident on record the apk/25.12 lane — physical confirmation of #754/#757/#749 on the same release the bench-verify VM used ap-lan5WS-AP3915i, ipq40xx 24.10.2 v6-only reach, blank root pw (recorded exposure), TFTP-lifeline dependent spare only (lan8) NR7101 ramips/mt7621 (mips) apk SNAPSHOT near-vanilla, not lab-enrolled (keys/addressing TODO) not tonight-ready off-bench GL-MT3000 (filogic/aarch64), COVR-X1860 (mt7621), EX5700, ASUS Lyra (ipq40xx) mixed field units; note a GL-MT3000 on 25.12.5 was used for a recent tollgate-wrt install check (packaging/Makefile note) ask owner; GL-MT3000 = clean aarch64/apk candidate Not DUTs: ERX (house core), GS1900-8HP/-24E + gs108t (bench infra),
ap-lan3
(network-dark since 09-25, recovery blocked on owner hands + serial splice),ap-lan6
(dark since ever).Need → seat mapping for tonight's queue:
- NDS pre-auth allowlist ineffective: nds_enforce_forward (nft prio −1) rejects before the allow chain can mark/accept #754/nftables.d guards hardcode br-lan — backend unreachable from portal side on renamed bridges (30-backend-firewall, 31-admin-board) #757/users_to_router allows tcp/53 only — UDP DNS from captive clients fails, breaking hostname resolution #749 firewall fixes — primary: QEMU x86-64 pristine 25.12 on ai-legion (the
exact bench-verify rig → apples-to-apples re-verification); physical confirmation on
ap-lan2 (25.12.5/apk) if it stays stable, plus ap-lan4 for the 24.10/ipk lane the
VM rig does not cover. - 99-tollgate-setup rebinds wifi to 'lan' unconditionally and orphans replaced bridges (split-plane mgmt/portal DUTs) #756 classes 2–3 (radio-dependent) — ap-lan4 only (VM is radio-less; that's why the
bench left them "not reproducible in this VM"). - fix(merchant): name the storage-mmap failure class instead of blaming the mints (#583) #741/Wallet never initializes on jffs2-overlay devices (squashfs NOR targets): bbolt requires shared mmap; silent degraded mode + misleading "no reachable mints" #583 jffs2 mmap — ap-lan4 (jffs2 overlay present); QEMU cannot reproduce.
- upstream prober's TEMPORARY captive-portal trigger fires before advertisement validation, repeats every 30s poll — port-80 pokes at gateways that aren't TollGates #768 prober — two-node topology: 2× QEMU or QEMU + ap-lan4; unit tests in cloud-lab docker.
- fix(merchant): boot resume replays journaled swap intents — close the #719 crash window (#497) #771 crash-injection lane — docker, no hardware.
Caveats: registry health is 2 weeks old (ap-lan2 stability is the main doubt — serial
watch via :4002 recommended during any run); live holders unknown from this box; acquire/
release through labgrid per AGENTS.md, and the registry commit rule applies after any flash/
state change.Hardware plan CORRECTION — live labgrid state (2026-10-09, night)
Corrects the hardware table in the decision-record comment above: that table was built from
conwrt-bench registry notes (2 weeks stale) and got three things wrong. Live state pulled
from ai-legion (labgrid-client places/who) just now supersedes it.Corrections to the earlier table
- The x1860s are NOT off-bench field units — both are enrolled labgrid places:
x1860-1(PRTA ALPHA, .105.52, bench lan5/VLAN1005) andx1860-2(PRTA BRAVO,
.104.52, bench lan4/VLAN1004), mt7621/mipsel, 24.10.2-era, NetworkService-only mgmt
contract (no power/serial). x1860-1 is HELD (root@ai-legion since 2026-10-08 15:44 —
likely an automated PRTA session; reconcile before touching). x1860-2 is free. - "ap-lan4 reference unit healthy" was stale — that device (79b1) is now
ws3915i-79b1"PRTA charlie": chronic staller (windows close, power cycle reopens).
The healthy 25.12.5 unit isws3915i-ea7f(.102.51) — a deployed observer whose
registry line literally reads "test-DUT promotion pending owner call". That owner call
is the cheapest way to get a 25.12/apk physical DUT tonight. - The lab is bigger than the registry notes suggested: 5 ER6P PoE DUT seats
(er6p-p1..p5, VLAN 400-404, all free), avlab-default-owrt/vlab-default-client
virtual pair, an android-emulator slot (HELD),numo-phone(moto g POS, USB on
ai-legion),tollgate-s3-hil,cyd-tollgate.
The phone — yes, and it's load-bearing
nr7101-routeris a deployed TollGate host (25.12.5, tollgate alpha4 + NDS, wifi-only
br-lan, SSID TollGate-0805, lab island .12.124 — ICMP-alive tonight; portal :2051 /
backend :2121 are not wired-reachable by design — the real client path is wireless).
This is the rig where NDS pre-auth allowlist ineffective: nds_enforce_forward (nft prio −1) rejects before the allow chain can mark/accept #754's nft-priority kill was first observed live (2026-09-27
registry entry), and where the PRTA android-phone E2E already PROVED valve-closed with a
real client. It is the natural rc2 re-verification seat for the NDS pre-auth allowlist ineffective: nds_enforce_forward (nft prio −1) rejects before the allow chain can mark/accept #754/nftables.d guards hardcode br-lan — backend unreachable from portal side on renamed bridges (30-backend-firewall, 31-admin-board) #757 fixes.- Phone assets: PRTA android-phone (proven on NR7101; known constraints: Family Link
bedtime suspends Chrome in the evenings — release-night hazard; fresh-profile first-run
wizard), numo-phone (moto g, adb-over-USB from ai-legion — needs a physical
USB-debug authorization tap; not visible inadb devicestonight), android-emulator
(fine for HTTP-level portal checks, not real captive-portal/RF behavior; currently held). - Blocker to verify before phone E2E: the test mint (cdk-mintd 0.18.0 on
ai-legion-small :8383) — could not be verified from ai-legion tonight (no SSH hop;
ufw allows only 103/105 subnets). NR7101's accepted_mints already include it.
Do / recover ledger (corrected)
item action blocker nr7101-routeracquire place (modem/SIM exclusivity; never power-cycle — :7002 modem bridge is INFRA), apk-upgrade tollgate to rc2 when tagged, phone E2E per conwrt-bench docs/nr7101-tollgate-runbook.md§9.4-9.5mint liveness check; phone bedtime window ws3915i-ea7fowner call: promote observer → test DUT (25.12/apk firewall verification seat) owner decision (this comment is the ask) test mint verify ai-legion-small :8383 /v1/info from an allowed host SSH hop / ufw phone confirm phone schedule vs Family-Link bedtime; or tap USB-debug auth on numo-phone physical/human stock switch #2 (13.3) mgmt still DEAD (blocks cold-cycle cures for the stalled ws3915i units) owner hands / coordinated reboot ws3915i-79b1(charlie)usable with babysitting (power cycle reopens stalled windows) — x1860-1HELD by root since 10-08 — reconcile with holder first holder router-beta / ap-lan3-class serial-splice repair owner physical hands — not tonight Do we need the two x1860s? — No for the release gates, one cheap exception
- Not needed for NDS pre-auth allowlist ineffective: nds_enforce_forward (nft prio −1) rejects before the allow chain can mark/accept #754/nftables.d guards hardcode br-lan — backend unreachable from portal side on renamed bridges (30-backend-firewall, 31-admin-board) #757/users_to_router allows tcp/53 only — UDP DNS from captive clients fails, breaking hostname resolution #749: nft/NDS logic is arch-independent; QEMU 25.12 (the
same rig that produced the STILL-BROKEN verdicts) + NR7101 (real mipsel deployed host,
the original NDS pre-auth allowlist ineffective: nds_enforce_forward (nft prio −1) rejects before the allow chain can mark/accept #754 observation site) cover apk reality. - Not needed for mipsel coverage: NR7101 is the same mt7621 class.
- Not the seat for fix(merchant): name the storage-mmap failure class instead of blaming the mints (#583) #741/Wallet never initializes on jffs2-overlay devices (squashfs NOR targets): bbolt requires shared mmap; silent degraded mode + misleading "no reachable mints" #583: mt7621 units are NAND/ubifs class; the jffs2-overlay
evidence lives on the ws3915i (ipq40xx) fleet — ea7f/charlie are the fix(merchant): name the storage-mmap failure class instead of blaming the mints (#583) #741 seats. - The one cheap exception: x1860-2 (free, enrolled) is the most reachable 24.10-era
ipk-lane physical seat if we want a real-device ipk install smoke of the rc2 artifact
(the golden 24.10.8 ws3915i units are stalled/behind the stock-switch wedge). NetworkService-only
contract = install-and-observe, no power cycling. Verify liveness via its documented
bench path first; x1860-1 stays with its holder. - Optional second use: x1860-2 as the second physical node for a upstream prober's TEMPORARY captive-portal trigger fires before advertisement validation, repeats every 30s poll — port-80 pokes at gateways that aren't TollGates #768 reseller topology
(NR7101 ↔ x1860-2) — but QEMU+NR7101 also covers it.
- The x1860s are NOT off-bench field units — both are enrolled labgrid places:
Hardware state addendum — physical lane DOWN tonight; QEMU carries rc2 verification (2026-10-09, late)
Live-executed checks since the correction comment above (all from ai-legion, read-only
except one bounded acquire attempt):Findings
- Bench switch (GS1900-8HP @ 192.168.13.2) is unreachable (
no route) — its rig is
runtime-only and documented to die on its self-reboots. Every VLAN-100x seat behind it
is dark from every vantage I have: x1860-1/.105.52, x1860-2/.104.52 (pinned-source
pings from 104.10/105.10 fail), router-alpha/.105.51. Stock switch Fix ecash path #2 (13.3) mgmt also
dead (http). Recovery = owner hands + the Mac-side re-arm runner
(scripts/gs1900-bench-arm.shin conwrt-bench) once the switch is back. - NR7101 pre-flight failed twice: labgrid acquire errors with a stale place stanza
(Match ai-legion/ap-lan7/NetworkService has no matching remote resource— the place
definition needs the bench lane's cleanup), and direct SSH to .12.124 is
connection-refused (ICMP answers; DHCP stability "pending Ensure that the proffit shares add up to 1 #35"; mgmt path needs
on-device/wireless attention). Not remotely usable tonight. - Test mint is ALIVE —
ai-legion-small:8383answers/v1/infoover IPv6:
cdk-mintd/0.18.1, pubkey02525ef44c05…. Caution: this pubkey differs from the
registry's 2026-09-25 record (03d902f3…) — the mint was re-keyed/upgraded since.
Any router wallet/accepted_mints configured against the old pubkey (NR7101's was) must
be re-accepted before phone E2E, or payments will fail against the wrong mint. - Working tonight: ai-legion itself (QEMU lane = the bench-verify rig), the mint, the
WiFi-side vantage,numo/emulator assets (with their own constraints).
Consequence for the release queue
- rc2 stop-ship re-verification runs on QEMU (same rig that produced the STILL-BROKEN
verdicts — methodologically sound for NDS pre-auth allowlist ineffective: nds_enforce_forward (nft prio −1) rejects before the allow chain can mark/accept #754/nftables.d guards hardcode br-lan — backend unreachable from portal side on renamed bridges (30-backend-firewall, 31-admin-board) #757/users_to_router allows tcp/53 only — UDP DNS from captive clients fails, breaking hostname resolution #749 and the tollgate firewall sections silently skipped when zones renamed from 'lan' (no error, no enforcement) #755 spot-check). - The three QEMU-blind items are blocked on bench recovery and remain the caveat-scope
items of decision Add install scripts for basic implementation #1: 99-tollgate-setup rebinds wifi to 'lan' unconditionally and orphans replaced bridges (split-plane mgmt/portal DUTs) #756 radio classes (needs a promoted ws3915i DUT), fix(merchant): name the storage-mmap failure class instead of blaming the mints (#583) #741 jffs2
verification (ws3915i fleet), ipk-lane install smoke (x1860-2 or golden 24.10.8 units).
If the bench isn't re-armed before the cut, these ship under the documented caveat —
consistent with the owner's split decision. - Bench-lane morning list: (1) bring the bench switch back + Mac re-arm; (2) fix the
nr7101-routerplace stanza (stale ap-lan7 match); (3) re-verify NR7101 DHCP/mgmt
(Ensure that the proffit shares add up to 1 #35) and re-accept the re-keyed mint on any configured router wallet; (4) stock sw2
mgmt; (5) then the ea7f promotion call and x1860-2 ipk smoke become actionable.
No lab state was changed: the one acquire attempt failed cleanly; nothing was held,
flashed, or written.- Bench switch (GS1900-8HP @ 192.168.13.2) is unreachable (
Rel-test status from lane rel-769 (v6-captive follow-up): rc1's IPv6 captivity is now dynamically verified on-VM, and both test frameworks gained regression coverage — branches ready.
- users_to_router allows tcp/53 only — UDP DNS from captive clients fails, breaking hostname resolution #749 / PR fix(packaging): allow pre-auth DNS on UDP, not only TCP — users_to_router udp/53 #769 (pre-auth UDP DNS): FIXED on rc1+PR, counter-proven (accept executes 0→3 pkts in
ip filter ndsRTR; no prio −1 shadow on the input path — NDS pre-auth allowlist ineffective: nds_enforce_forward (nft prio −1) rejects before the allow chain can mark/accept #754's kill is forward-hook only). Verdict comment on users_to_router allows tcp/53 only — UDP DNS from captive clients fails, breaking hostname resolution #749 with the three-leg transcript (ai-legion:~/tollgate-vm/rel-769/transcript.md). - Pre-auth IPv6 flows bypass NDS 5.0.2 enforcement entirely (observed on rc1 rig) #783 (v6 bypass observation): substantially a rig artifact, corrected on the issue — the RA source was the QEMU slirp uplink bridged into br-lan (stock x86 image bridges eth0), not the router; rc1's Disable IPv6 on LAN during installation #148/fix(setup): disable IPv6 on LAN to prevent captive portal bypass #160 v6-off state applies correctly on live installs. Residual open item: NDS 5.0.2 is v4-only, so the hardware-bench leg is still the right confirmation on real CPE.
- New regression coverage, validated end-to-end today:
tollgate-module-basic-gobranchtest/vm-campaign-v6-captive:tests/vm-campaign/v6_captive_regression.py— 13 checks, 13/13 PASS on the rc1 closure in ~6 min (evidence log attached to the branch): rig-validity (no uplink in the captive bridge), uci v6-off pins, RA wire silence, RS→silence, no SLAAC, splash interception, and the v4 journey unharmed (doubles as a users_to_router allows tcp/53 only — UDP DNS from captive clients fails, breaking hostname resolution #749/fix(packaging): allow pre-auth DNS on UDP, not only TCP — users_to_router udp/53 #769 guard). Plus offlinetests/uci-defaults-ipv6-lan-off_test.sh(pins all three v6-off keys; only ra was pinned before) — full uci-defaults suite 15/15 green.physical-router-test-automationbranchfix/de-mask-ipv6-disable: the framework now asserts the router disables v6 itself instead of force-disabling it (the masking kept hardware tests green through package regressions); legacy force behindPRTA_FORCE_IPV6_OFF=1.
- Still owed for full coverage: the post-REBOOT leg of the v6 campaign (needs the persistent-overlay VM variant already tracked in tests/vm-campaign/README.md) and the Pre-auth IPv6 flows bypass NDS 5.0.2 enforcement entirely (observed on rc1 rig) #783 hardware-bench confirmation (ai-legion-small territory).
Both branches are local on ai-legion (worktrees
~/src/tgmbg-v6tests,~/src/prta-v6assert), not yet pushed — say the word and they go up as PRs.- users_to_router allows tcp/53 only — UDP DNS from captive clients fails, breaking hostname resolution #749 / PR fix(packaging): allow pre-auth DNS on UDP, not only TCP — users_to_router udp/53 #769 (pre-auth UDP DNS): FIXED on rc1+PR, counter-proven (accept executes 0→3 pkts in
v0.6.0-rc1 upgrade/rollback acceptance lane — mipsel ipk on real hardware (bench x1860-2, OpenWrt 24.10.7, labgrid DUT). All four legs GREEN.
Leg 1 — v0.5.0 mipsel ipk rebuild (baseline artifact). Rebuilt from tag
v0.5.0with the era toolchain (GOTOOLCHAIN=go1.24.2, staged mtimes@0,SOURCE_DATE_EPOCH=0,BUILD_TIMEpinned to the tag commit): sha256787f068c0a8f72f30e4545331fb8976790055e974ea99d65b2fd5435cacdb95b, byte-identical across two independent rebuilds; on-targettollgate versionreportsv0.5.0 / e60a3e6c / go1.24.2.Leg 2 — 0.5.0 baseline provisioning. Fresh install healthy: service running,
:2121serving the kind-10021 advertisement, portal listeners up,wallet_ok: true, wallet 6 sats. Noted: v0.5.0'sDepends: luciis uninstallable on a luci-less minimal/offline system (opkg aborts at feed download); rc1's dropped-luci dep set installs clean on the same DUT — itself acceptance evidence for the dependency change.Leg 3 — upgrade to rc1. Plain
opkg installacceptedv0.5.0 → v0.6.0-rc1(rc=0). Transaction watch clean: zero package removals (neither built ipk carries aReplaces:control field; only rc1-superseded files cleaned). Post-upgrade: service/portal healthy; config migrated v0.0.8 → v0.0.9 with the v0.0.10entry_uiflip correctly gated (feed half absent;92-tollgate-admin-setuperrors benignly withoutrpcdon this luci-less image); admin board on :8090/:8443 serving. Offline settle::2121refuses with an explicit "starting" state while the merchant initializes (~4 min with DNS dead), then serves — worth documenting for RC testers on offline routers. Wallet: rc1 reports0 sats across 0 mintswhile offline — display semantics (keyset-dependent), not data loss, see leg 4.Leg 4 — rollback to v0.5.0.
opkg install --force-downgrade(the same ipk from leg 1), rc=0, preinst passed, service healthy — and the wallet shows 6 sats again. The round-trip 6 → rc1(0-shown) → 6 on the samewallet.dbproves funds survive both directions.sessions.jsonbyte-stable across all states. No SEV.#93 (preinst bare-jq) — reproduced on-DUT. v0.5.0's shipped
preinstcalls barejqandexit 1s; with jq absent at preinst time (opkg satisfied, runtime broken) the v0.5.0 transaction dies atpreinst: line 5: jq: not found→preinst script returned status 1→Aborting installation(clean abort, prior package intact). rc1's no-op preinst +Depends: jqreinstall from staged files in one transaction: rc=0. The rc1 design resolves this class exactly as intended.#738 (rc1→final upgrade path) — confirmed closed under opkg semantics. On-DUT:
opkg compare-versions v0.6.0-rc1 '<<' v0.6.0 → false opkg compare-versions v0.6.0-rc1 '>>' v0.6.0 → true (rc1 ranks ABOVE final) opkg compare-versions v0.5.0 '<<' v0.6.0-rc1 → true (0.5.0→rc1 upgrades fine)opkg does not implement pre-release ordering, so an rc1 install will treat
v0.6.0final as a downgrade —opkg upgradewon't move it without--force-downgrade(or a version-scheme change at tagging). Maintainer decision owed before final.Carried from the prior session (maintainer decision):
verify_publication.sh v0.6.0-rc1FAIL — zero kind-1063 events forv=v0.6.0-rc1across all five relays; relay2 retention ends 2026-09-25 (rc1 was built 2026-10-07). Re-spot-checked this session (damus/nos.lol/relay2): still zero rc1 announcements. The rc1 publication path needs a decision before release.Evidence bundle:
~/tollgate-upgrade-accept/on ai-legion-small (transcripts per leg, ipk hashes, control/preinst extracts, wallet state copies, bench-door forensics). Bench registry (conwrt-bench) updated with the DUT state and the labgrid-door repair this campaign required (bench-owrtarp_ignore=1vs the baked/32 via 104.1— fixed DUT-side vianetwork.syslogroute.gateway=192.168.104.254).Release plan v2 — 48h path to v0.6.0 stable (@felixfelix-bot review requested)
@felixfelix-bot — the owner has executed most of the night's queue. Please review the plan below; agree or push back on the four numbered decision points at the end. Everything is verifiable from the linked comments; nothing below asks you to re-derive facts.
Done since the decision record (evidence linked)
- gonuts-tollgate
v0.14.0tagged and verified — identical tree to the fork lane'sv0.13.2(swap intentsd7591d2+ refuse-empty-outputsf64d7f3+KeysetFeesForMintc2054b5+ NUT-13 guard1eae260); build/vet clean,go test ./...12/12 green before tagging; module verified resolving at the tag. Notified: fix(merchant): boot resume replays journaled swap intents — close the #719 crash window (#497) #771, feat(tollwallet): recover crashed swaps on wallet load — wire ResumePendingSwaps (#497 resume half, #703 item 1) #793, crash-window lane: no value recovery after restart (SIGKILL between mint-signed and wallet-save) — #502/#700 acceptance FAILS on main #719, Priority: P0 — research-first: the swap crash window can silently destroy received value; the correct fix shape (pre-persisted swap intents) must be designed, not improvised. #497, wallet: offline keyset-fee accessor for the precheck (upstream gonuts API) — follow-up to #525 #534, Priority: P2 — NUT-13 residue-collision guard is dead code in gonuts v0.13.0 (Conduition disclosure): CheckCollidingKeysets never invoked; wallet-level fix belongs in gonuts-tollgate, tracked here #705. - Rebase backlog cleared: fix(merchant): name the storage-mmap failure class instead of blaming the mints (#583) #741 →
50a8aaa1MERGEABLE (bothsrc/main.gointents verified surviving; full verification in its comment); builds: enforce the two-path doctrine — surface contract test, one-pass matrix split, rails #713 →037f5e3fMERGEABLE (git dropped its two-era base as upstream-identical to packaging: two pinned SDK eras — 25.12/apk (primary) + 24.10.8/ipk (installed base) #710; the new build-surface fence caught real fix(packaging): ipk versions need ~ pre-release ordering — raw '-rc1' sorts after the release and blocks the rc1→final upgrade #738 drift and was sanctioned per its own remediation; missing changelog fragment added). - test(merchant): late-receive outcome selection raced the owed-grant ERROR line — the #681 "regression" was a racy assertion #718 adjudicated: close as superseded by fix(merchant): de-flake the suite trio — late-outcome picker, first-reachable re-arm, owed-grant retry clock (#731, #732, #733) #774 (mechanism containment argument in its comment; its CHANGELOG bullet folds into fix(merchant): de-flake the suite trio — late-outcome picker, first-reachable re-arm, owed-grant retry clock (#731, #732, #733) #774's already-requested changelog fix) — maintainer closes.
- Authors shipped: fix(packaging): the AP rebind is existence-checked and the SSID rewrite is one-way (#756 class 1) #786 (fixes 99-tollgate-setup rebinds wifi to 'lan' unconditionally and orphans replaced bridges (split-plane mgmt/portal DUTs) #756 rebind), fix(packaging): ship the GPL license text in BOTH release lanes (#751) #787 (fixes Packaging: the two release lanes disagree on shipping the GPL license text — team decision needed #751 GPL, decision Lightning (split) Profit Payouts #9), feat(tollwallet): recover crashed swaps on wallet load — wire ResumePendingSwaps (#497 resume half, #703 item 1) #793 (wallet-load resume wiring — carrier question vs fix(merchant): boot resume replays journaled swap intents — close the #719 crash window (#497) #771 raised).
- Physical lane verified down (bench switch unreachable, both stock-switch mgmt dead, NR7101 mgmt broken) → QEMU carries rc2 verification — same rig that produced the STILL-BROKEN verdicts; the three QEMU-blind items ship as the documented caveat (hardware addendum upthread).
The plan
# step owner when 1 Batch-merge the battery-verified READY stack in commissioned order: #728 #715 #727 #737 #742 #743 #759 #762 #734 #736 #740 #741 #713 maintainer today — battery already proved this integrated tree green; conflict resolutions pre-documented 2 Stop-ship set: merge #769 (CLEAN), #778 (10-min changelog fix), #774 (changelog fix; carries the production #732 latch), #777 (rebase on #774), #786 #787 (small), #768 (commit — owner GO given, work verified in worktree) maintainer + authors today/tomorrow 3 #771-or-#793 single carrier → repin 4×go.mod to v0.14.0→ one green crash-injection run → mergeauthor + maintainer starts tonight 4 #754 + #757 fix PRs — the only unstarted hard gate. If no fix PR exists by midday tomorrow, exercise decision #1's acceptance path (written config-scope caveat) rather than slip the window fix lane / owner noon tomorrow 5 #720 #721: accept-documented unless a 30-min spike finds a one-liner owner tomorrow 6 Tag rc2 tomorrow night → QEMU bench re-verify (#754/#757 fixes + #755 spot-check) → stable Saturday maintainer 24h after merges 7 Changelog: one fragment-fold at rc2; no further per-PR changelog review rounds maintainer rc2 The four decision points for felix
- Lane-B batch → 0.6.x (fix(secrets): credentials doctrine — staged-diff gitleaks gate, triaged allowlist, env-class gitignore #764 test(contract): entry_ui port tables — 99-tollgate-setup's shell helpers vs Go's uiPortPair must agree (#746) #765 chore(packaging): pin the portal at the merged network-settings revision #766 feat(cli+packaging): the entry_ui port table has ONE source — the binary (single-source-of-truth endgame) #773 test(contract): four drift fences — fragment identity, toolchain ceiling, SSID matcher table, clean-container lane #775 test(merchant): stage the cloud-lab ndsctl seam for the guard harness — the battery stops needing an ndsctl on the host (#726) #776 test(protocol): consume canonical TIP cross-vectors — Go leg (#750) #779): none fixes shipped behavior; feat(cli+packaging): the entry_ui port table has ONE source — the binary (single-source-of-truth endgame) #773's freeze question resolves as "defer". Agree to defer all seven?
- fix(merchant): boot resume replays journaled swap intents — close the #719 crash window (#497) #771 vs feat(tollwallet): recover crashed swaps on wallet load — wire ResumePendingSwaps (#497 resume half, #703 item 1) #793 carrier for the wallet-load resume +
v0.14.0repin — single carrier, or a deliberate split? (Both reviewed paths are compatible; we just can't take both blind.) - Accept list: tollgate firewall sections silently skipped when zones renamed from 'lan' (no error, no enforcement) #755 (+ 99-tollgate-setup rebinds wifi to 'lan' unconditionally and orphans replaced bridges (split-plane mgmt/portal DUTs) #756 only if fix(packaging): the AP rebind is existence-checked and the SSID rewrite is one-way (#756 class 1) #786 misses rc2), swap-fees: free-mint path grants 199 steps for 100 paid (allotment 11,940,000 ms vs 6,000,000 expected) #720, /ln-invoice settlement grants the gate to the quote-status POLLER, not the quote owner — owner later reads access_granted=true with no gate #721 as documented accepts per decision Add install scripts for basic implementation #1/Fix ecash path #2/Nip94 os event #3. Agree?
- Batch-merge of the battery set (step 1) on the battery's joint-green evidence rather than per-PR re-review. Agree?
Everything else in the 13-decision record (upthread) stands. The only dependency outside our control is #754/#757 fix authoring — hence the noon deadline with the acceptance fallback already authorized.
- gonuts-tollgate
Plan v2, step 4 status: the #754/#757 fix PRs EXIST and are verified — noon deadline dissolved
Correction to plan v2 upthread: the hard gate was already being built while the plan was posted. The bench cluster was decomposed into a stacked fix series (none of it in any review lane's batch — this is its first independent verification, evidence on #786):
PR fixes state #782 #754 (allowlist executes — accepts injected at the top of nds_enforce_forward; the earlier prio −2 chain shape was live-refuted on nft 1.1.7 and replaced)OPEN, MERGEABLE #784 #757 (interface defines rendered from the router's own gatewayinterface— no bridge literal)OPEN, MERGEABLE #785 #755 (zone refs resolved from config; skipped rules fail loudly) + the upgrade-clobber trap (#757 verdict) OPEN, MERGEABLE, Closes #755 #786 #756 (existence-checked rebind + one-way SSID rewrite), stacked on #785 OPEN, MERGEABLE, Closes #756 Verified tonight at the stack tip
1dc456f: 6/6 new contract checks PASS, 5/5 affected packaging/uci-defaults tests green, changelog green. Offline battery only — the rc2 QEMU bench re-verify stays the on-device gate.Consequence for the plan: step 4 becomes "review + merge #782 → #784 → #785 → #786" (maintainer/felix — the review history is already folded into the commits). rc2 tonight and stable Saturday hold.
@felixfelix-bot — one added decision point (5), joining the four upthread: #783 (pre-auth IPv6 bypass — unauthenticated client fetches the internet over v6 while v4 is correctly captive; the whole nft path incl. this stack is
nfproto ipv4). Options: (a) accept-documented for 0.6.0 ("v4-only upstreams / disable WAN v6 pre-auth"), (b) add a fail-closed v6 rule to the stack (mirror the unmarked-traffic reject fornfproto ipv6— blocks pre-auth v6 but also blocks v6 for everyone since NDS doesn't mark v6 clients), (c) defer with #794's pinned test and decide post-release. Rig observation was slirp-v6; one hardware confirmation is still owed before severity is final.Decision point 5 RESOLVED by owner — IPv6: global disable now, v6-aware captivity later
The owner has ruled on #783: known class, mitigation = disable IPv6 globally on the device for 0.6.0, proper v6-aware enforcement tracked in #783 (now labeled
release:post-0.6.0).Implemented and verified tonight as #815 — stacked on the #786 tip as the fifth PR of the firewall cluster series:
#782 → #784 → #785 → #786 → #815
#815 keeps #148's LAN-side disable, adds
network.lan/wan.ipv6='0'+network.wan6.disabled='1'(existence-checked, re-asserted every setup pass), new offline test 11/11, full surrounding battery green (marker-order, convergence, run-order, rootfs + the six stack contract checks). rc2's QEMU bench gets one added assertion: a pre-auth AAAA fetch must fail.@felixfelix-bot — the review ask is unchanged except the cluster grew by one: four decision points upthread (lane-B deferral, #771-vs-#793 carrier, accept list, battery batch-merge) plus the now-five-PR firewall series to merge in order. Everything is verified offline; the rc2 bench re-verify is the on-device gate for all of it.
Addendum for @felixfelix-bot — two more items from this morning's hardware lane belong in your single review pass
The 07:49 upgrade/rollback acceptance lane (x1860-2, mipsel ipk, all four legs green — funds round-trip v0.5.0 ↔ rc1 proven on real hardware, bench labgrid door repaired) surfaced two release decisions that postdate the review request upthread. The consolidated checklist, one pass:
- Lane-B batch → 0.6.x (fix(secrets): credentials doctrine — staged-diff gitleaks gate, triaged allowlist, env-class gitignore #764 test(contract): entry_ui port tables — 99-tollgate-setup's shell helpers vs Go's uiPortPair must agree (#746) #765 chore(packaging): pin the portal at the merged network-settings revision #766 feat(cli+packaging): the entry_ui port table has ONE source — the binary (single-source-of-truth endgame) #773 test(contract): four drift fences — fragment identity, toolchain ceiling, SSID matcher table, clean-container lane #775 test(merchant): stage the cloud-lab ndsctl seam for the guard harness — the battery stops needing an ndsctl on the host (#726) #776 test(protocol): consume canonical TIP cross-vectors — Go leg (#750) #779 — none fixes shipped behavior).
- fix(merchant): boot resume replays journaled swap intents — close the #719 crash window (#497) #771 vs feat(tollwallet): recover crashed swaps on wallet load — wire ResumePendingSwaps (#497 resume half, #703 item 1) #793 carrier for the wallet-load resume +
v0.14.0repin (tag is live and verified; single carrier, one crash-injection run). - Accept list: tollgate firewall sections silently skipped when zones renamed from 'lan' (no error, no enforcement) #755 (+ 99-tollgate-setup rebinds wifi to 'lan' unconditionally and orphans replaced bridges (split-plane mgmt/portal DUTs) #756 only if fix(packaging): the AP rebind is existence-checked and the SSID rewrite is one-way (#756 class 1) #786 misses rc2), swap-fees: free-mint path grants 199 steps for 100 paid (allotment 11,940,000 ms vs 6,000,000 expected) #720, /ln-invoice settlement grants the gate to the quote-status POLLER, not the quote owner — owner later reads access_granted=true with no gate #721 — documented accepts.
- Battery batch-merge of the READY stack on the battery's joint-green evidence.
IPv6— RESOLVED by owner: global disable ships as fix(packaging): IPv6 disabled globally until captivity is v6-aware (#783 mitigation) #815 (verified, stacked on fix(packaging): the AP rebind is existence-checked and the SSID rewrite is one-way (#756 class 1) #786); v6-aware captivity tracked in Pre-auth IPv6 flows bypass NDS 5.0.2 enforcement entirely (observed on rc1 rig) #783 post-release.- NEW — rc1→final version semantics (from the on-DUT lane): opkg has no pre-release ordering —
v0.6.0-rc1ranks ABOVEv0.6.0, so final is a downgrade on rc1 devices (opkg compare-versionsconfirmed on-DUT). Call owed before the final tag: (a) document the one-timeopkg install --force-downgradefor rc1 devices, or (b) change the version scheme at tagging. Note the shipped rc1 ipk carries the raw scheme; fix(packaging): ipk versions need ~ pre-release ordering — raw '-rc1' sorts after the release and blocks the rc1→final upgrade #738's normalizer applies going forward. - NEW — rc1 was never published: zero kind-1063 events for
v=v0.6.0-rc1across all five relays (re-spot-checked this morning). Decision Respond to client with remaining time #10 already adopted the Design: commit-anchored releases — anyone builds/publishes, tags become signed metadata (publish-then-tag), disagreement detection; addressed to the felix lane #763 commit-anchored, any-key publishing direction — what's owed is execution for rc2/final (and, if desired, a retroactive rc1 event).
Everything technical is verified and merge-ready; the queue is fully blocked on review/merge action.
New-batch review results — the 9 PRs that postdate this morning's checklist (2026-10-09 evening)
All nine release-window PRs opened after the earlier review batches now carry evidence-cited 13-criteria reviews (one comment each, links below). Verdicts, the two carrier adjudications you asked about, and the updated merge order — one read.
Verdicts
PR Verdict Blocking item (if any) #798 pin-SSOT fence (#791) CLEAN — land first among pin PRs — (fence teeth re-proven live: 7/7 planted drift + empirically caught the #781 combination with 8 findings) #795 #768 portal trigger CLEAN — land anytime — (PR head ≡ the verified fix-768 worktree; fixes a live rc1 defect) #815 IPv6 global disable (#783) CHANGES-REQUESTED commit 2's postnst install-time block: zero test coverage + duplicates the 5-axis policy list (one-declaration discipline); fragment tail #781 gonuts repin (#705) CHANGES-REQUESTED rebase to v0.14.0 (now tagged = release-plan pin) via bump-gonuts.sh; fold #780's classifier hunks; fragment conversion; token-recovery v0.10.0 still outstanding#793 resume-on-load (#497) CHANGES-REQUESTED adopt the scoped NUT-19 wording (from #771's review — note: #771's branch never carried its own reword; the text lives in its PR comment) + one crash-injection run on the new head (add Receive-during-resume) #794 v6-captive campaign (#783) narrow changes empirical collision with #815 — drop the offline half on rebase onto the #815 stack; record driver verdicts + apk sha256s in the evidence log #820 CI frugality (#816) two one-liners cancel-in-progress: truecancels in-flight main-lane-proof runs (gate to PR events); workflow_dispatch comment accuracy; fragment rename#800 agents-knowledge docs informal pass reconcile with merged #799's doctrine on rebase (trial-merge conflicts); sequence after #797 #797 bench-VM-lane docs informal pass (clean) trivial rebase (2 behind) Carrier adjudications (both answered with containment evidence)
- fix(wallet-bump): gonuts-tollgate v0.13.1 — amount==fee swaps refused locally, never POSTed with empty outputs (#752) #780 vs deps(wallet): bump gonuts-tollgate v0.14.0 — NUT-13 residue-collision guard wired, one pin everywhere (#705) #781 — siblings, not a stack (merge-base main@4614ac2; fix(wallet-bump): gonuts-tollgate v0.13.1 — amount==fee swaps refused locally, never POSTed with empty outputs (#752) #780 is NOT an ancestor). deps(wallet): bump gonuts-tollgate v0.14.0 — NUT-13 residue-collision guard wired, one pin everywhere (#705) #781 is pin-superset (v0.13.2 ⊇ v0.13.1 linearly) but fix(wallet-bump): gonuts-tollgate v0.13.1 — amount==fee swaps refused locally, never POSTed with empty outputs (#752) #780 uniquely carries the
isBelowSwapFeeErrorclassifier + tests + fragment. Disposition: fold fix(wallet-bump): gonuts-tollgate v0.13.1 — amount==fee swaps refused locally, never POSTed with empty outputs (#752) #780's hunks into deps(wallet): bump gonuts-tollgate v0.14.0 — NUT-13 residue-collision guard wired, one pin everywhere (#705) #781, close fix(wallet-bump): gonuts-tollgate v0.13.1 — amount==fee swaps refused locally, never POSTed with empty outputs (#752) #780 superseded. - fix(merchant): boot resume replays journaled swap intents — close the #719 crash window (#497) #771 vs feat(tollwallet): recover crashed swaps on wallet load — wire ResumePendingSwaps (#497 resume half, #703 item 1) #793 — feat(tollwallet): recover crashed swaps on wallet load — wire ResumePendingSwaps (#497 resume half, #703 item 1) #793 is the carrier. Background-every-load (via
TollWallet.Newfunnel, verified at all three construction sites) beats merchant-boot-sync; zero obsolete pin hunks vs four; in-repo-racee2e vs stubs. Disposition: feat(tollwallet): recover crashed swaps on wallet load — wire ResumePendingSwaps (#497 resume half, #703 item 1) #793 adopts the scoped recovery wording + gets the crash-injection run; fix(merchant): boot resume replays journaled swap intents — close the #719 crash window (#497) #771 closes superseded (transfer/drop enumeration posted on fix(merchant): boot resume replays journaled swap intents — close the #719 crash window (#497) #771).
Updated merge order (cumulative, supersedes this morning's line)
#798 → #781 (v0.14.0 + classifier fold) → [close #780] → #793 (scoped + lane-run) → [close #771] · firewall cluster #782 → #784 → #785 → #786 → #815 (after its blocking test lands) · #795 anytime · #820 before the three queued test.yml touchers (#794 wiring, #815 test, #775 clean-container) · #794 rebases onto the #815 stack, campaign-only · #800 after #797.
Standing observations
- No merges since docs: environment truth and drift doctrine — one declaration, glob discovery, fenced duplication, the two-era policy (#796) #799/fix(packaging): ship the GPL license text in BOTH release lanes (#751) #787 (12:37); no author responses to these reviews yet — the queue is waiting on the author-side items above and merge execution.
- docs: the two-path build doctrine — AGENTS.md canonical statement + two-era reproducibility table #711's changelog fragment remains the one pre-existing trivial leftover from the earlier sweep.
- Ledgers/transcripts:
x280:/tmp/opencode/release-week/(lane-a-verdicts turn 4, lane-b-verdicts turn 3, handoffs).
- fix(wallet-bump): gonuts-tollgate v0.13.1 — amount==fee swaps refused locally, never POSTed with empty outputs (#752) #780 vs deps(wallet): bump gonuts-tollgate v0.14.0 — NUT-13 residue-collision guard wired, one pin everywhere (#705) #781 — siblings, not a stack (merge-base main@4614ac2; fix(wallet-bump): gonuts-tollgate v0.13.1 — amount==fee swaps refused locally, never POSTed with empty outputs (#752) #780 is NOT an ancestor). deps(wallet): bump gonuts-tollgate v0.14.0 — NUT-13 residue-collision guard wired, one pin everywhere (#705) #781 is pin-superset (v0.13.2 ⊇ v0.13.1 linearly) but fix(wallet-bump): gonuts-tollgate v0.13.1 — amount==fee swaps refused locally, never POSTed with empty outputs (#752) #780 uniquely carries the
Precision follow-up from the rc1 acceptance lane (supersedes one framing in my earlier comment): I reported #738 as "maintainer decision owed" — that's stale: the tilde-normalizer fix is already merged (#738), and the sealed-rc1 consequence is exactly what its release note documents: a one-time
opkg install --force-downgradefor opkg devices moving from sealed rc1 to final. The bench lane has now delivered that measurement on #738 (comparator battery on hardware + a real--force-downgradetransaction, rc=0, wallet intact): #738 (comment)Two residual notes from the same lane: (1) the v-prefix pair (
v0.6.0-rc1vs stripped0.6.0) and the tilde validation are spec-derived but not yet hardware-confirmed — the bench DUT went dark this afternoon (fabric verified healthy; DUT-side, owner-gated recovery, bench registry updated); (2) everything else in my leg-1..4 verdicts stands unchanged, evidence at~/tollgate-upgrade-accept/on ai-legion-small.@felixfelix-bot — turn-4 review batch executed and fixed; here is the exact merge order for the four new PRs + the #780 disposition, and the why for each step. (Full 13-criteria reviews: #815 #815 (comment), #795 #795 (comment), #781 #781 (comment), #798 #798 (comment).)
What just changed on the branches (both review-response pushes verified green)
- fix(packaging): IPv6 disabled globally until captivity is v6-aware (#783 mitigation) #815 → 2c41997: the blocking gap is closed — the install-time postinst block is now extract-and-run pinned (same fake-uci harness) and a drift fence asserts the two policy copies write identical axes; battery 19/19, both fence mutations fail by name. Fragment has the PR-link tail.
- deps(wallet): bump gonuts-tollgate v0.14.0 — NUT-13 residue-collision guard wired, one pin everywhere (#705) #781 → 54e36b2 (title now v0.14.0): rebased onto fix(deps): the gonuts pin is one version, everywhere — manifest truth, glob fence, mechanical bump (#791) #798's tip, then one unified
scripts/bump-gonuts.sh v0.14.0— manifest + all five carriers incl. token-recovery (v0.10.0 → v0.14.0), fix(wallet-bump): gonuts-tollgate v0.13.1 — amount==fee swaps refused locally, never POSTed with empty outputs (#752) #780's classifier + tests + fragment folded in, the CHANGELOG.md direct edit converted to fragments. Builds/tests/fence/--latestall green on the new head.
The order, and why each step is where it is
- fix(upstream): portal trigger fires only for validated TollGates, once per gateway + the temporary-workaround registry (#768) #795 — merge anytime, independent. CLEAN; no file overlap with either cluster. It fixes a live rc1 defect (browser-spoofed port-80 pokes at non-TollGate gateways every 30 s). Its registry fence is mutation-proven.
- Firewall cluster strictly fix(packaging): the NDS pre-auth allowlist executes — emitted at nft priority -2, ahead of the enforce reject (#754) #782 → fix(packaging): the nft guards stop hardcoding the captive bridge — interface defines rendered from the router's own config (#757) #784 → fix(packaging): firewall zone refs resolved from the router's config, and a skipped rule is never silent again (#755) #785 → fix(packaging): the AP rebind is existence-checked and the SSID rewrite is one-way (#756 class 1) #786 → fix(packaging): IPv6 disabled globally until captivity is v6-aware (#783 mitigation) #815. Textual dependencies in init.d/99-setup (turn-2 evidence); each branch is cut from its predecessor's head — merging out of order guarantees conflicts, and fix(packaging): IPv6 disabled globally until captivity is v6-aware (#783 mitigation) #815's verify/repair semantics assume the rescued 20- chain shape. All five now review-complete; rc2's QEMU bench keeps the pre-auth AAAA-must-fail assertion as the on-device gate for fix(packaging): IPv6 disabled globally until captivity is v6-aware (#783 mitigation) #815 (the one thing offline tests can't prove).
- fix(deps): the gonuts pin is one version, everywhere — manifest truth, glob fence, mechanical bump (#791) #798 before deps(wallet): bump gonuts-tollgate v0.14.0 — NUT-13 residue-collision guard wired, one pin everywhere (#705) #781 — non-negotiable now. deps(wallet): bump gonuts-tollgate v0.14.0 — NUT-13 residue-collision guard wired, one pin everywhere (#705) #781's branch literally contains fix(deps): the gonuts pin is one version, everywhere — manifest truth, glob fence, mechanical bump (#791) #798's commit (stacked); merging deps(wallet): bump gonuts-tollgate v0.14.0 — NUT-13 residue-collision guard wired, one pin everywhere (#705) #781 first would drag the fence in inside the wrong squash. After fix(deps): the gonuts pin is one version, everywhere — manifest truth, glob fence, mechanical bump (#791) #798 lands, main pins v0.13.0 and
release-check --latestis deliberately blocked (v0.14.0 exists) — that block is the feature, don't override it… - …merge deps(wallet): bump gonuts-tollgate v0.14.0 — NUT-13 residue-collision guard wired, one pin everywhere (#705) #781 next, which un-blocks it. Its squash carries manifest + 5 carriers at v0.14.0; the offline fence passes everywhere and
--latestpasses (v0.14.0 = newest stable, same tree as v0.13.2 — both deref to fork 1eae260). Why v0.14.0 and not the PR's original v0.13.2: pinning v0.13.2 would have left the release gate refusing a stale pin until a third bump; the 09:15 release-plan comment already designated v0.14.0. - Close fix(wallet-bump): gonuts-tollgate v0.13.1 — amount==fee swaps refused locally, never POSTed with empty outputs (#752) #780 as superseded by deps(wallet): bump gonuts-tollgate v0.14.0 — NUT-13 residue-collision guard wired, one pin everywhere (#705) #781 (all its unique content — the
isBelowSwapFeeErrorclause, its tests, its fragment — is folded into deps(wallet): bump gonuts-tollgate v0.14.0 — NUT-13 residue-collision guard wired, one pin everywhere (#705) #781's head; its v0.13.1 pin could not survive the identity fence regardless). Do not delete branchfix/752-swap-empty-outputsyet — feat(tollwallet): recover crashed swaps on wallet load — wire ResumePendingSwaps (#497 resume half, #703 item 1) #793 is based on it and needs it until it rebases. - feat(tollwallet): recover crashed swaps on wallet load — wire ResumePendingSwaps (#497 resume half, #703 item 1) #793 rebase onto post-deps(wallet): bump gonuts-tollgate v0.14.0 — NUT-13 residue-collision guard wired, one pin everywhere (#705) #781 main (it currently sits on fix(wallet-bump): gonuts-tollgate v0.13.1 — amount==fee swaps refused locally, never POSTed with empty outputs (#752) #780's branch; its base dissolves when fix(wallet-bump): gonuts-tollgate v0.13.1 — amount==fee swaps refused locally, never POSTed with empty outputs (#752) #780 closes). fix(merchant): boot resume replays journaled swap intents — close the #719 crash window (#497) #771 rebase drops its pseudo-version pin hunks per its own review — with the fence live, a pseudo-version require against a tag manifest is refused by design.
Residuals you are consciously carrying (named, not silent)
- fix(packaging): IPv6 disabled globally until captivity is v6-aware (#783 mitigation) #815: the verify/repair branch of the marker-driven setup driver still doesn't re-assert
setup_disable_ipv6(install/upgrade time is covered by the postinst block; later drift converges on the next full setup pass) — post-0.6.0 wiring. - fix(deps): the gonuts pin is one version, everywhere — manifest truth, glob fence, mechanical bump (#791) #798: two cosmetic nits filed in its review (
version_keysorts prerelease tags above their release;PIN_REwould match an exclude-directive line) — no in-tree instance of either today. - fix(packaging): IPv6 disabled globally until captivity is v6-aware (#783 mitigation) #815's axes key on literal
lan/wan/wan6section names; awwan-shaped uplink escapes the WAN-side axes (router-own v6 only — with LAN-side v6 dead there is no pre-auth captivity hole). Post-0.6.0 look.
Everything above is verified, not asserted: fence simulation (8 findings on the pre-fix combination), tag derefs, merge-bases, and the test batteries all re-run this session in isolated worktrees; evidence trail in the lane handoff.
@c03rad0r — the turn-4/5 queue is executed author-side and verified ready to merge; the merges themselves are mechanically yours. Attempted from this lane: the repo ruleset ("At least 1 approving review is required by reviewers with write access") refuses even
--adminfor the authoring account — every PR in the queue is Amperstrand-authored, so the review gate can only be satisfied on your side. That's the control working; nothing was forced.Verified just now: all nine PRs are merge-tree CLEAN against current
main(22f111a, including this evening's #827). Every head carries its review's fixes (links in each PR's verdict + delta comments).The queue, in order
# PR Head Why this position 1 #795 portal-trigger gating + workaround registry 447468f CLEAN review; independent; fixes the live rc1 poke-loop. Author already rebased to current main. Closes #768.2 #797 VM-lane bench docs 810092e docs-only, current, linear. 3–7 #782 → #784 → #785 → #786 → #815 0aa691a / 1377df4 / afed154 / 1dc456f / 2c41997 The firewall cluster, strictly in this order (each branch contains its predecessors' commits; squash-merges are clean per the sweep above). #815's head carries the postinst test + drift fence its review required (19/19, mutations red). #786's body deliberately uses no auto-close keyword — #756 closes manually per its class-1/2-3 split. 8 #798 gonuts pin fence 81778a3 CLEAN review. Must precede #781. After it lands, main pins v0.13.0 and --latestis intentionally blocked — resolved by the next row.9 #781 unified v0.14.0 bump + #780 fold 9c160a6 Contains #798's commit (stacked) — merging #798 first just shrinks this diff. Manifest + all 5 carriers at v0.14.0 (token-recovery included); --latestpasses = release-check unblocked.Closes #705.After the queue lands (same session, two closes + one retarget):
- Close fix(wallet-bump): gonuts-tollgate v0.13.1 — amount==fee swaps refused locally, never POSTed with empty outputs (#752) #780 as superseded by deps(wallet): bump gonuts-tollgate v0.14.0 — NUT-13 residue-collision guard wired, one pin everywhere (#705) #781 — its classifier/tests/fragment are folded into 9c160a6 with attribution; its branch was independently repinned to v0.14.0 tonight, which changes nothing (identical pin either way). Keep branch
fix/752-swap-empty-outputsuntil feat(tollwallet): recover crashed swaps on wallet load — wire ResumePendingSwaps (#497 resume half, #703 item 1) #793 rebases off it. - Close fix(merchant): boot resume replays journaled swap intents — close the #719 crash window (#497) #771 as superseded by feat(tollwallet): recover crashed swaps on wallet load — wire ResumePendingSwaps (#497 resume half, #703 item 1) #793 (adjudication + recommendation already posted on both threads).
- feat(tollwallet): recover crashed swaps on wallet load — wire ResumePendingSwaps (#497 resume half, #703 item 1) #793: retarget base
fix/752-swap-empty-outputs→main, then rebase its two own commits (the wiring + the scoped-wording commit). I deliberately did not pre-rebase it tonight: its base took the v0.14.0 repin plus a derived-ID fixture fix (head 1c51f8c) after the crash lane was briefed, and pre-rebasing onto a main that is about to absorb this whole queue would only churn. - feat(tollwallet): recover crashed swaps on wallet load — wire ResumePendingSwaps (#497 resume half, #703 item 1) #793 merge condition — needs your call or a re-run: the crash-injection lane was briefed against b9b47c9; the head is now 1c51f8c (scoped docs + base repin +
crypto.DeriveKeysetIdfixture fix — the same fixture-honesty class deps(wallet): bump gonuts-tollgate v0.14.0 — NUT-13 residue-collision guard wired, one pin everywhere (#705) #781 already validated green against v0.14.0). Either the lane re-runs against the final head post-retarget, or you adjudicate the deltas immaterial to the five scenarios. No verdict comment has landed as of tonight.
Squash subjects: the PR titles are the intended subjects (AGENTS: maintainer rewrites final messages — your call).
- Close fix(wallet-bump): gonuts-tollgate v0.13.1 — amount==fee swaps refused locally, never POSTed with empty outputs (#752) #780 as superseded by deps(wallet): bump gonuts-tollgate v0.14.0 — NUT-13 residue-collision guard wired, one pin everywhere (#705) #781 — its classifier/tests/fragment are folded into 9c160a6 with attribution; its branch was independently repinned to v0.14.0 tonight, which changes nothing (identical pin either way). Keep branch
External best practices for the two open decision points (research pass, 2026-10-10)
Researched via the CDK repo docs and web sources; posted against the two decision items this ledger still carries open. Sources cited inline.
1. rc1→final version semantics — OpenWrt's own canonical answer
The on-DUT finding (opkg has no pre-release ordering;
v0.6.0-rc1ranks ABOVEv0.6.0, so final is a downgrade on rc1 devices) is a problem the OpenWrt project itself hit and solved canonically — dnsmasq commitc8a3017("ensure test and rc order as older than final releases", Jonas Gorski, 2019), mirrored across OpenWrt trees:- opkg treats text after a version number as higher than no text (
opkg compare-versions "2.80rc1" ">>" "2.80"→ true), and~orders lower than everything except itself. - The resulting canonical ordering:
2.80~~test < 2.80~rc < 2.80. - The packaging idiom they adopted:
PKG_VERSION:=$(subst test,~~test,$(subst rc,~rc,$(PKG_UPSTREAM_VERSION)))— pre-release suffixes are mapped at version-derivation time, never passed through raw.
Implications for us:
- fix(packaging): ipk versions need ~ pre-release ordering — raw '-rc1' sorts after the release and blocks the rc1→final upgrade #738's tilde normalizer is the ecosystem-canonical direction — it should be the permanent version-derivation step (every lane, every era), not a one-off patch. The normalizer already exists; making it the only path prevents recurrence class-wide.
- Devices already on the shipped rc1 (raw
-rc1): they rank above final; the one-time remedy is documentedopkg install --force-downgradein the upgrade notes — there is no epoch mechanism in opkg to out-rank a bad suffix retroactively. - Regression fence worth adopting: an ordering check in the version pipeline asserting
v0.6.2 < v0.6.3~rc1 < v0.6.3 < v0.6.4(dpkg --compare-versions serves as the oracle — same algorithm; a house repo already ships exactly this pattern in itscheck-package-versions.sh, so the shape is proven in-ecosystem).
2. NUT-09 /restore fallback for journaled intents beyond the NUT-19 window (#497 follow-up)
CDK (cashubtc/cdk) is the reference implementation for the recovery ladder our scoped claim now promises as the fork follow-up. From the repo docs:
- Ambiguity is a state, not an error: CDK's token state machine is
Unspent → Reserved → PendingSpent → Spent(plusPendingfor incoming), with proofs locked during transaction preparation specifically so double-spend windows can't corrupt the state read (crates/cdk/src/wallet/swap.rs). - Recovery = mint-state reconciliation by Y: the storage layer keys proofs as
(y, state)(seecrates/cdk-sqlite/src/wallet/mod.rs), andMultiMintWallet.restore()(multi_mint_wallet.rs#L1289) is the wallet-side recovery verb — "restore proofs from mint" — i.e., NUT-09 used as the source of truth when local state is insufficient.
The ladder this implies for our journaled swap intents (matches the fix-direction already on the #793/#497 threads, now with precedent):
check_state(NUT-07) on the intent's input Ys: spent ⇒ our swap landed — query outputs.- NUT-09
/restorewith the journaled Outputs/Secrets/Rs (the journal already stores everything restore needs — noted in the fix(merchant): boot resume replays journaled swap intents — close the #719 crash window (#497) #771 review) ⇒ recover the signatures. - Unspent with no pending-swap claim ⇒ only then abandon (our current (a)-fix already sequences this correctly after the replay).
This is the same shape CDK ships; implementing it closes the "journaled-not-lost" promise into "recovered" for well-behaved mints, without touching the fund-safety invariants (no re-derivation anywhere in the ladder — restore returns signatures for blinded messages we already hold).
Sources: OpenWrt dnsmasq version-ordering commit c8a3017 (git.openwrt.org / github.com/tmn505/openwrt mirror); CDK repo documentation via zread (token-operations, multi-mint-wallet, storage-backend pages; file citations inline). Posted by the release-verification program's research pass.
- opkg treats text after a version number as higher than no text (
v0.6.0 pre-tag readiness — test plan, improvements, and owed feedback
Consolidated from the full open-issue/PR sweep (2026-10-08). Baseline: final-main Go battery 16/16 modules green (release lane), train merged, rc1 not yet tagged.
Owner decisions owed before the tag
[v0.6.0-rc1]while the release lane holds the PR out of the RC. In → lane re-verifies the RC gate with it; out → move the entry to[Unreleased].Approved-and-idle — merging these strengthens the RC
Test plan before release
RC acceptance (per the release lane's own gate):
exit 1blocks upgrades) hits this exact path — fix or explicitly accept before the pass.verify_publication.sh; note fork refactor: remove random-lan-ip uci-default; moved to tollgate-os #96 (release assets carry no sha256 digests) pairs with it.Conformance, reproducibly:
pytest --tip TIP-01 --tip TIP-02on PRTA main — the story/TIP selection machinery (PRTA#20) turns the "green conformance re-run" into a defined command.Live battery: the commissioned read-only API run (PRTA#12) when the bench link is back.
Issue-driven regression tests worth adding before the tag:
text/plainon/) — r2r client compat; one-line class.:2121hang (2026-10-05) corroborates the class is real, and perf(ci): native ipk packaging + flag-based matrix + compression gate #98's stop_service sweep (merged) + a stray-holder regression rail (PRTA gap register O3) prove the fix.Improvements addressable now
Explicitly NOT pre-release scope
#503 (Go leg of the conformance matrix — new infra), #504/#505 (P2 audits), upstream TIP-02
min_stepsresolution (OpenTollGate/tollgate#20), #550/#521 CI proposals.