Skip to content

Bound cleanup of app-owned zmx clients - #880

Merged
coneilen merged 2 commits into
mainfrom
coneilen-app-child-cleanup-875
Oct 18, 2026
Merged

coneilen merged 2 commits into
mainfrom
coneilen-app-child-cleanup-875

Conversation

@coneilen

@coneilen coneilen commented Oct 18, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Recognize capture-owned zmx clients spawned by the held GraphCode app generation, so guarded teardown can close the inherited app output pipes instead of refusing the child and leaving EOF unproved. Refs #875; #866 remains separate because its action-wait/cleanup-reserve deadline is a different path.

Changes

  • Validate the actual app zmx argument forms (ls, seeded/owned-session attach, resize, and kill --force) instead of requiring the app-generated prefix to appear somewhere in every command. D sessions remain exact, foreign sessions and unexpected commands remain refusals, and the executable must still be the capture-owned runtime image.
  • Establish ancestry using the held native parent generation, native child parent/session queries, and exact kernel birth/exit times. CIM supplies candidate and command metadata, not ownership authority; descendant queries are filtered to the held parent set rather than a host-wide census.
  • Retain each app's guarded descendant handles, including validated client console hosts, across discovery and cleanup. Recovery stops that app's set, not the global seed/app descendant list. Keep the existing bounded reads of the actual app stdout/stderr; job-empty and task completion alone are not substituted for stream EOF.
  • Add real spawned-child coverage in the existing test root: exited and live app-like parents, inherited stdout/stderr, seeded and shell clients, repeated discovery, D sessions, wrong images/roles/generation stamps, foreign or malformed commands, unrelated parents, and forged advisory parent metadata. Fixtures are assigned to a private kill-on-close job before releasing their spawn gate.
  • After observing the default suite pass 149/149, change only the single capture expected-count literal in ValidationRunner.Tests.ps1 from 140 to 149. Its actual predicate also passed a five-case check with the real report and stale-count, zero-count, failure, and wrong-type negatives; no other registrar code is changed.
  • Operator-authorized second commit 51888b6a changes only the shared hidden-action fixture's lease from 2 to 15 seconds for both late-success and terminal-refusal cases. All assertions, counters, timestamp comparisons, snapshot/ownership checks and teardown checks remain unchanged; production deadlines, polling defaults, cleanup, registration and test counts are unchanged.

Test plan

RED: pwsh -NoProfile -File Tools\windows\Tests\UiCaptureDriver.Tests.ps1 -AppChildContractOnly -ScratchRoot D:\g875-e4ba\red3 -PrebuiltFixtureManifest D:\g875-e4ba\red3-prebuilt\fixtures.json -PrebuiltFixtureManifestSha256 53B6D6C3985E504571AAEA90542E2DA63B3517E295EA462FD21B27C9757E58B7 -> exit 1; 7 executed, 1 passed, 6 failed; actual seeded/listing child refused with "Unrecognized live zmx child"; actual app stdout "did not reach bounded EOF after guarded cleanup."
GREEN: pwsh -NoProfile -File Tools\windows\Tests\UiCaptureDriver.Tests.ps1 -AppChildContractOnly -ScratchRoot D:\g875-e4ba\final-focused -PrebuiltFixtureManifest D:\g875-e4ba\final-prebuilt\fixtures.json -PrebuiltFixtureManifestSha256 87FBAC89EE4B195B14B264339E8D93F9E76B3E1F5A146A8DB2B385B74567DB26 -> exit 0; 9/9 passed, including real spawned-child cleanup and both actual inherited app pipe EOFs.
REGRESSION: pwsh -NoProfile -File Tools\windows\Tests\UiCaptureDriver.Tests.ps1 -ScratchRoot D:\g875-e4ba\final-default -PrebuiltFixtureManifest D:\g875-e4ba\final-prebuilt\fixtures.json -PrebuiltFixtureManifestSha256 87FBAC89EE4B195B14B264339E8D93F9E76B3E1F5A146A8DB2B385B74567DB26 -> exit 0; 149/149 passed, including existing launch, identity, selector, admission, scene-component, job, journal and release contracts.

The focused suite ran 21 real spawned-child fixture cases in nine groups: eight positive guarded cleanups and thirteen non-adoption controls. Positive child receipts prove held kernel exit and both successful actual app stream EOFs before the test's final safety-job cleanup. Negative fixtures are not adopted or killed by production discovery; the test subsequently closes only its own contained processes. All 21 private safety jobs were observed empty before closure and all their final actual app pipes reached EOF. The generation negative corrupts a saved stamp; it does not force OS PID reuse. The advisory-parent negative changes only a targeted metadata reading for a real test-owned child, not any process.

Retained unsuccessful attempts are not relabelled as GREEN: early fixture setup and reporting mistakes, an Add-Type assembly-reference omission, a fixture-only console-parent issue, and a focused run that printed 9 passes but exited 1 because its expected-count literal was stale. The first wider run executed 147 cases with 145 passed and 2 failed: the exact native inventory still expected 26 imports, and its failure correctly made the report gate refuse the run. The updated inventory explicitly preserves those 26 declarations and adds the four new native imports (30 exact declarations); no existing check or negative coverage was removed. Raw failing and corrected logs remain in separate D:\g875-e4ba attempt directories.

