Skip to content

fix: match a sandbox name as a whole argv token, not a substring - #53

Merged
ndr-ds merged 2 commits into
mainfrom
ndr-ds/match-sandbox-name-as-a-whole-token
Aug 10, 2026
Merged

ndr-ds merged 2 commits into
mainfrom
ndr-ds/match-sandbox-name-as-a-whole-token

Conversation

@ndr-ds

@ndr-ds ndr-ds commented Aug 10, 2026 •

Copy link
Copy Markdown

Motivation

Two questions were sharing one filter, and neither was answered correctly.

extract_open_ttys selected wrapper lines with line.contains(sandbox). Every identity-suffixed sandbox name has the legacy bare name as a prefix, and sandbox_common.sh::_is_reapable_name still hands out that bare name for a pre-identity sandbox:

sbx exec --env SANDBOX_HOST_TTY=/dev/ttys001 linera-agent bash
sbx exec --env SANDBOX_HOST_TTY=/dev/ttys055 linera-agent-e3f43ae4b7d1 bash
sbx exec --env SANDBOX_HOST_TTY=/dev/ttys077 linera-agent-09be684a5547 bash

Asking for linera-agent returned all three terminals. A session's own tty is always inside a superset, so !open.contains(tty) could never be true — terminal_is_gone failed open forever for that one name, silently, and it is the name that keeps the oldest sandbox on the dashboard. #50 made that path load-bearing, which is what makes this worth fixing now rather than noting.

But tightening that match alone would have silently disabled orphan reaping, because the other caller depends on the looseness:

  • scan_host_open_ttys asks with sandbox_name(), which falls back to the literal "linera-agent" whenever sbx ls does not show exactly one running sandbox — i.e. always, on a host running several.
  • The substring match made that fabricated name sweep up every linera-agent-<hash> terminal, so it worked by accident. Matched precisely, it returns nothing.
  • Its only consumer is the orphan killer. With an empty set, decide_action's Guard 1 (open_ttys.is_empty() && alive_count > 0) skips every pass reporting "probable host scan failure" — a feature that is off while blaming ps. Guard 1 is the only reason this would not have mass-killed live sessions instead.

The legacy naming scheme is unaffected either way: a genuinely old sandbox is named exactly linera-agent, so its sbx exec lines carry that as a whole token, and its registry slice is keyed by the same string (registry_sandbox_name → resolve_sandbox_name). Render and --restore-sbx-sessions both keep working. The substring behaviour only ever helped when the name asked about was a prefix of a different sandbox's name, which is never the question the render path wants.

Change

Split the two questions:

question caller answer
which terminals belong to sandbox X? render path (open_host_ttys_by_sandbox) per-sandbox, whole-token match
is this tty still open anywhere on the host? orphan reaper (scan_host_open_ttys) no sandbox filter at all (host_open_ttys)
  1. extract_open_ttys matches the name as a whole whitespace-delimited argv token. sbx exec takes the sandbox as its own bare argument, so token equality is exactly right — and strictly narrower than the substring test, so no row that was judged before stops being judged.
  2. scan_host_open_ttys drops the name filter entirely, from the same single ps sweep. This is strictly better than the old substring behaviour, which missed a differently-named sandbox's terminals — SANDBOX_NAME=my-team-sandbox contributed none, so its live sessions looked like orphans and were kill candidates. A host-wide set is also the conservative direction for a kill decision: a tty absent from a broader set is stronger evidence, never weaker.

Fallout: sandbox_name() lost its last caller and is deleted, along with the reaper's now-vestigial DEFAULT_SANDBOX_NAME (sandbox_registry keeps its own for the resolver call that still needs one). No #[allow(dead_code)]. Losing that helper is a bonus — an "always answers, with a convention" name resolver is the exact footgun this bug is made of, and target_sandbox(), which refuses to invent a name, remains.

Tests

  • the_legacy_bare_name_does_not_match_its_identity_suffixed_siblings — with all three names present, the bare name must own exactly /dev/ttys001, and a suffixed name must still resolve to its own terminal.
  • the_orphan_reapers_host_scan_is_not_narrowed_by_any_sandbox_name — a sweep carrying a hashed name, the bare legacy name and my-team-sandbox must yield all three terminals, while extract_open_ttys(ps, "my-team-sandbox") still yields exactly one. This is the case the original commit broke.
  • Existing extract_open_ttys_picks_sandbox_host_tty and extract_open_ttys_honors_alternate_sandbox_name unchanged and green — their other-sandbox / my-team-sandbox cases were already distinct tokens.

Verification

$ cargo fmt --all -- --check                  # clean
$ cargo clippy --all-targets -- -D warnings   # clean
$ cargo test --lib reaper::                   # 80 passed

Review note

The second commit exists because the first was wrong on its own — the loose match was load-bearing for the orphan reaper, caught in review. Neither existing test found it: both callers were only ever exercised with real sandbox names, never with the convention sandbox_name() invents when it cannot detect one.

@ndr-ds
ndr-ds marked this pull request as ready for review August 10, 2026 19:53
@ndr-ds
ndr-ds merged commit a25d871 into main Aug 10, 2026
5 checks passed
@ndr-ds
ndr-ds deleted the ndr-ds/match-sandbox-name-as-a-whole-token branch August 10, 2026 19:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant