Skip to content

fdev host hygiene: browser-run and /tmp holds, disk-backed TMPDIR, dead-run temp cleanup (Refs #5240) - #5242

Merged
EHotwagner merged 6 commits into
mainfrom
routine/5240-fdev-host-hygiene
Nov 2, 2026
Merged

EHotwagner merged 6 commits into
mainfrom
routine/5240-fdev-host-hygiene

Conversation

@EHotwagner

@EHotwagner EHotwagner commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

Refs #5240, Refs #4730, Refs #5172

fdev host hygiene, from process problem 4 of the 2026-11-02 progress report: the quiet-host bench probe never admitted for 60 minutes, /tmp (a 16 GB tmpfs) filled and failed 38 SDD tests, and many browser smokes ran at once. Source only: nothing is installed on the host by this PR; Root installs after merge with the steps in docs/coordination/fdev-host-hygiene.md.

What changed

  1. Capacity probe (scripts/fdev-lane-capacity.py): two new holds. At least --browser-hold-at (default 2, 0 disables) active headless Chromium main processes (Chromium-family argv[0], --headless, --remote-debugging-pipe|port, no --type=; renderer, GPU, zygote, utility, crashpad children and node/timeout launchers do not count). Free space at --tmp-path (default /tmp) below --min-tmp-gib (default 3). JSON: browsers.count, browsers.pids, tmp_free_gib, tmp_path, thresholds.browser_hold_at, thresholds.tmp_free_gib_min; /tmp is reported apart from disk_free_gb. Unreadable inputs hold. Chromium rewrites its process title, so /proc/<pid>/cmdline is one space-joined string; the probe splits it (fixtures captured from a real Playwright chrome-headless-shell; verified live: one browser reads count 1, two hold).
  2. Disk-backed TMPDIR: fdev-chromium-env had no repository source (it was only ~/.local/bin, from Supported headless Chromium on fdev: vetted runtime-lib/font bundle + one wrapper (no borrowed lane dirs) #5193). Its source of truth is now deployment/fdev-chromium/fdev-chromium-env, installed by install.py chromium-env. It defaults TMPDIR to $HOME/.cache/fsgg-tmp (mkdir then chmod 0700) unless already set; the default is refused above 32 bytes because a Unix socket path is at most 107 bytes and harness trees nest under it. --print-tmpdir gives .NET runs the directory without the Chromium library path.
  3. Dead-run cleanup (scripts/fdev-tmp-cleanup.py): dry run unless --apply. Known names only (playwright-artifacts and fsgg-gate-* are not swept); owner dead (PID in the name absent, and no live process references the path through cmdline, environ, cwd, fd, maps or /proc/net/unix, with relative words resolved against the process cwd and symlink aliases by realpath); idle over 1 h for PID-named runs and 6 h for PID-less patterns by the newest mtime in the tree; never follows a symlink (iterative directory-fd O_NOFOLLOW walk, no recursion, no mount crossing); refuses to delete if the process table is unreadable or a same-user process cannot be inspected (allowlist: systemd --user, (sd-pam), sshd); refuses euid 0; re-reads the table before the first deletion. Hourly user timer templates (minute 37) in deployment/fdev-board-tend/, installed by their own install.py tmp-cleanup effect, so the existing board-tend units effect and its rollback are unchanged.
  4. Operating note docs/coordination/fdev-host-hygiene.md, pointers from the two existing fdev notes, one Atlas record (environment/fdev-host-hygiene), and workflow fdev-host-hygiene-selftest.

Review response (REQUEST_CHANGES at 41673b0): M1 cmdline parsing, M2 SC2174, M3 playwright-artifacts dropped, and Root's rulings 1 to 9 (6 h PID-less idle, uninspectable-process refusal, iterative removal, fsgg-gate-* dropped, relative and alias references, rollback with a wrapper backup step, socket budget from the real SDD race test, MSBuild node-reuse note, euid 0 refused). Decisions kept for Root: the cleanup timer is a separate unit pair beside the board-tend units; PID-less patterns rely on the live-reference test plus the 6 h idle rule. The Atlas record was re-captured (the first one was never in a baseline).

Tests (local, exact output)

  • bash tests/fdev-lane-capacity/run.sh: Ran 37 tests ... OK (13 new, incl. real-shaped one-field cmdline fixtures for the main process and --type= children).
  • bash tests/fdev-tmp-cleanup/run.sh: Ran 30 tests ... OK (incl. 6 h vs 1 h idle, relative and symlink-alias references, uninspectable same-user process blocks --apply, allowlist, 1,100-level tree, euid 0).
  • bash tests/fdev-chromium-env/run.sh: Ran 13 tests ... OK (modelled SDD race socket path: default 104 bytes, a 39-byte lane TMPDIR exceeds 107).
  • bash tests/fdev-board-tend/run.sh: Ran 43 tests ... OK.
  • scripts/lint-shell.sh with the pinned shellcheck 0.11.0: 224 file(s) and 721 workflow-embedded step(s) clean. tests/atlas/check.py and prepare-registry --check: current. check-v1-writer-census --structural: PASS. check-prose-citations: ok.

Live verification of the browser count (real Playwright, no fixtures)

With one real Playwright chrome-headless-shell 1243 launched through fdev-chromium-env: browsers {"count":1}, verdict unaffected. With two: browsers {"count":2}, verdict hold, reason browser runs 2 >= 2 (headless Chromium main processes).

Dry run on the real host (default dry run; --apply was never run on the real /tmp)

python3 -I scripts/fdev-tmp-cleanup.py --list-limit 3, exit 0: processes_read 376, unreadable [], entries_matched 1077, would_remove 874, would_free_bytes 3569328128, removed 0, kept {young: 203}; by pattern (removable/matched) browser-smoke 14/14, chromium-temp 444/492, fsgg-sdd-run 124/237, fsgg-test-root 219/261, playwright-profile 73/73.

Fixtures: clock, source and sandbox

Cleanup tests use the real wall clock (--now is passed explicitly and mtimes are set relative to it), a temp-dir root and a fake procfs; no real /tmp, no systemd except systemd-analyze verify, no network, no credentials. Probe tests use --proc-root/--cgroup-root fixtures and point --tmp-path at the fixture directory. The wrapper tests use a fake bundle and fake $HOME.

🤖 Generated with Claude Code

rev and others added 6 commits November 2, 2026 07:25
…ad-run temp cleanup (Refs #5240)

- scripts/fdev-lane-capacity.py: hold at >=2 headless Chromium main processes
  (children count once) and when /tmp has under 3 GiB free; both configurable,
  reported as browsers.* and tmp_free_gib.
- deployment/fdev-chromium/fdev-chromium-env: the wrapper now has a repository
  source; it defaults TMPDIR to a short disk path unless already set.
- scripts/fdev-tmp-cleanup.py: dry-run-by-default removal of known harness
  temp directories whose owner is dead and idle over an hour; never follows a
  symlink. Hourly user timer templates and install.py tmp-cleanup and
  chromium-env effects; nothing is installed by this change.
- docs/coordination/fdev-host-hygiene.md and fixture tests for all of it.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…unt and TMPDIR work (Refs #5240)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…fest (Refs #5240)

prepare-registry --write: seed catalogs and records for .agents and .claude,
registry/driver-skill-manifest.json. No authored registry row changes.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…nup safety (Refs #5240)

Review response (REQUEST_CHANGES at 41673b0):
- probe: Chromium rewrites its process title, so /proc/<pid>/cmdline is one
  space-joined string; split it and take argv[0] from exe. Fixtures captured
  from a real Playwright chrome-headless-shell; verified live: 1 browser ->
  count 1, 2 browsers -> hold.
- fdev-chromium-env: mkdir -p then chmod 0700 (shellcheck SC2174); MSBuild
  node-reuse caveat.
- cleanup: drop playwright-artifacts and fsgg-gate-*; PID-less patterns need
  6 h idle; refuse to delete while a same-user process cannot be inspected
  (allowlist: systemd --user, (sd-pam), sshd); relative and symlink-alias
  references; iterative removal (1,100-level test); refuse euid 0.
- docs: rollback with a wrapper backup step, socket budget from the real SDD
  race test, corrected non-dumpable-process wording.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…(Refs #5240)

The earlier record was never in a baseline; replaced because its pattern list,
idle periods and verification no longer matched the code. Seeds and driver
manifest regenerated by prepare-registry --write.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
# Conflicts:
#	.agents/skills/atlas/assets/seed/catalog.json
#	.claude/skills/atlas/assets/seed/catalog.json
#	docs/atlas/catalog.json
#	registry/driver-skill-manifest.json
@EHotwagner

Copy link
Copy Markdown
Contributor Author

Root/fdev: the delta review APPROVED 114423ef.

  • M1: the browser hold is confirmed live; one browser counts 1, two hold.
  • M2 and M3: fixed.
  • Root's rulings hold under test:
    • the unreadable-process block can't be bypassed and doesn't trip on this host;
    • iterative removal is safe under symlinks and mounts;
    • the reviewer's own E1–E9 live-holder experiments are all kept, except the ruled 6 h in-memory case;
    • realpath matching costs about 1 s.
  • Counts: reproduced exactly. All required checks are green.

Non-blocking, carried into the ops note after install:

  • a non-dumpable process silently stalls cleanup, so check systemctl --user --failed;
  • the depth wording;
  • the allowlist matches by comm (parent-chain hardening is optional).

closingIssuesReferences is []. Merging; Root installs per docs/coordination/fdev-host-hygiene.md. Refs #5240

@EHotwagner
EHotwagner marked this pull request as ready for review November 2, 2026 07:20
@EHotwagner
EHotwagner merged commit acfe0b8 into main Nov 2, 2026
95 of 96 checks passed
@EHotwagner
EHotwagner deleted the routine/5240-fdev-host-hygiene branch November 2, 2026 07:21
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