the remote-control surface said things that were not true, and two of the guards for that were false green - #545
Merged
Merged
Conversation
…same thing
`POSIXErrorCode(rawValue: 61): Connection refused` reached the phone's UI,
untranslated, in an app that ships six languages. Eight sites in
`LANRemoteScreens` rendered `"\(error)"` straight into `error`/`status`,
plus two hardcoded English sentences ("Declined on the Mac", "Mac not
found on this Wi-Fi").
WHAT THE OLD CODE THREW AWAY. Both of the interesting failures arrive as
`.waiting` and were flattened into `handshakeFailed("\(e)")` — one string,
so the UI could not tell them apart. Measured here on macOS 26.5, loopback
`NWListener`, pinned suite:
wrong PSK → tls(-9820) "bad MAC" → pair again
nothing listening → posix(61) refused → wake the Mac
Opposite instructions to the user, and the phone said neither. `ConnectError`
now carries a `Reason`, classified at the throw site while the `NWError` is
still in hand.
`LANRemoteFailureText` is the single mapper. It is deliberately NOT behind
`#if os(iOS)`: the screens are, and CI runs no iOS tests, so a mapper living
beside them could only ever get source guards. Here it has real behavioural
tests that run on macOS. The `SessionControlError` switch is exhaustive with
no `default`, so a new case must be given words rather than inheriting a
generic sentence. Detail strings are kept for logs and never shown; a test
asserts no message contains an error description.
A GATE THAT WAS BLIND, FOUND BY BREAKING IT. Writing the English strings I
left `"Can"t reach this Mac."` in `en.lproj`. `check_apple_strings_parity.py`
printed OK and reported the full count, because it reads keys with a line
regex that happily reads past the break. CFBundle does not: it dropped the
WHOLE catalogue, so every key in the app — long-shipped ones included —
rendered as its raw dotted identifier. Only `L10nFallbackTests` noticed. The
gate now runs `plutil -lint`, the same parser the runtime uses. en is the
worst case to break: it is the fallback for every other locale and has no
fallback itself.
Negative controls, each seen red then green:
· collapse keyRejected/unreachable in the ConnectError overload
→ test_wrongKeyAndUnreachableAreNotTheSameSentence fails
· put `"\(error)"` back in a screen → the source guard fails
· drop one en.lproj entry → the key-echo guard fails
· reintroduce `Can"t` → the new plutil step fails, and with that step
disabled in place the old gate still prints OK
Full CLIPulseCore suite: 2995 tests, 4 skipped, 0 failures. CLIPulseCore
also builds for iOS Simulator, since `swift test` compiles macOS only and
every screen edit here is `#if os(iOS)`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`index.ts`'s flow comment read `--sign ES256 {sub: owner, role/aud:
authenticated, exp}`. That is the PRE-v0.65 token, and `role: authenticated`
is exactly the account-wide PostgREST authority migrate_v0.65 was written to
remove — its own header calls a leaked one "indistinguishable from the
owner's own GoTrue session JWT". Since 2026-07-04 `token.ts` signs
`role: "r0_broadcast"` plus an `r0_session_id` claim binding the token to one
session; only this comment did not catch up.
Found because it misled a reader: an M2 relay design took "the existing
minter produces role/aud = authenticated" from this comment as a measured
fact about the current system, and built a token scheme on it that four
independent reviewers then flagged as re-opening v0.65's F1.
Comment-only; no behaviour change.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`migrate_v0.80` shipped four §8 latches on 2026-09-05 to answer one question:
"Is anyone using LAN or tailnet? … If nobody leaves the LAN, the relay should
never be built at all." Two days later production reads 12 installs and
0/0/0/0. Some of that is the dark ship being off. Not all of it.
`LANLinkAgent.accept` classified the inbound peer with
`LANDirectAddress.classify`, which is the ADVERTISE-side allowlist. Its
question is "may the Mac offer this address of its own?", and there mapping
every non-Tailscale IPv6 to nil is right — offering one invites exposing the
listener. Measuring how a phone ARRIVED is a different question, and "not
offered" is not "not a LAN". Measured:
192.168.1.117 -> .lan 192.168/10/172.16-31 ok
100.101.102.103 -> .tailnet fd7a:115c:a1e0:: ok
fe80::1c2d:3e4f:5a6b:7c8d -> nil <-- ordinary Bonjour on Wi-Fi
fd12:3456:789a::1 -> nil <-- a private v6 LAN
2001:db8:85a3::8a2e:370:7334-> nil
127.0.0.1 / ::1 -> nil <-- the iOS Simulator
`remoteTransportUsed` is called only inside `if let kind = …`, so an iPhone
arriving over IPv6 fired nothing at all and `remote_lan_used_at` stayed null
through real LAN use. The owner's own Mac shows it: the feature override is
on and `cli_pulse_anonymous_install_reported` is 1, yet
`cli_pulse_anonymous_remote_lan_reported` was never written across two days
of driving real sessions.
`classifyPeer` is the measurement-side function: fe80::/10 and fc00::/7
count as LAN, Tailscale keeps priority (its prefix is INSIDE fc00::/7 — put
the ULA rule first and every tailnet arrival relabels as LAN, which answers
plan question 1 backwards; the negative control proves it). Loopback and
global v6 stay unclassified on purpose, and the doc comment says why: the
Simulator is not a phone on a network, and a global v6 peer is
indistinguishable from one on the internet. Guessing would write a made-up
number into the one place the plan reads for a decision.
The pre-existing guard did its job and caught the rename — it asserted the
class is DERIVED, not taken from the wire. That stayed true the whole time;
it just could not see that the derivation answered the wrong question. It
now names `classifyPeer` and refuses the old call.
Negative controls, each seen red then green: point the latch back at
`classify` · drop the fe80::/10 branch · move the ULA rule ahead of the
Tailscale check.
Full CLIPulseCore suite: 3003 tests, 4 skipped, 0 failures. Also builds for
iOS Simulator.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
My own fault, and the interesting part is the wrong fix. `plutil` is macOS
only; Repo Hygiene and CI Gate run on ubuntu, where the new step died with
`FileNotFoundError: 'plutil'`. Wrapping it in a try/except would have made
the gate SKIP on Linux — blind in the one place it actually runs, which is
precisely the failure it was added to prevent.
So the syntax check is now a hand-written strict scanner over the old-style
plist grammar: pure stdlib, same result everywhere. It allows what the
shipped catalogues actually contain — `\"` escapes (providers.show_all_hint,
remote.scan_hint) and literal newlines inside a value
(advanced.remote_consent_body) — and rejects what CFBundle rejects, naming
the line.
Two readers, one file, and they must agree. `read_keys` is a line regex that
COUNTS; `_scan_strings` is a grammar that VALIDATES. On the six shipped
catalogues they agree exactly (1069/814/1014/814/1069/1017). A disagreement
means one of them is reading past something — which is the exact state
2026-09-06 shipped in, where the regex reported a full 1069 keys for a file
the runtime could not load at all.
Four durable cases added to the gate's own suite, where the ad-hoc ones I ran
by hand would not have survived:
5a unescaped quote -> rejected, "does not parse"
5b escaped quote + multi-line -> ACCEPTED (the paired positive control, so
neither direction can be "fixed" by
breaking the other)
5c empty catalogue -> rejected, "declares no entries"
5d two entries on one line -> rejected, "disagree" (line-anchored regex
sees one, the scanner sees two)
Meta negative control: replace `_scan_strings` with the regex count in place
and 5a and 5d go red. Suite: 15 passed, 0 failed, up from 11.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Written, verified against production read-only, and deliberately left
unapplied. Two problems, both measured on gkjwsxotmwrgqsvfijzs 2026-09-07.
PROBLEM 1 — the ledger asserts a security fix that is not there.
`schema_migrations` carries `20260704045711 v0_65_r0_token_least_privilege`,
but the live DB has no `r0_broadcast` role, no `r0_broadcast_topic_allowed`,
no grants, and both policies still `{authenticated}` with v0.56's inlined
EXISTS. The deployed `mint-realtime-token` (v4) is likewise still the
pre-v0.65 build signing `role: "authenticated"` — the account-wide authority
v0.65 exists to remove. So `main`'s security posture is not production's, and
the ledger row says otherwise. Believe objects, never the ledger.
PROBLEM 2 — v0.65 and v0.77 are incompatible, and it went unnoticed.
v0.65 deliberately left the READ policy as v0.56 wrote it: `to authenticated`
with an inlined `select ... from public.remote_sessions`. Correct then. v0.77
(08-30) later revoked `authenticated`'s grants on the remote_* tables, and a
policy expression is evaluated as the CALLING role. Measured as a returned
value, not a NOTICE:
as postgres -> RAN OK
as authenticated -> FAILS: 42501 permission denied for table remote_sessions
Both directions of the R0 private terminal are therefore dead on production.
Nobody has hit it because `realtime_private_enabled` is false for all 216
accounts — which is also why this stays SAFE TO APPLY and inert.
The fix re-applies v0.65 verbatim (every statement there is idempotent) and
adds the half v0.65 did not need: a definer oracle for the READ policy too.
The read oracle must NOT require the `r0_session_id` claim — subscribers
arrive with their own GoTrue token, which carries no such claim, so demanding
it would deny every legitimate reader. That asymmetry is the one thing not
copyable from v0.65.
The in-transaction assertions are not vacuous, and that is checked rather
than claimed. Each was run against today's production state, and all four
return true — meaning each WOULD abort as things stand:
role missing -> would abort
either oracle missing -> would abort
write policy on wrong role -> would abort
a policy still inlines the table -> would abort
⚠️ OWNER, BEFORE THE CUTOVER: re-deploy `mint-realtime-token` first or
together. Applying this alone with the flag flipped would leave a token
claiming `role: authenticated` against a WRITE policy admitting only
`r0_broadcast` — broadcast fails closed.
Every backend gate passes: search_path, rpc_contract, user_id_cascade,
alert_types, date_windows, migration numbering (81 unique). v0.81 was
confirmed free on every remote branch, not just on main.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sent my own six commits through an adversarial review before merging, the way
this repo has learnt to. It found a real defect in the fix itself.
TWO SITES SURVIVED. `main` had TEN raw interpolations, not eight. I converted
the eight bare `"\(error)"` and missed the two that interpolate AFTER a
localised prefix:
self.error = "\(L10n.remote.startFailed): \(error)" // :719
setStatus("\(L10n.remote.disconnected): \(error)") // :968
:968 is the terminal event stream's fallthrough catch — the busiest failure
path in the feature. `LANLinkChannel` wraps every NWConnection failure as
`receiveFailed("\(e)")` and the type has no `CustomStringConvertible`, so a
Japanese user losing Wi-Fi mid-session read
`切断されました: receiveFailed("POSIXErrorCode(rawValue: 54): Connection reset
by peer")`. Exactly the defect this branch is named for.
AND THE GUARD SAID IT WAS FINE. `test_noScreenPaintsAnErrorDescription`
matched the literal `"\(error)"` WITH its delimiting quotes — the shape of a
bare interpolation. Both survivors have a space before `\(error)`, so the
substring never appeared and the assertion passed while the defect shipped.
I wrote a guard for the change I had made instead of for the invariant, and
it then certified an invariant it did not hold. It now matches the
interpolation itself; verified precise, not over-broad (zero hits on the
fixed file, and `message(for: error)` contains no interpolation).
Three more, all from the same review:
· `LANLinkChannelError` had no branch in the mapper, so a dropped link fell
to "Something went wrong". A dropped link is a REACHABILITY failure —
"check the Mac is awake" is the useful sentence. `.framing` deliberately
gets different words: the bytes arrived and did not parse, so pointing the
user at the Mac's power state would aim them at the wrong thing.
· `.dns` fell to `.other`. `LANDirectAddress.parse` accepts hostnames on
purpose (MagicDNS, `.local`), so a typo on the connect-by-address field
said "Try again" — the one thing that cannot help. Now `.unreachable`,
along with `.EADDRNOTAVAIL` / `.ENOTCONN`.
· The compiler corrected the review, which claimed `NWError` has three cases:
this SDK also has `.wifiAware`. Handled explicitly rather than swept into
`@unknown default` — that exists to warn when a genuinely NEW case appears,
and a known case hiding there is a warning nobody reads.
Also corrected an overclaim I wrote yesterday: `classifyPeer`'s doc said the
loopback rule keeps the owner's Simulator out of the fleet evidence. It does
not. A Simulator shares the host's network stack, so Bonjour returns the
host's real addresses and the arrival is typically the en0 link-local, which
scores `.lan` exactly like a real iPhone. No address-based rule can separate
them; the comment now says so instead of promising otherwise.
Negative control, seen red: restore the prefixed interpolation at :968 and
the widened guard fails — the same edit left the old guard green.
Full CLIPulseCore suite: 3005 tests, 4 skipped, 0 failures. Builds for iOS
Simulator. SwiftLint clean, and the one warning I introduced is gone.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…eview
CI caught a risk I had identified and then talked myself out of. Naming
`case .wifiAware` in the `NWError` switch compiles on this Mac (macOS 26.5)
and fails on the runner:
error: type 'NWError' has no member 'wifiAware'
`swift build`, the full suite and the iOS archive were all green locally, so
nothing on this machine could have caught it. Newer SDKs add cases to
`NWError`, and naming one the runner lacks is a build ERROR there, not a
warning. Back to a plain `default:` — NOT `@unknown default` — with the
reasoning recorded so the next person does not "improve" it back.
Nine more, from the same self-review, none of which CI would have caught:
CODE
· IPv4 link-local (169.254/16) still scored nil, reproducing in v4 exactly
the under-count this branch fixes for IPv6. Two devices on Wi-Fi with no
DHCP self-assign from that range and reach each other over Bonjour fine.
· A QR code that is not a pairing payload said only "Something went wrong.
Try again." — the one failure entirely in the user's hands, and re-scanning
the same wrong thing is what that sentence invites. Now names the cause and
says where the real code is, in six languages.
COPY THAT OUTLIVED ITS CODE
· `Kind.lan` still documented itself as "RFC 1918". `classifyPeer` now
returns it for IPv6 link-local and ULA too, and the narrow reading is
precisely what let the latch miss every IPv6 arrival.
· `unparseable`'s docstring claimed it lists files "CFBundle itself would
refuse". It is STRICTER than CFBundle — which also accepts unquoted tokens,
a brace-wrapped dictionary and a stray `;`. Deliberate, but the docstring
has to say so rather than overclaim.
· `expect()` blamed every rejection on an unescaped quote. An unquoted key
now gets its own message instead of a confident wrong diagnosis.
THE MIGRATION, which someone will apply by hand
· "Applying this changes ZERO current behaviour" covered sections (1)-(3) and
silently not (4), which changes EXECUTE grants on three LIVE RPCs. It is
still inert, but by a different argument — v0.36 and v0.37 already revoked
anon on two, and the third raises 'Not authenticated' for a null uid. Both
arguments are now written down, separately.
· The FIX summary numbered its sections 1..3 / 4 while the body numbers them
1,2 / 3 / 4. In a file applied by hand that is a defect of its own.
· PROBLEM 1 diagnosed the ledger drift and never said that the CAUSE is
unknown. It now says so, and says what follows regardless: run the
post-apply queries by hand — a green apply is not evidence.
SCOPE, stated rather than implied
· The Mac's `LANLinkAgent` still puts raw `NWError` text on the Settings
card. That is NOT the same oversight: its header says `state` deliberately
records "what NWListener actually reported, so an entitlement or sandbox
surprise shows up ... rather than as a phone that silently never connects".
Converting it trades a diagnostic the repo asked for against copy on a
surface only the Mac's owner sees. Worth its own change — localised copy
AND the raw detail — but not by quietly widening the iOS guard, so the
guard now documents what it does not cover.
· The four new gate cases were numbered 5a-5d and collided with the existing
5b/5c further down. Renamed to a "syntax A-D" group that cannot collide.
Full CLIPulseCore suite: 3005 tests, 4 skipped, 0 failures. Gate suite 15/15.
Strings parity green in all six locales (now 1070/815/1015/815/1070/1018).
SwiftLint clean. Every backend gate passes; migration numbering still unique.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Costed a CI failure today and would have cost one grep to avoid, so it goes
where the next session looks rather than only in a commit body.
swift-ci.yml runs-on: macos-15 (lint-ci.yml: macos-14)
this machine macOS 26.6, Xcode 26.6
`case .wifiAware` on `NWError` — a case the macOS 26 SDK added, and one the
compiler explicitly suggested adding — compiled here, passed 3005 tests and
an iOS Simulator archive, and was a build ERROR on the runner.
Two things follow, and the second is the one I had been getting wrong: for a
framework's non-frozen enum prefer a plain `default:`, and a local
`xcodebuild -destination 'generic/platform=iOS Simulator'` is weaker evidence
than it looks — it uses this machine's SDK, not CI's.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
JasonYeYuhe
added a commit
that referenced
this pull request
Sep 7, 2026
The hardware run could not exercise #545's IPv6 change: on this Wi-Fi a client resolving Bonjour arrived on IPv4 both times, and `LANDirectAddress.parse` rejects `%`, so a link-local address cannot be typed into connect-by-address either. The one path left is to make the connection myself. These drive a real `NWListener` over the real TLS-PSK parameters and read what `LANLinkAgent`'s own function sees, rather than asserting on a string literal the way `LANPeerClassificationTests` does: · a real connection to this Mac's own `fe80::…%en0` is accepted, `remoteHost` observes an `fe80:` literal, `classifyPeer` returns `.lan`, and the advertise-side `classify` still returns nil for the same string. That is #545's claim, end to end, on a real socket. · loopback stays unclassified — pinning that the wiring is live AND that its answer there is the intended nil, so the first test cannot pass by everything being `.lan`. IT ALSO CLOSES A GAP THE REVIEW FLAGGED. `remoteHost` had no test at all, and it reads `conn.currentPath?.remoteEndpoint` — not `conn.endpoint` — with `currentPath` not documented to be populated when `.ready` fires. If it were nil there the latch could never fire, and that failure is indistinguishable from "classified to nil" and from "accept never ran": three causes, one non-observation. Measured: it IS populated. Now pinned.⚠️ Two of my own mistakes worth leaving in the record, because both presented as product failures: 1. `remoteHost` is MainActor-isolated (its owner is a `@MainActor` class), so calling it from the listener queue traps with `fatalError`. Production hops via `Task { @mainactor }` inside `accept`; the test must too. 2. Having hopped, waiting on a semaphore from the test body DEADLOCKS — XCTest runs on the main actor, which is exactly the thread the hop needs. It presents as "the connection never reached .ready", which reads like a transport bug. The helper is `async` and polls instead; the header says so. Full CLIPulseCore suite: 3007 tests, 4 skipped, 0 failures. SwiftLint clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
JasonYeYuhe
added a commit
that referenced
this pull request
Sep 7, 2026
* correct #545's causal claim: the hardware run refuted it I ran the hardware test #545's own comments promised, and it did not confirm them. Correcting the record where the wrong claim lives, not only here. WHAT #545 CLAIMED. That an iPhone "routinely arrives on an IPv6 link-local address, and that is why `remote_lan_used_at` stayed null through real LAN use." WHAT I MEASURED, 2026-09-07, this Mac, a paired client on the same Wi-Fi, peer address read at the socket: arm A pre-#545 binary -> 192.168.1.104 latch FIRED arm B post-#545 binary -> 192.168.1.104 latch FIRED Both arrivals were IPv4, never `fe80::`. `classify` and `classifyPeer` are byte-identical for RFC 1918, so that arrival cannot distinguish the two at all — inconclusive for the IPv6 change, exactly as the pre-registered protocol said an IPv4 arrival would be. AND THE HISTORICAL NULL HAD A SIMPLER CAUSE. The Mac app's card read **0 paired** until I paired a phone with it during this run. No phone had ever completed a handshake with the shipping app. The earlier sessions ran against an XCTest-hosted agent, where `AnonymousTelemetryCoordinator.shared` is nil and nothing is reported regardless of address. So the null was never evidence of a classification bug. WHAT THE RUN DID ESTABLISH, and it is worth having: · the chain works end to end — connection → classifyPeer → remoteTransportUsed → RPC → `anonymous_installs.remote_lan_used_at`, observed non-null on production, `last_seen_at` moved from Sep 3 to now; · #545 does not regress the IPv4 path; · the emitter is live in both bundles. THE FIX STAYS. fe80::/10, fc00::/7 and 169.254/16 ARE the same network when a phone arrives on one, and `classify` returning nil for them would under-count. That is an argument from address semantics, proven by `LANPeerClassificationTests` — not from an observed fleet miss, and it must not be cited as one. Both comments now say so. Production left as found: the probe's `remote_lan_used_at` was written by a Simulator on this same Mac, not by a phone on a network, so it was nulled back. The row is kept — it carries genuine install history. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * observe a REAL fe80:: peer, since the network would not produce one The hardware run could not exercise #545's IPv6 change: on this Wi-Fi a client resolving Bonjour arrived on IPv4 both times, and `LANDirectAddress.parse` rejects `%`, so a link-local address cannot be typed into connect-by-address either. The one path left is to make the connection myself. These drive a real `NWListener` over the real TLS-PSK parameters and read what `LANLinkAgent`'s own function sees, rather than asserting on a string literal the way `LANPeerClassificationTests` does: · a real connection to this Mac's own `fe80::…%en0` is accepted, `remoteHost` observes an `fe80:` literal, `classifyPeer` returns `.lan`, and the advertise-side `classify` still returns nil for the same string. That is #545's claim, end to end, on a real socket. · loopback stays unclassified — pinning that the wiring is live AND that its answer there is the intended nil, so the first test cannot pass by everything being `.lan`. IT ALSO CLOSES A GAP THE REVIEW FLAGGED. `remoteHost` had no test at all, and it reads `conn.currentPath?.remoteEndpoint` — not `conn.endpoint` — with `currentPath` not documented to be populated when `.ready` fires. If it were nil there the latch could never fire, and that failure is indistinguishable from "classified to nil" and from "accept never ran": three causes, one non-observation. Measured: it IS populated. Now pinned.⚠️ Two of my own mistakes worth leaving in the record, because both presented as product failures: 1. `remoteHost` is MainActor-isolated (its owner is a `@MainActor` class), so calling it from the listener queue traps with `fatalError`. Production hops via `Task { @mainactor }` inside `accept`; the test must too. 2. Having hopped, waiting on a semaphore from the test body DEADLOCKS — XCTest runs on the main actor, which is exactly the thread the hop needs. It presents as "the connection never reached .ready", which reads like a transport bug. The helper is `async` and polls instead; the header says so. Full CLIPulseCore suite: 3007 tests, 4 skipped, 0 failures. SwiftLint clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Eight commits. Every finding below was verified on the artifact, and every new
assertion was seen red before green. Three of them were found by things other
than me — an adversarial self-review and CI — which is recorded honestly at
the bottom.
1. The phone showed the user an errno, and two opposite causes said the same thing
mainhad ten sites rendering a raw Swift error into localised UI. In asix-language app the worst read
POSIXErrorCode(rawValue: 61): Connection refused.Worse, the two interesting failures both arrive as
.waitingand wereflattened into one string. Measured here on macOS 26.5, loopback
NWListener, pinned suite:NWErrortls(-9820)bad MACposix(61)refusedConnectErrornow carries aReasonclassified at the throw site.LANRemoteFailureTextis the single mapper, deliberately not behind#if os(iOS)— the screens are, and CI runs no iOS tests, so a mapper besidethem could only ever get source guards. Here it has behavioural tests that run.
Its
SessionControlErrorswitch is exhaustive with nodefault, so a newwire error is a compile error rather than a silent "Something went wrong".
Also mapped, from the review: a dropped link (
LANLinkChannelError) says"check the Mac is awake" rather than the generic fallback,
.framingdeliberately does not, a name that does not resolve is a reachability failure
(MagicDNS /
.localtypos), and a QR code that is not a pairing payload namesits own cause instead of inviting a re-scan of the same wrong thing.
2. The strings gate accepted a file the runtime cannot load
Writing the English copy I left
"Can"t reach this Mac."inen.lproj.check_apple_strings_parity.pyprinted OK with a full key count, becauseit reads keys with a line regex that reads past the break. CFBundle does not —
it dropped the whole catalogue, so every key in the app, long-shipped ones
included, rendered as its raw dotted identifier. Only
L10nFallbackTestsnoticed.
The gate now runs a syntax scanner as well, and the two readers must agree:
the regex COUNTS, the grammar VALIDATES, and a disagreement means one of them
is reading past something. Four cases added to the gate's own suite (11 → 15),
including a paired positive control —
\"escapes and literal newlinesinside a value are legal and shipped — so neither direction can be "fixed" by
breaking the other.
3. The LAN usage latch never fired, because it measured with an allowlist
LANLinkAgent.acceptclassified the inbound peer withLANDirectAddress.classify— the advertise-side allowlist, whose questionis "may the Mac offer this address of its own?". Excluding every non-Tailscale
IPv6 is right for that question. Measuring how a phone arrived is a
different one, and "not offered" is not "not a LAN":
So an iPhone arriving over IPv6 fired nothing and
remote_lan_used_atstayednull through real LAN use — on the owner's own Mac, with the feature override
on and telemetry reporting. This is the instrument
migrate_v0.80installedto decide whether the self-built transport lives.
classifyPeeris the measurement-side function. Tailscale keeps priority: itsprefix is inside
fc00::/7, and the negative control shows that puttingthe ULA rule first relabels every tailnet arrival as LAN — answering plan
question 1 backwards.
4.
migrate_v0.81— written, NOT applied, owner gateMeasured on production: the ledger carries
v0_65_r0_token_least_privilegebut none of its objects exist, the deployed
mint-realtime-tokenis stillthe pre-v0.65 build signing
role: "authenticated", and bothrealtime.messagespolicies fail42501because v0.77 revoked the grantstheir inlined bodies need. All four of the migration's in-transaction
assertions were run against today's production state: all four would abort,
so none is vacuous.
mint-realtime-tokenfirst or together.What found what
The guard matched the literal
"\(error)"with its delimiting quotes, sothe two sites that interpolate after a localised prefix never matched — it
certified an invariant it did not hold. And
case .wifiAwarecompiled here(macOS 26.6) and was a build error on the runner (
macos-15), a risk I hadnamed and then argued away. Both are written up in
AGENTS.mdand the commitbodies rather than only fixed.
Verification
gate's own suite 15/15
on every remote ref
🤖 Generated with Claude Code