Skip to content

fix(bin): identify herdr lab viewers by kernel start time so stop works on WSL2 - #11

Merged
dardant merged 2 commits into
mainfrom
fm/fm-herdr-lab-lstart-drift
Sep 25, 2026
Merged

dardant merged 2 commits into
mainfrom
fm/fm-herdr-lab-lstart-drift

Conversation

@dardant

@dardant dardant commented Sep 25, 2026

Copy link
Copy Markdown
Owner

Intent

bin/fm-herdr-lab.sh viewer stop refuses to stop its own lab viewer on WSL2 because the recorded ps -o lstart start time and a later reading differ by one to two seconds for the same process, so lab teardown refuses while attached.

Context: the captain approved starting the queued Firstmate fixes, each as a PR for his merge call. This hit three workers on 2026-09-24/25 (fm-afk-inject-wedge, fm-done-worker-stale-noise twice): each had to verify the pid, parent chain and --session <lab> cmdline by hand and kill its own viewer before the guarded stop and teardown succeeded. A leftover lab session from an earlier task was also seen running, which suggests some teardown path may leak labs.

What Changed

  • bin/fm-herdr-lab.sh replaces fm_herdr_lab_process_start (the ps -o lstart value) with fm_herdr_lab_process_identity. When /proc is readable, the identity is /proc/<pid>/stat field 22 (kernel starttime), prefixed with the kernel boot id if one can be read. Hosts without that /proc still fall back to the locale-pinned lstart. viewer stop compares the recorded identity against the current one, so a live viewer whose lstart drifts by a second or two (as it does on WSL2) is no longer disowned.
  • bin/fm-herdr-lab-viewer.py gets its values from that same shell function and writes launcher_identity/viewer_identity in place of launcher_start/viewer_start in the viewer pidfile, so the recorder and the stop guard can't disagree on the format.
  • tests/fm-herdr-lab.test.sh adds two tests. One checks that viewer stop still stops the viewer when lstart drifts. The other checks that the guard still refuses a reused PID whose kernel start time doesn't match the record.

Risk Assessment

✅ Low: The change now only replaces the viewer record's lstart identity with /proc stat starttime plus boot_id (falling back to exact lstart when /proc is unavailable), which addresses the WSL2 drift the intent describes while keeping PID-reuse protection. The fix round fully removed the lab-leak backstop the user asked to drop, and the new tests exercise the public stop path: under a simulated lstart drift the viewer is still signalled, and with reused PIDs (mismatched starttime, boot id or lstart) nothing is signalled.

Testing

I drove four throwaway fm-lab-* Herdr sessions through bin/fm-herdr-lab.sh on this WSL2 host, each with a real attached pty viewer. On HEAD, the viewer record carries boot=&lt;id&gt; starttime=&lt;ticks&gt; identities that match /proc, and viewer stop plus teardown succeed both normally and with a 1-second lstart drift injected through a ps shim. Under the same drift, the pre-fix base code leaves the viewer alive and refuses teardown, which reproduces the reported bug. A forged starttime in the record still leaves the viewer unsignalled and teardown refused. Natural drift did not occur during a 15-second probe here, so the drift was simulated rather than produced by the host clock. The focused fm-herdr-lab suite and the real-Herdr attached-viewer e2e both pass. Every lab I created was torn down, and the default session and other agents' labs were never touched. This change has no visual UI surface, so the CLI transcript is the evidence.

  • Live validation: ✅ go - 4 of 5 scenarios driven live against the product
Scenario Result Live Evidence
Worker starts a real lab viewer on WSL2, then viewer stop detaches it and teardown succeeds ✅ pass live live-lab-viewer-lstart-drift-transcript.txt Scenario A: the record holds boot=7d67b0a2... starttime=33979757 matching /proc; stop exit 0 with viewer and launcher gone; teardown exit 0
With ps lstart drifted by 1s for the same live process, HEAD viewer stop still recognises and stops its own viewer, and teardown succeeds ✅ pass live Scenario B in live-lab-viewer-lstart-drift-transcript.txt: the drift shim shows lstart 14:21:00 vs real 14:20:59; stop exit 0, viewer pid GONE, teardown exit 0
Regression repro: the pre-fix code under the same drift refuses to stop its own viewer and blocks teardown ✅ pass live Scenario C: base stop exit 1 'did not detach', viewer ALIVE, teardown refused while attached; demonstrates the fix is load-bearing
Adversarial: a record whose identity no longer matches the live process (PID-reuse stand-in) is never signalled ✅ pass live Scenario D: forged starttime+1 left viewer and launcher ALIVE and teardown refused; the restored genuine record then stopped cleanly
Genuine host-clock lstart drift on WSL2 (not simulated) no longer disowns the viewer ⏸️ untested no Natural drift depends on WSL2 time-sync re-rendering the boot time, which could not be triggered without root clock changes to the host (outside the workspace boundary). It was covered by injecting dr…
Evidence: Live lab transcript: HEAD happy path, HEAD under drift, base under drift (bug repro), forged-identity refusal

Source: Live lab transcript: HEAD happy path, HEAD under drift, base under drift (bug repro), forged-identity refusal

===== host: 6.18.33.2-microsoft-standard-WSL2  herdr: herdr 0.9.0
===== sessions before:
{"name":"default","default":true,"running":true}
{"name":"fm-lab-autocompact-3192236-6327","default":false,"running":true}
{"name":"fm-lab-claude-exit-3678035","default":false,"running":true}

######## Scenario A: HEAD happy path - real viewer start, stop, teardown on WSL2

$ ~/.no-mistakes/worktrees/a2097c65a770/01M3CVW0WT6WFCYP6EZVWHXRNR/bin/fm-herdr-lab.sh provision fm-lab-lstartA-3742065-29273
[exit 0]

$ ~/.no-mistakes/worktrees/a2097c65a770/01M3CVW0WT6WFCYP6EZVWHXRNR/bin/fm-herdr-lab.sh viewer start fm-lab-lstartA-3742065-29273
viewer attached to fm-lab-lstartA-3742065-29273 (pid 3742814)
[exit 0]
--- viewer record fm-lab-lstartA-3742065-29273 ---
launcher_pid=3742761
launcher_identity=boot=7d67b0a2-af55-4f4b-a4ea-efe5887dbf4c starttime=33979755
viewer_pid=3742814
viewer_identity=boot=7d67b0a2-af55-4f4b-a4ea-efe5887dbf4c starttime=33979757
pid 3742814: /proc starttime=33979757 boot_id=7d67b0a2-af55-4f4b-a4ea-efe5887dbf4c real lstart=[Fri Sep 25 14:20:57 2026] drifted lstart=[Fri Sep 25 14:20:58 2026] cmdline=herdr --session fm-lab-lstartA-3742065-29273 
pid 3742761: /proc starttime=33979755 boot_id=7d67b0a2-af55-4f4b-a4ea-efe5887dbf4c real lstart=[Fri Sep 25 14:20:57 2026] drifted lstart=[Fri Sep 25 14:20:58 2026] cmdline=python3 ~/.no-mistakes/worktrees/a2097c65a770/01M3CVW0WT6WFCYP6EZVWHXRNR/bin/fm-herdr-lab-viewer.py fm-lab-lstartA-3742065-29273 /tmp/fm-lstart-live.VGJ4/state/fm-lab-lstartA-3742065-29273.viewer 
foreground reason while attached: cleared

$ ~/.no-mistakes/worktrees/a2097c65a770/01M3CVW0WT6WFCYP6EZVWHXRNR/bin/fm-herdr-lab.sh viewer stop fm-lab-lstartA-3742065-29273
[exit 0]
pid 3742814 GONE
pid 3742761 GONE
--- viewer record fm-lab-lstartA-3742065-29273 ---
<no record>

$ ~/.no-mistakes/worktrees/a2097c65a770/01M3CVW0WT6WFCYP6EZVWHXRNR/bin/fm-herdr-lab.sh teardown fm-lab-lstartA-3742065-29273
[exit 0]

######## Scenario B: HEAD under injected 1s lstart drift (ps shim on PATH for stop/teardown)

$ ~/.no-mistakes/worktrees/a2097c65a770/01M3CVW0WT6WFCYP6EZVWHXRNR/bin/fm-herdr-lab.sh provision fm-lab-lstartB-3747466-28552
[exit 0]

