Skip to content

zmx seed daemon inherits redirected pipes and prevents bounded EOF attestation #834

Open

Description

@coneilen

Describe the bug
The live capture core cannot finish attesting an exited daemonising zmx seed when the daemon inherits the launcher's redirected stdout/stderr pipes. The owned launcher exits, but its daemon keeps those pipes open; the required bounded both-stream EOF never arrives. The single coordinator B slot stopped before any GraphCode GUI launch or image capture.

To reproduce
Steps to reproduce the behavior:

  1. Historical run cdba36a51f144798b40fd78bdb50251b used the unchanged production supervisor at source 944ee565ed2e36885b8cf1087fc5fbe2d05d38b6 (full tree identical to merged main badcf5149be2a01abd866e4cf180521f485fa2c4) and pinned zmx source 785b3fd15dcafd1882b495c831a10f98c201b908.
  2. The B worker launches its real offline peer and the zmx daemon seed. The original slot lease was 640c69a9-049e-46ba-9ebe-5c813a3f93a1, granted 2026-10-15T23:50:18.4647178Z; this is retained provenance, not a new execution grant.
  3. At 23:50:45Z, worker PID 37428 recorded: Process.Start identity attestation failed for zmx-seed PID 36288: Exited zmx-seed PID 36288 did not reach bounded stdout/stderr EOF; cannot attest it genuinely gone.; guarded rollback also failed: Rollback stdout reader did not reach bounded EOF for PID 36288.
  4. Worker exit was 1. The final manifest contains zero GUI launches, zero images and a pending seed-launch rollback. The supervisor release is CLEANUP_UNPROVEN, not a zero-survivor success.

No repeat live slot, experiment, process-name kill or production-source change is authorized by this issue.

Expected behavior
A real daemonising zmx seed must have separately owned, bounded output capture and generation-bound lifecycle evidence for its launcher, daemon and descendants. Launch/rollback/cleanup must not depend on a pipe that the deliberately live daemon inherits. Keep fail-closed ownership, kernel-exit and EOF requirements meaningful; do not turn missing EOF, empty CIM results or an unproved descendant into success.

Proposed design to plan-review after #833: redirect the seed's stdout/stderr to owned files, or use a launch mechanism that prevents unwanted handle inheritance. Specify launcher versus daemon attestation and when files/streams become quiescent; a file snapshot is not proof that a live writer has closed. Track and clean up the actual owned daemon/descendants by verified generation. Preserve diagnostics and original failure classification.

Add a real-zmx, no-GUI seed rehearsal to the preflight so this path cannot remain hidden behind the Start-CapOfflineSeed stub. Test-only #833 handles compiler-log readers; this issue concerns the capture core's real daemon-launch path and must not be silently folded into that fixture-only PR.

Screenshots
None: the manifest reports guiLaunches=[] and images=[]; the official release reports captures=0.

Environment

  • GraphCode version: production capture source 944ee565ed2e36885b8cf1087fc5fbe2d05d38b6, tree a6ccbfd5580d9fba0c336da93e716609c4538ce0, identical to main badcf5149be2a01abd866e4cf180521f485fa2c4.
  • macOS version: not applicable; Windows and PowerShell 7.
  • Backend: isolated offline fixture, no paid/backend turn.
  • Pinned zmx binary SHA-256: DFFF744FA8D7B0FB483416674C2FF4C2647EA46A3BDD82A95DC173A1FA07DC5B.

Additional context
Retained evidence under C:\gccp\capture-coordinator-b1:

  • worker.log: primary seed attestation and rollback error.
  • manifest.json: run ID cdba36a51f144798b40fd78bdb50251b, launcher PID 36288, creation file time 134365818411426552, kernel-exited=true but pending rollback; peer PID 46440 has kernel exit and both EOF true; no GUI/images.
  • lease-release.json: SHA-256 DED09AD2C54E77E4AF6E4251F06467F2BEABEC999B9D7CE80E3536A1C985058F, CLEANUP_UNPROVEN, worker stdout/stderr EOF false, no accepted final journal binding. Its empty survivor array does not override the unproved cleanup status.
  • Coordinator console: C:\gcs\coord2\coor-b-slot1-console.txt, SHA-256 7DE661F76921934337EEAA909AF20DB80BFFB44F22F8DB77AE1DA8E20DA5D0C5.
  • Journal directory: C:\gccp\runs\cdba36a51f144798b40fd78bdb50251b\process-journal; immutable published snapshots remain preserved.

Operator-observed root cause: launcher PID 36288 spawned daemon PID 22760, which inherited the launcher's output pipes. The operator stopped the verified orphaned fixture daemon at C:\gccp\runtime\zmx.exe and subsequently reported no remaining zmx, graphcode-windows or graphcoded processes. This later cleanup is a separate operator action, not a rewrite of the original CLEANUP_UNPROVEN receipt or an invented original EOF proof.

