pub fn discover_stray_devflow_processes() -> Vec<StrayProcess>Expand description
Census both of DevFlow’s orphan-prone process layers directly from the OS process table — the only remaining discovery surface once a project root has been deleted off disk, taking every registry entry, lock file and state file with it (999.44).
This is a pure, read-only survey: it never signals a process. Deciding whether to act on a result, and re-confirming identity immediately beforehand, is the caller’s job.
Two structural matchers, deliberately narrower than the predicate
999.47 disproved (which matched ANY argv element whose basename began
with the binary name, so sleep /tmp/devflow-scratch/x was a false
positive):
- Layer 1 — the monitor wrapper.
argv[0]issh,argv[1]is-c, andargv[2](the script) contains [MONITOR_WRAPPER_MARKER] verbatim. - Layer 2 — the trailing advance child.
argv[0]’s basename equals [DEVFLOW_BINARY_NAME] ANDargv[1]equals [ADVANCE_SUBCOMMAND].
Neither matcher scans all argv elements or matches a prefix; both check specific, named positions only.
Two hard constraints on the census, both load-bearing:
-
No parentage filter. These orphans reparent to the user’s per-user service manager, not to the init process — a parent-identity filter was directly measured against this repository (23-FINDINGS.md) to report zero orphans while 14 genuinely existed. This function does not consult parentage at all.
-
Never return a process owned by another user. Each candidate’s owning uid is compared against the caller’s effective uid, and anything that does not match is skipped — the concrete hazard is a caller later signalling a stranger’s process on a shared machine.
-
Structural, not exec-confirmed (25-12/999.47, the production half of the defect class). This is a structural match over
/proc/<pid>/cmdlinealone. During a process’s ownfork()->execve()window —process_start_time’s doc comment is this codebase’s authoritative statement of the mechanism —/proc/<pid>/cmdlinetransiently reports its PARENT’s argv, not its own. A transient child of the monitor wrapper, or ofdevflow advance, therefore matches Layer 1 or Layer 2 respectively while being neither. This census does not — and deliberately should not — filter that case out: a census that guessed at exec status would also drop genuine strays, and it has no reliable way to distinguish the two (seeprocess_age’s own doc comment for why). It is the caller’s obligation not to act on an unqualified census result — and that obligation has TWO parts, bounding two DIFFERENT hazards, neither of which discharges the other (CR-01, 999.44/DEN-68):- The age floor (
process_age/STRAY_MIN_AGE) bounds the fork/exec cmdline-inheritance window above — “is this argv match even real yet.” - Registry-reachability (
commands::unreachable_stray_candidates,devflow-cli::commands) bounds a different question — “is this process alive AND OWNED by a live registry entry, lock file, or state file” — which the age floor says nothing about: a monitor wrapper minutes old sails straight past it while still being a live, registered process, not a stray.
reap_stray_candidates(devflow-cli::commands) is the one caller with a destructive consequence, and it discharges the first withprocess_ageandSTRAY_MIN_AGE;unreachable_stray_candidates(devflow-cli::commands), interposed before eitherdoctororreap_stray_candidatesacts, discharges the second — never this function. - The age floor (
Every read failure is tolerated silently (a pid that vanishes between
the directory listing and the cmdline/stat read is normal churn, not an
error), and an unreadable /proc returns an empty list rather than
propagating an error.