$ ~/.no-mistakes/worktrees/a2097c65a770/01M3CVW0WT6WFCYP6EZVWHXRNR/bin/fm-herdr-lab.sh viewer start fm-lab-lstartB-3747466-28552
viewer attached to fm-lab-lstartB-3747466-28552 (pid 3747990)
[exit 0]
--- viewer record fm-lab-lstartB-3747466-28552 ---
launcher_pid=3747926
launcher_identity=boot=7d67b0a2-af55-4f4b-a4ea-efe5887dbf4c starttime=33979939
viewer_pid=3747990
viewer_identity=boot=7d67b0a2-af55-4f4b-a4ea-efe5887dbf4c starttime=33979941
pid 3747990: /proc starttime=33979941 boot_id=7d67b0a2-af55-4f4b-a4ea-efe5887dbf4c real lstart=[Fri Sep 25 14:20:59 2026] drifted lstart=[Fri Sep 25 14:21:00 2026] cmdline=herdr --session fm-lab-lstartB-3747466-28552 

$ env PATH=/tmp/fm-lstart-live.VGJ4/shim:~/.local/bin:~/.nix-profile/bin:~/go/bin:~/.cargo/bin:~/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/usr/local/sbin:/usr/bin:/bin:/usr/sbin:/sbin:~/.claude/plugins/cache/compact-adviser/compact-adviser/0.1.4/bin ~/.no-mistakes/worktrees/a2097c65a770/01M3CVW0WT6WFCYP6EZVWHXRNR/bin/fm-herdr-lab.sh viewer stop fm-lab-lstartB-3747466-28552
[exit 0]
pid 3747990 GONE
pid 3747926 GONE
--- viewer record fm-lab-lstartB-3747466-28552 ---
<no record>

$ env PATH=/tmp/fm-lstart-live.VGJ4/shim:~/.local/bin:~/.nix-profile/bin:~/go/bin:~/.cargo/bin:~/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/usr/local/sbin:/usr/bin:/bin:/usr/sbin:/sbin:~/.claude/plugins/cache/compact-adviser/compact-adviser/0.1.4/bin ~/.no-mistakes/worktrees/a2097c65a770/01M3CVW0WT6WFCYP6EZVWHXRNR/bin/fm-herdr-lab.sh teardown fm-lab-lstartB-3747466-28552
[exit 0]

######## Scenario C: BASE (pre-fix) under the same injected drift - reproduces the reported refusal

$ /tmp/fm-lstart-live.VGJ4/base/bin/fm-herdr-lab.sh provision fm-lab-lstartC-3750752-29387
[exit 0]

$ /tmp/fm-lstart-live.VGJ4/base/bin/fm-herdr-lab.sh viewer start fm-lab-lstartC-3750752-29387
viewer attached to fm-lab-lstartC-3750752-29387 (pid 3751386)
[exit 0]
--- viewer record fm-lab-lstartC-3750752-29387 ---
launcher_pid=3751334
launcher_start=Fri Sep 25 14:21:00 2026
viewer_pid=3751386
viewer_start=Fri Sep 25 14:21:01 2026
pid 3751386: /proc starttime=33980100 boot_id=7d67b0a2-af55-4f4b-a4ea-efe5887dbf4c real lstart=[Fri Sep 25 14:21:01 2026] drifted lstart=[Fri Sep 25 14:21:02 2026] cmdline=herdr --session fm-lab-lstartC-3750752-29387 

$ env PATH=/tmp/fm-lstart-live.VGJ4/shim:~/.local/bin:~/.nix-profile/bin:~/go/bin:~/.cargo/bin:~/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/usr/local/sbin:/usr/bin:/bin:/usr/sbin:/sbin:~/.claude/plugins/cache/compact-adviser/compact-adviser/0.1.4/bin /tmp/fm-lstart-live.VGJ4/base/bin/fm-herdr-lab.sh viewer stop fm-lab-lstartC-3750752-29387
fm-herdr-lab: lab viewer for 'fm-lab-lstartC-3750752-29387' did not detach within 5 seconds (last reason: cleared)
[exit 1]
pid 3751386 ALIVE
pid 3751334 ALIVE
foreground reason after base stop: cleared

$ env PATH=/tmp/fm-lstart-live.VGJ4/shim:~/.local/bin:~/.nix-profile/bin:~/go/bin:~/.cargo/bin:~/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/usr/local/sbin:/usr/bin:/bin:/usr/sbin:/sbin:~/.claude/plugins/cache/compact-adviser/compact-adviser/0.1.4/bin /tmp/fm-lstart-live.VGJ4/base/bin/fm-herdr-lab.sh teardown fm-lab-lstartC-3750752-29387
fm-herdr-lab: lab viewer for 'fm-lab-lstartC-3750752-29387' did not detach within 5 seconds (last reason: cleared)
fm-herdr-lab: refusing teardown of 'fm-lab-lstartC-3750752-29387' while this lab's viewer is still attached
[exit 1]
-- cleanup: base stop without drift, then teardown

$ /tmp/fm-lstart-live.VGJ4/base/bin/fm-herdr-lab.sh viewer stop fm-lab-lstartC-3750752-29387
[exit 0]
pid 3751386 GONE

$ /tmp/fm-lstart-live.VGJ4/base/bin/fm-herdr-lab.sh teardown fm-lab-lstartC-3750752-29387
[exit 0]

######## Scenario D: HEAD adversarial - record identity forged to a different starttime (PID-reuse stand-in) must not be signalled

$ ~/.no-mistakes/worktrees/a2097c65a770/01M3CVW0WT6WFCYP6EZVWHXRNR/bin/fm-herdr-lab.sh provision fm-lab-lstartD-3775407-19262
[exit 0]

$ ~/.no-mistakes/worktrees/a2097c65a770/01M3CVW0WT6WFCYP6EZVWHXRNR/bin/fm-herdr-lab.sh viewer start fm-lab-lstartD-3775407-19262
viewer attached to fm-lab-lstartD-3775407-19262 (pid 3775937)
[exit 0]
--- viewer record fm-lab-lstartD-3775407-19262 ---
launcher_pid=3775901
launcher_identity=boot=7d67b0a2-af55-4f4b-a4ea-efe5887dbf4c starttime=33981325
viewer_pid=3775937
viewer_identity=boot=7d67b0a2-af55-4f4b-a4ea-efe5887dbf4c starttime=33981326
pid 3775937: /proc starttime=33981325 boot_id=7d67b0a2-af55-4f4b-a4ea-efe5887dbf4c real lstart=[Fri Sep 25 14:21:13 2026] drifted lstart=[Fri Sep 25 14:21:14 2026] cmdline=herdr --session fm-lab-lstartD-3775407-19262 

$ ~/.no-mistakes/worktrees/a2097c65a770/01M3CVW0WT6WFCYP6EZVWHXRNR/bin/fm-herdr-lab.sh viewer stop fm-lab-lstartD-3775407-19262
fm-herdr-lab: lab viewer for 'fm-lab-lstartD-3775407-19262' did not detach within 5 seconds (last reason: cleared)
[exit 1]
pid 3775937 ALIVE
pid 3775901 ALIVE
foreground reason after refused stop: cleared

$ ~/.no-mistakes/worktrees/a2097c65a770/01M3CVW0WT6WFCYP6EZVWHXRNR/bin/fm-herdr-lab.sh teardown fm-lab-lstartD-3775407-19262
fm-herdr-lab: lab viewer for 'fm-lab-lstartD-3775407-19262' did not detach within 5 seconds (last reason: cleared)
fm-herdr-lab: refusing teardown of 'fm-lab-lstartD-3775407-19262' while this lab's viewer is still attached
[exit 1]
-- restore genuine record, stop and teardown

$ ~/.no-mistakes/worktrees/a2097c65a770/01M3CVW0WT6WFCYP6EZVWHXRNR/bin/fm-herdr-lab.sh viewer stop fm-lab-lstartD-3775407-19262
[exit 0]
pid 3775937 GONE
pid 3775901 GONE

$ ~/.no-mistakes/worktrees/a2097c65a770/01M3CVW0WT6WFCYP6EZVWHXRNR/bin/fm-herdr-lab.sh teardown fm-lab-lstartD-3775407-19262
[exit 0]

===== sessions after:
{"name":"default","default":true,"running":true}
{"name":"fm-lab-autocompact-3192236-6327","default":false,"running":true}
{"name":"fm-lab-claude-exit-3678035","default":false,"running":true}
Evidence: WSL2 host probe of btime, lstart and /proc starttime

Source: WSL2 host probe of btime, lstart and /proc starttime

14:19:40.064310246 btime=1790020660 pid1_lstart=[Mon Sep 21 15:57:41 2026] pid1_starttime=126
14:19:41.089739974 btime=1790020660 pid1_lstart=[Mon Sep 21 15:57:41 2026] pid1_starttime=126
14:19:42.104676441 btime=1790020660 pid1_lstart=[Mon Sep 21 15:57:41 2026] pid1_starttime=126
14:19:43.123967354 btime=1790020660 pid1_lstart=[Mon Sep 21 15:57:41 2026] pid1_starttime=126
14:19:44.141807026 btime=1790020660 pid1_lstart=[Mon Sep 21 15:57:41 2026] pid1_starttime=126
14:19:45.155139494 btime=1790020660 pid1_lstart=[Mon Sep 21 15:57:41 2026] pid1_starttime=126
14:19:46.167107488 btime=1790020660 pid1_lstart=[Mon Sep 21 15:57:41 2026] pid1_starttime=126
14:19:47.182897025 btime=1790020660 pid1_lstart=[Mon Sep 21 15:57:41 2026] pid1_starttime=126
14:19:48.194869517 btime=1790020660 pid1_lstart=[Mon Sep 21 15:57:41 2026] pid1_starttime=126
14:19:49.207149513 btime=1790020660 pid1_lstart=[Mon Sep 21 15:57:41 2026] pid1_starttime=126
14:19:50.218861582 btime=1790020660 pid1_lstart=[Mon Sep 21 15:57:41 2026] pid1_starttime=126
14:19:51.231671913 btime=1790020660 pid1_lstart=[Mon Sep 21 15:57:41 2026] pid1_starttime=126
14:19:52.243852953 btime=1790020660 pid1_lstart=[Mon Sep 21 15:57:41 2026] pid1_starttime=126
14:19:53.255825961 btime=1790020660 pid1_lstart=[Mon Sep 21 15:57:41 2026] pid1_starttime=126
14:19:54.266878401 btime=1790020660 pid1_lstart=[Mon Sep 21 15:57:41 2026] pid1_starttime=126
Evidence: Base vs HEAD under identical 1s lstart drift
BASE: viewer stop fm-lab-lstartC-* -> 'lab viewer ... did not detach within 5 seconds' [exit 1]; viewer pid ALIVE; teardown -> 'refusing teardown ... while this lab's viewer is still attached' [exit 1]
HEAD: viewer stop fm-lab-lstartB-* [exit 0]; viewer pid GONE, launcher GONE, record removed; teardown [exit 0]

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

🔧 **Review** - 2 issues found → auto-fixed ✅
  • ⚠️ bin/fm-herdr-lab.sh:601 - This change adds more than the required fix. It adds a per-lab .owner file written from FM_TASK_ID during provision (bin/fm-herdr-lab.sh:80-98, 156-159), a new public reap-task &lt;task-id&gt; &lt;worktree&gt; command (bin/fm-herdr-lab.sh:601-631, 679-682), and a new 'Fix 4' teardown step in bin/fm-teardown.sh:3460-3464 that runs on every non-secondmate task teardown. The intent requires one thing: viewer stop must stop recognising its own viewer as someone else's when WSL2 lstart drifts. The leaked lab is mentioned only as an observation ('suggests some teardown path may leak labs'). The change does not name the teardown path that leaked. It assumes the owner was killed before its EXIT trap ran and adds a backstop. Nothing in the intent requires the new durable state, the new command or the new teardown step. Recommended remedy: remove the owner record, reap-task and Fix 4 from this PR, and follow up separately once the leaking path is known. The captain can instead confirm that the backstop is wanted here.
  • ⚠️ bin/fm-herdr-lab.sh:81 - The owner record depends on FM_TASK_ID being set when the lab is provisioned. tests/lib.sh:54 runs unset FM_TASK_ID, and many real-Herdr suites source tests/lib.sh before they provision a lab: fm-herdr-attached-viewer-live-e2e (sources it at line 32, provisions at line 55), fm-control-claude-exit-dialog-live-e2e, fm-herdr-submit-confirm-live-e2e, fm-herdr-version-floor-live-e2e, fm-spawn-claude-transcript-live-e2e, fm-backend-herdr-presentation-e2e, and others. Example: a worker with FM_TASK_ID=T runs bin/fm-test-run.sh tests/fm-herdr-attached-viewer-live-e2e.test.sh from its worktree and the suite is killed before its EXIT trap. The lab stays registered with no .owner file, and fm-teardown.sh T finds nothing to reap, so the lab leaks exactly as in the reported incident. That makes the backstop silently partial for the test labs it is meant to catch. This defect is inside the reap-task component flagged above, so the smallest honest remedy is to remove that component. If the component stays, ownership needs a task marker that tests/lib.sh does not clear, which extends the change and needs the author's sign-off.

🔧 Fix applied.
✅ Re-checked - no issues remain.

✅ **Test** - passed

✅ No issues found.

  • Live validation: ✅ go - 4 of 5 scenarios driven live against the product
Scenario Result Live Evidence
Worker starts a real lab viewer on WSL2, then viewer stop detaches it and teardown succeeds ✅ pass live live-lab-viewer-lstart-drift-transcript.txt Scenario A: the record holds boot=7d67b0a2... starttime=33979757 matching /proc; stop exit 0 with viewer and launcher gone; teardown exit 0
With ps lstart drifted by 1s for the same live process, HEAD viewer stop still recognises and stops its own viewer, and teardown succeeds ✅ pass live Scenario B in live-lab-viewer-lstart-drift-transcript.txt: the drift shim shows lstart 14:21:00 vs real 14:20:59; stop exit 0, viewer pid GONE, teardown exit 0
Regression repro: the pre-fix code under the same drift refuses to stop its own viewer and blocks teardown ✅ pass live Scenario C: base stop exit 1 'did not detach', viewer ALIVE, teardown refused while attached; demonstrates the fix is load-bearing
Adversarial: a record whose identity no longer matches the live process (PID-reuse stand-in) is never signalled ✅ pass live Scenario D: forged starttime+1 left viewer and launcher ALIVE and teardown refused; the restored genuine record then stopped cleanly
Genuine host-clock lstart drift on WSL2 (not simulated) no longer disowns the viewer ⏸️ untested no Natural drift depends on WSL2 time-sync re-rendering the boot time, which could not be triggered without root clock changes to the host (outside the workspace boundary). It was covered by injecting dr…
  • bin/fm-herdr-lab.sh provision|viewer start|viewer stop|teardown fm-lab-lstartA-* against real herdr 0.9.0 on WSL2 kernel 6.18.33.2 (no drift)
  • The same lifecycle with a ps shim on PATH that renders lstart +1s, for HEAD viewer stop/teardown (fm-lab-lstartB-*)
  • The same injected drift against the base-commit copy of bin/fm-herdr-lab.sh plus fm-herdr-lab-viewer.py (fm-lab-lstartC-*), then cleanup without drift
  • Adversarial: live viewer record rewritten with starttime+1 on HEAD, then viewer stop and teardown (fm-lab-lstartD-*), then the genuine record restored and cleaned up
  • Host probe sampling /proc/stat btime, PID 1 ps -o lstart and /proc starttime over 15s
  • bash tests/fm-herdr-lab.test.sh (focused suite incl. test_viewer_stop_survives_lstart_drift and test_viewer_stop_refuses_a_reused_pid)
  • bash tests/fm-herdr-attached-viewer-live-e2e.test.sh (existing real-Herdr attached viewer e2e)
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

…ask left behind

fm-herdr-lab.sh viewer stop compared a recorded `ps -o lstart` string with a
fresh reading. On WSL2 ps derives lstart from a wall-clock boot time that
re-renders one to two seconds apart for the same live process, so the helper
disowned its own viewer and teardown refused while it stayed attached.

The viewer record now stores a kernel identity: /proc/<pid>/stat starttime in
clock ticks, qualified by the boot id, with the locale-pinned lstart kept as
the exact-match fallback on hosts without that /proc. The Python launcher takes
its identity values from the same helper function, so one function owns the
format.

A lab server outlives the shell that provisioned it, so a lab whose owner died
before its EXIT trap ran stayed registered after its task ended. Provision now
records the owning task id and working directory when FM_TASK_ID is set, and
the new `reap-task` command tears down exactly those labs through the guarded
teardown path. fm-teardown.sh runs it before its worktree process reap, which
would otherwise kill only a lab server started from the worktree and leave the
session registered.
@dardant
dardant merged commit f7a07d9 into main Sep 25, 2026
21 checks passed
dardant added a commit that referenced this pull request Sep 25, 2026
Resolve the Herdr lab viewer overlap in favor of main's kernel start-time
identity (#11), which supersedes this branch's own WSL2 viewer fix; every
other part of this branch is kept.
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