The passing 59-case core-59b preflight explicitly stubs Start-CapOfflineSeed and is not a real zmx/peer rehearsal; it therefore did not exercise this defect. The live track is stopped with no retries. Refs #826, #829 and #833. Schedule this core prerequisite after #833 and before D's Quick Chat adapter or option 1 integration. Plan review must precede any source edits; close nothing on the strength of this report alone.

Activity

  1. coneilen commented on Oct 16, 2026

    @coneilen
    CollaboratorAuthor

    Observed EOF-attestation-family failure to retain for #834; underlying cause is not established.

    In original PR #836 hardening run https://github.com/scgopi/GraphCode/actions/runs/38832369710, Windows 2025 job https://github.com/scgopi/GraphCode/actions/runs/38832369710/job/116753830293 executed 59 UiCaptureDriver cases: 57 passed and 2 failed. The primary reported case was same capture command handles target nonzero: Failure/release/EOF evidence missing. The repaired rehearsal-report gate also rejected the unsuccessful rehearsal. No uploaded case artifact was available (artifact inventory count 0), so the log does not identify which missing proof field or timing caused this.

    This was the default stubbed offline/no-desktop rehearsal, NOT a real pinned-zmx/peer run or proof of the #834 daemon-inherited-file/pipe mechanism. The same source head 359a033 passed 59/59 on Windows 2022, and the ONE operator-authorized failed-job diagnostic rerun passed an actual 59/59/0 on Windows 2025 (job 116759891173, same workflow attempt 2). Preserve the original failed observation and watch for recurrence; do not silently treat the rerun as a source fix.

    Original raw log: C:\gcs\coord2\833-836-hardening2025-failed-job-raw.log; SHA-256 C55FDB3B79E7FEBFBC9AC1BD34AACE603DF6BABA3018D3A63829A52C280E4436. Diagnostic success raw log: C:\gcs\coord2\833-836-hardening2025-diagnostic2-success-raw.log; SHA-256 6FE4D50158FEEB038E8F859F4EE6A8FBD1E62EA68A40D750EFE43B0B4F52F273.

    PR #836 is now merged as 78ef35d with its exact reviewed fixture blob preserved. This clears only the fixture-fix merge dependency: it does NOT grant #834 implementation/native execution, change its approved seven-import design, or resume the stopped live B capture track. Exact clean work surface, sole-writer lease, source-boundary review and orphan-cleanup/native grants still apply.

  2. coneilen commented on Oct 16, 2026

    @coneilen
    CollaboratorAuthor

    Plan approved: job-based ownership pivot

    This is an operator-delegated design decision for later user review, not a claim that the user personally reviewed the source or that runtime acceptance has passed. The previous custom-launch candidate remains frozen as history on its existing branch/worktree; the pivot uses a new branch, coneilen-capture-job-ownership.

    Approved mechanism: assign a gated normal launcher/worker to a kill-on-close job before releasing it; clear the worker's own standard-handle inheritance before any child launch; launch public pinned zmx normally; use owned output files, job membership and terminate/wait-empty/ActiveProcesses=0, with independent file-close and real pipe-EOF proofs. Keep J5 binding/publication/hash-chain functions byte-unchanged, strict generic J4 foreign-process refusal, and N1's strict owned-root fixture.

    The corrected standalone diagnostic established that real zmx can start inside the added nested job (exit0,444ms,endpoint probe successful). Its in-script cleanup receipt failed; the separate operator post-hoc clean census does not retroactively repair that receipt. The exact cause of the old custom launch's exit1 remains unproved. The pivot is an approved ownership/proof design choice, not a demonstrated exact-parameter diagnosis.

    M1 source is now frozen for one independent Sonnet source review: five leased files, no commit or native suite, ParseFile0, exact-AST compile-only with six loaded types, and 26 protected J4/J5 function extents byte-identical. Source-owned imports are 26 declarations/24 unique APIs. Prospective case counts are64offline and67with real-seed definitions, retaining original59; zero behavioral cases have run for this source. Handoff SHA256 99C9E8EB59FEC7FAF0F80EAC57BAFAEDD623290C77420148A7CB358C8D42E7CF; patch 94B5611A9A5206B1B67FDE39044BD1681D40E4A9802819DE2E66BD90C051271F.

    Runtime M2 stays held until review and explicit admission. It must cover original59 plus the updated positive count, real normal/nonzero/timeout paths, job-empty/file-close/raw-EOF proof. If M2 fails, stop and report without a fix chain. M3 is a new-branch PR using historical run cdba36a51f144798b40fd78bdb50251b as RED, not an unsafe reproduction.

    Review and publication must disclose the changed pre-execution identity proof, foreign-writer absence and cached/duplicated standard-handle limits, plus the current private-pipe-pump versus direct-file-STD and uncensused app/peer receipt questions. No candidate GREEN, capture/parity acceptance, issue closure or ledger promotion is claimed here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions