Skip to content

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

Closed

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.

  3. coneilen commented on Oct 16, 2026

    @coneilen
    CollaboratorAuthor

    Live bounded-lifecycle evidence; scene failure is separate

    The ONE admitted B run bc69d8cca8f244da89d0ffc30f819502 used the merged capture core at d814e14372c3cea499c8c7227b36dd47b1ce7514, pinned real zmx, and the five-character owned root C:\b9. The original seed/launcher-EOF startup hang did not recur in this observed attempt:

    2026-10-16T19:23:45.1451495Z ZMX_SEED_EXIT phase=before exit=0 pid=61036 args=run graphcode-11111111-1111-4111-8111-000000000001 -d cmd.exe /Q /K echo Offline card fixture - no backend
    2026-10-16T19:23:50.1559210Z ZMX_SEED_READY phase=before daemon=1 cmd=2 conhost=2
    2026-10-16T19:23:56.9284922Z ACTION phase=before action=actual-size name= result={"Action":"actual-size","Name":"Actual size","AutomationId":"actual-size","Invoked":true}
    

    The supervisor ran 19:23:08.9578401Z to its official release at 19:24:01.2659832Z. The capture then failed on the first Alpha project selector, not seed startup: Expected exactly one visible UIA element Name='Alpha' id='' prefix='open-project-'; found=0.

    Actual retained proof Result
    Official outcome FAILED_ZERO_SURVIVORS; KERNEL_EXIT_AND_EOF_PROVEN; worker exit 1
    Supervisor-owned worker streams Actual stdout and stderr EOF both true
    Umbrella job Active processes 0, explicitly observed before handle close; no pre-stop members or typed safety-net kills
    Seed job 55ca478dd6f54df98aec272f95fbfc26 CLOSED, Active processes 0, census proven for five daemon/conhost/cmd descendants; launcher stdout/stderr EOF both true
    Owned lifecycle Eight positive generation-bound kernel-exit records; peer, launcher and app each have both actual reader EOFs
    Cleanup No pending launches, kernel-verified survivors or cleanup failures
    Immutable publication All 16 numbered envelopes, decoded payload hashes/lengths, predecessor links and bindings independently checked; exact terminal 16 and manifest agree with the unique last canonical worker stdout record

    Scope matters: the failed GUI was the historical-before app at 112d4583, PID 24808. The current-after app never ran; there are zero images, so the overall B capture remains FAILED and supplies no visual/parity acceptance. The distinct selector/observability failure is tracked in #848; it is not a positive #831 signature match.

    The seed output transport is explicitly PRIVATE_TARGET_PIPE_PUMPS_TO_OWNED_FILES, with targetPipeEofClaimed=false. Its two owned files are recorded closed/stable with verified hashes; that is not target raw-pipe EOF, universal absence of inherited/cached/duplicated handles, or host-wide writer/process absence. Actual worker/launcher EOF and owned-job closure are the bounded evidence here. No capture budget, retry or ownership/cleanup gate was relaxed.

    Retained artifacts: C:\b9\capture-b9 and C:\b9\runs\bc69d8cca8f244da89d0ffc30f819502. Official release SHA-256: 1C98E5161D55DFC1480F390ADAC59A76284229DE57A94840AA08EADB596E450B; manifest: 77826F8B3A75F1599C8FB26CF5D44420C66096934D87D3CB07498434AC130D16; terminal envelope: 6EE5FB5918CC1A7D700773150F9395BA4B5FC136803F02D825BAE7C0C9DC4150. Earlier failed logs, including the original CLEANUP_UNPROVEN run, remain unchanged.

    Closure proposal: the qualified merged core plus this actual live, job-owned seed/readiness/cleanup path meet #834's original functional bounded-lifecycle requirement. I propose closing that defect while leaving #848 and the remaining capture/visual work open. The operator retains the closure decision; this comment does not close anything or promote the ledger.

  4. coneilen commented on Oct 16, 2026

    @coneilen
    CollaboratorAuthor

    Closing as completed, based on the operator's review of the evidence:

    • Fix merged in Bound Windows capture seed lifecycle and output ownership #845 (d814e14372c3cea499c8c7227b36dd47b1ce7514): the job-owned seed lifecycle, a short owned ZMX_DIR with a typed 240-character lease guard, a share-tolerant receipt reader, and precompiled fixtures.
      • Local contained qualification passed 72/72.
      • All 9 required checks passed on the exact head.
    • Live bounded-lifecycle evidence is in the comment above. Real zmx seed exited 0 and reached ready (daemon=1, cmd=2, conhost=2). Cleanup was KERNEL_EXIT_AND_EOF_PROVEN, with zero survivors and umbrella job Active 0 observed before close.

    The overall B capture in that run still FAILED, with zero images. That failure is a separate selector issue, tracked in #848. Remaining scope stays open in its own issues:

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