fix(devices): resolve the operator platform before choosing a probe's shell family - #3711
taylorg009 wants to merge 4 commits into
Conversation
… shell family probeDeviceStats picked the PowerShell vs POSIX stats snippet (and the probe budget) from the registry's DISCOVERED shell, while buildSshInvocation dialed with the RESOLVED profile (per-device `config: platform` overlay). A Windows-discovered box whose sshd lands in WSL, configured `agents devices config <name> platform linux`, was therefore dialed as a bash login shell but handed the PowerShell snippet: empty stdout, `reachable: false`, and an "offline" row on every `agents devices list --refresh` even though `agents ssh <name>` worked. Every tailnet sync re-stamps the discovered platform, so the manual-add workaround did not survive either. Extract buildProbeInvocation (resolves first, then picks snippet + budget) and cover it against the real config read path (temp HOME + device doc, no mocks). Apply the same resolve-before-shell-check to the two sibling probes that inspected `device.shell` directly: the teams readiness probe and the usage-sync peer exchange. Verified end to end on the fleet: with the discovered-windows / configured- linux profile the installed CLI renders the box offline and the patched tree renders it idle with live load/mem/disk. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… JSON gather and doctor probes Same defect class as the stats probe: the session fan-out (remote-list), the fleet agents-json gather and the doctor fleet probe all handed `remoteShellFor` the registry's DISCOVERED platform while dialing with the resolved profile. A WSL-routed Windows box configured `platform: linux` was sent a PowerShell wrapper into a bash login shell and skipped as "unreachable or no agents CLI" — `agents sessions --active` hid its sessions even though `agents ssh <name>` worked. Verified on the fleet: the installed CLI skips the box; this tree lists it with its two live sessions. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Review verdict: REQUEST CHANGES Non-author review (subagent reviewer — The diagnosis is correctThe claim checks out. The three dial helpers all resolve the operator profile internally:
and Each of the six changed sites is correct as written. The PR's claim that
Blocking: the sweep is incomplete — four more sites pick the shell from the raw record
1. consumed by:
This one is not a judgment call: the sibling fan-outs in
2. while the dial one frame down is resolved: 3. 4. Same shape: Either fix these four, or state in the PR description which are out of scope and why. Blocking: no CHANGELOG entry
Non-blocking5. Gate asymmetry the PR introduces. and 6. Test coverage for five of the six fixed sites. 7. Nit — What is correctMocking rule — PASS. It drives the real config read path — temp HOME, a real per-device doc, and a real That last assertion is what makes it a real-path test rather than ceremony: Budget bug fixed too. Verification
(no diagnostics; 0 lines of output)
Both green. The verdict is not about the gates — it is items 1-4 (incomplete sweep) and the missing CHANGELOG entry. |
…/feed watch, apply, and the peer gates (review round) Review found four more sites of the same class: the doctor --check fan-out (platform for the PowerShell branch while fleetDialTarget resolved), the sessions-watch and feed-watch peer streams (remoteWatchCommand / remoteFeedWatchCommand from the discovered platform while streamFromPeer resolved), and fleet apply's probe + reconcile (osHint / remoteEnv from the discovered platform while sshTargetFor / deviceIdentityArgs resolved). All now resolve once at the top. The automatic-peer gates in remote-list and both watchers filter on the resolved platform too, so the gate and the shell decision agree. usage-sync now imports the resolver statically like every other site. Adds the .changelog/next fragment. The remote-list peer-gate fixtures gain the auth block a DeviceProfile always carries, which the resolver reads. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Review verdict: APPROVE Re-review of Blocking 1 (incomplete sweep) — RESOLVED, all four sites
Now matches
Resolving at the map rather than at each read is the right shape here: the same
Shadowing the parameter as Blocking 2 (CHANGELOG) — RESOLVED, and via the right mechanismI pointed at
Non-blocking gate asymmetry — RESOLVEDmatching Nit — RESOLVEDThe test fixture change is correct, not a mask
— and the write path always populates it: So the fixtures were type-dishonest casts that Re-grep: one remaining raw-shell decision, out of this PR's scopeI re-ran the sweep (
The one exception —
while the dial one line later re-resolves — Not blocking, deliberately: this is a different mechanism (the It does, however, make one sentence in the queued note literally untrue: Narrowing "Every remote-shell decision" to the fleet probe and fan-out paths the note already enumerates would make it accurate. A one-line edit, not a re-review. Verification
(no diagnostics; 0 lines of output)
Both green (up from 7 files / 158 tests last round). The mocking rule still passes: |
…view nit) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Problem
Several fleet paths chose the PowerShell vs POSIX remote shell from the registry's discovered platform/shell, while
buildSshInvocation/sshTargetFordial with the resolved profile (per-deviceconfig: platformoverlay fromresolve-profile.ts).A Windows-discovered box whose sshd lands in WSL, configured with
agents devices config <name> platform linux, was dialed as a bash login shell but handed a PowerShell snippet or wrapper. Empty stdout becamereachable: falseinagents devices list --refresh(offline on every refresh) and "unreachable or no agents CLI — skipped" inagents sessions --active, even thoughagents ssh <name>worked. Because every tailnet sync re-stamps the discovered platform, re-adding the device manually as Linux only lasted until the next sync.Fix
Resolve the operator profile before choosing the shell family, at every site that read the raw registry record:
devices/health.ts: extractbuildProbeInvocation(resolve first, then snippet + budget);probeDeviceStatsconsumes it.teams/placement-probe.ts: readiness probe command.accounting/usage-sync.ts: peer exchange command.session/remote/remote-list.ts: fan-out targets and the single-machine target.remote-agents-json.ts: fleet JSON gather targets.commands/doctor.ts: fleet probe targets.hosts/providers/devices.tsalready resolved and is unchanged.Tests
health.test.ts: two new cases on the real config read path (temp HOME + per-deviceagents.yaml, fresh modules, no mocks). A Windows-discovered profile with no override dials the PowerShell wrapper on the Windows budget; withplatform: linuxin the device doc it dialsPROBE_SNIPPETon the relayed POSIX budget and targets the configured user.vitest runondevices/health,devices/resolve-profile,teams/placement-probe,accounting/usage-sync,session/remote/remote-list,remote-agents-json,commands/doctor: all green.tsc --noEmit: clean.End-to-end
Real fleet, WSL-backed Windows host
jupiter(discoveredwindows, configuredplatform: linux, sshdDefaultShellrouted into WSL):tsx src/index.ts)devices list --refreshjupiter linux offlinejupiter linux 20c 15.5G 1007G 0% 7% 5% idlesessions --activejupiter: unreachable or no agents CLI — skippedjupiter (2)with both live codex sessions🤖 Generated with Claude Code