Skip to main content

discover_stray_devflow_processes

Function discover_stray_devflow_processes 

Source
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] is sh, argv[1] is -c, and argv[2] (the script) contains [MONITOR_WRAPPER_MARKER] verbatim.
  • Layer 2 — the trailing advance child. argv[0]’s basename equals [DEVFLOW_BINARY_NAME] AND argv[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:

  1. 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.

  2. 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.

  3. Structural, not exec-confirmed (25-12/999.47, the production half of the defect class). This is a structural match over /proc/<pid>/cmdline alone. During a process’s own fork()->execve() window — process_start_time’s doc comment is this codebase’s authoritative statement of the mechanism — /proc/<pid>/cmdline transiently reports its PARENT’s argv, not its own. A transient child of the monitor wrapper, or of devflow 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 (see process_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 with process_age and STRAY_MIN_AGE; unreachable_stray_candidates (devflow-cli::commands), interposed before either doctor or reap_stray_candidates acts, discharges the second — never this function.

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.