LearnNewsExamplesServices
Frontmatter
id14993
titleHarness smoke crash sweep: bind cleanup to exact process-instance identity
stateOpen
labels
bugaitesting
assigneesneo-gpt
createdAt12:20 AM
updatedAt12:20 AM
githubUrlhttps://github.com/neomjs/neo/issues/14993
authorneo-gpt
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]

Harness smoke crash sweep: bind cleanup to exact process-instance identity

Open Backlog/active-chunk-5 bugaitesting
neo-gpt
neo-gpt commented on 12:20 AM

Context

Exact-head cycle-3 review of PR #14976 (83ae579c3) proved that its crash-recovery sweep still confuses same program with same process instance. The PR correctly narrows persisted run-state to the isolated smoke:brain path: normal product attach/own mode neither writes nor sweeps this record. That makes this a separable smoke-recovery safety leaf rather than another author cycle on the already-complete Brain-in-harness topology.

The direct falsifier is concrete: a stale record for PGID 7001 carrying entry ai/daemons/orchestrator/daemon.mjs, followed by a recycled PGID now owned by a later or sibling-checkout process running that same relative entry, passes command.includes(entry) and receives SIGKILL(-7001).

The Problem

harness/brain.mjs persists only:

{"pgid": 7001, "entry": "ai/daemons/orchestrator/daemon.mjs"}

sweepStaleRunState() then treats a current command containing that relative script text as ownership proof. Every Neo checkout launches the same relative entry, and PID/process-group identifiers are reusable. The check therefore proves the current process is an orchestrator, not that it is the exact child created by the crashed smoke run.

The failure is destructive: a later process or a sibling checkout can be group-SIGKILLed.

Architectural Reality

Process cleanup needs two identities:

  1. checkout identity — the absolute entry path, not a repo-relative string shared by every clone;
  2. process-instance identity — a unique per-spawn token mechanically visible in the live process command, not a PID or program name.

Both supervised entrypoints are compatible with a trailing ownership argument: orchestrator/daemon.mjs and devFleetServer.mjs inspect only process.argv[1] for direct-entry detection and do not parse/reject later args.

The token is non-secret ownership metadata. It belongs to the harness lifecycle owner, not AiConfig, the registry, or the child organism.

The Fix

In startBrainChild():

  • resolve the entry to an absolute path under the selected repo root;
  • generate a per-spawn token (with an injectable deterministic seam for unit tests);
  • launch node --neo-harness-owner=;
  • expose/store the absolute entry + token alongside the child handle.

In smoke run-state, persist {pgid, entry: , ownershipToken}.

In sweepStaleRunState(), signal only when the current command proves both the exact absolute entry and the exact ownership token. Any malformed/legacy record, missing process, sibling checkout, same entry with a different token, or same token with a different entry fails closed by skipping the signal and clearing the stale record.

Contract Ledger

Target Surface Source of Authority Required Behavior Fail-Closed Fallback Evidence
child process identity startBrainChild() absolute checkout entry + unique per-spawn argv token spawn/record without a verifiable pair is never sweep-authorized injected-spawn unit probe
crashed-run record writeRunState() persist PGID + exact entry + ownership token per group malformed/legacy records are skipped round-trip spec
stale cleanup sweepStaleRunState() group-SIGKILL only on exact entry+token match mismatch/missing identity => no signal, record cleared same-entry/different-token and different-entry/same-token falsifiers
clean teardown teardownBrain() / clearRunState() clean runs leave no stale authorization record idempotent absence existing clean-stop pin

Acceptance Criteria

  • Each smoke-supervised child has an absolute checkout entry and unique per-spawn ownership token visible in its command line.
  • Run-state persists the exact identity pair for each PGID.
  • Exact entry + exact token authorizes cleanup and still kills the stale owned group.
  • Same entry + different token does not signal.
  • Same token + different checkout entry does not signal.
  • Malformed/legacy records and dead groups remain no-ops; the stale record is cleared either way.
  • Clean teardown still clears run-state.
  • Focused harness lifecycle specs, syntax, and git diff --check pass.

Out of Scope

  • Product-mode attach/own semantics.
  • Replacing process groups or the SIGINT → grace → SIGKILL ladder.
  • Orchestrator child-supervisor semantics.
  • Generic process-identity infrastructure outside the Electron harness.
  • AiConfig changes.

Decision Record impact

none — bounded correctness hardening inside ADR 0034 §2.1's existing one-lifecycle-owner contract.

Avoided Traps

  • Do not treat a PID, relative program name, or command substring as process-instance identity.
  • Do not query or persist secrets to identify a process.
  • Do not move lifecycle ownership into the child organism.
  • Do not keep legacy records kill-capable; fail closed by skipping.

Related

Parent surface: #13033 · originating leaf/PR: #14967 / #14976 · future Brain lifecycle arc: #14230.

Live duplicate sweep: latest 25 open issues + exact all-state searches for stale PGID ownership token run-state and process group PID reuse sibling checkout SIGKILL at 2026-07-10T22:19Z returned no equivalent. A2A process-identity window contains only the #14976 review handoff; no competing claim.

Origin Session ID: 019f484c-662f-7f31-969a-cbde373efd4a

Retrieval Hint: query_raw_memories("harness smoke run-state stale PGID same command sibling checkout SIGKILL ownership token")