The separate -JournalContractOnly run in D:\g875-e4ba\journal1 passed 10/10 with exit 0. pwsh -NoProfile -File Tools\tdd\Tests\TddEvidence.Tests.ps1 -> exit 0, PASS. git diff --check and parsing all four changed PowerShell sources -> PASS.

pwsh -NoProfile -File Tools\windows\Tests\UiCaptureDriver.Tests.ps1 -RequireRealSeed -RealSeedZmxPath D:\g875-e4ba\runtime\zmx.exe -RealSeedZmxSha256 EB6DC33A6E9908791A6612B4316C5D8EBEA154731F229F3553A870E75E139D95 -ScratchRoot D:\g875-e4ba\final-real -PrebuiltFixtureManifest D:\g875-e4ba\final-prebuilt\fixtures.json -PrebuiltFixtureManifestSha256 87FBAC89EE4B195B14B264339E8D93F9E76B3E1F5A146A8DB2B385B74567DB26 -> exit 0; 152/152 unique cases passed. All three real-provider lifecycle cases ran both phases (six seed exits zero): normal released RELEASED_ZERO_SURVIVORS with worker exit 0; nonzero and timeout released FAILED_ZERO_SURVIVORS with worker exit 1. All had zero cleanup failures/survivors, observed job zero, and actual launcher/worker stdout/stderr EOF. Seed output remained explicitly OWNED_FILES_JOB with closed, bound files, not a claim of seed raw-pipe EOF. The provider was a read-only copy of a retained build, independently matched to the current pinned commit and artifact SHA; no fresh provider build is claimed.

One fresh-eyes read-only code review of the original #875 four-file diff at e9aba03d found no blocking issues. It did not independently execute the runtime tests and predates the operator-authorized fixture-only second commit; no additional review was requested or run for that one-literal correction. No full ValidationRunner.Tests.ps1 run is claimed; only its approved single literal changed, and its actual count predicate was executed as described above.

Second-commit accepted hosted RED: Windows release hardening run 39163072528, failing Windows 2025 job 117780294289, artifact ui-capture-hardening-windows-2025-pwsh-1 / ID 12150305604, selector-action-late-fields.json. The target became available and was invoked: initial read 2026-10-18T20:31:48.5461066Z, available 2026-10-18T20:31:48.8987106Z, invokes 1, resolver entries 4, terminal snapshots 0. The action then failed the post-action fixture deadline check with Reserved worker deadline reached at stage 'after action open-project'. at 2589.5537 ms, after its fixture-only two-second lease. This is not evidence of a product polling failure. HWND destroyed/thread closed were both true; the terminal case passed in that artifact. The full unrelated run was 140 executed / 138 passed / 2 failed, not a green baseline. Its downloaded failure bytes remain unmodified under D:\g875-e4ba\ci-selector-flake.

Second-commit GREEN and related regression: pwsh -NoProfile -File Tools\windows\Tests\UiCaptureDriver.Tests.ps1 -SelectorRefusalContractOnly -ScratchRoot D:\g875-e4ba\selector-fixture15 -> exit 0; 16/16 passed, with both actual affected hidden-action cases present exactly once. Late: invokes 1, resolver entries 3, available after initial read, snapshots 0, no failure, destroyed/thread closed true, 1646.8199 ms. Terminal: invokes 0, resolver entries 42, snapshots 1, original timeout refusal and typed last-poll snapshot retained, destroyed/thread closed true, 15139.6073 ms. Never-shown owned HWND/thread fixtures only; no foreground, physical input or capture. The two logged CAP_TRACE_FAILURE lines belong to existing deliberate observer-failure negative controls, which passed their unchanged assertions.

The earlier 149-case default and 152-case real-provider qualification above belong to original #875 head e9aba03d, before this test-fixture-only literal correction. Neither full suite, provider build nor full runner was repeated for the second commit, and no all-green hosted-CI claim is made.

No production app, foreground action, screenshot, live capture, physical input, hardware, macOS parity, or full C5 release was exercised or claimed. Historical C4/C5 CLEANUP_UNPROVEN receipts remain unchanged. make test and make check were not run on this Windows host.

Checklist

  • I have read the Contributing Guidelines
  • I have signed off my commits (git commit -s) per the DCO
  • Tests pass locally (make test)
  • Code follows the existing style (make check)
  • I added the test/contract before the implementation and observed the intended RED failure

coneilen and others added 2 commits October 18, 2026 13:59
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: e4ba78f8-f77a-45ad-8b0c-ce093d01bcea
Signed-off-by: Colin Neilens <coneilen@microsoft.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: e4ba78f8-f77a-45ad-8b0c-ce093d01bcea
Signed-off-by: Colin Neilens <coneilen@microsoft.com>
@coneilen
coneilen force-pushed the coneilen-app-child-cleanup-875 branch from 51888b6 to c92bfe4 Compare October 18, 2026 20:59
@coneilen
coneilen marked this pull request as ready for review October 18, 2026 21:31
@coneilen
coneilen merged commit d8157b2 into main Oct 18, 2026
26 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant