Skip to content

ci: root macOS test discovery and diagnose silent process waits - #5483

Closed
luvs01 wants to merge 3 commits into
lidge-jun:devfrom
luvs01:fix/macos-catalog-fixture-hang
Closed

luvs01 wants to merge 3 commits into
lidge-jun:devfrom
luvs01:fix/macos-catalog-fixture-hang

Conversation

@luvs01

@luvs01 luvs01 commented Sep 22, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • Addresses the silent macOS shard failure in [Bug]: spawned Bun child processes stop producing output and never exit, on both macOS and Windows CI legs #4956. The general lane now uses ./tests, an explicit discovery root, instead of the repository-wide tests name filter. This keeps the same test inventory and shard ratio while avoiding unrelated directory traversal and descriptor retention in pinned Bun 1.4.0.
  • Add a read-only observer beside the original foreground Bun/tee pipeline. After sustained log silence, it identifies the owning shell's single direct Bun child, records its descendant process names without argv/environment, and requests one native stack sample. It never signals or retries the suite. The original Bun exit status remains authoritative.
  • Add CI-only phase boundaries to the catalog picker fixture and deterministic observer contracts for silence, continued progress, missing/ambiguous ownership, and termination while a diagnostic child is running. Exact argv and shell contracts preserve the full test root, shard ratio, exclusion ownership, first-failure propagation, budgets and assertions.
  • The controlled reproduction links the macOS stall to Bun's synchronous child wait: an already exited child remains unreaped while the parent spins in SpawnSyncEventLoop::tick_with_timeout. Rooted discovery removes the observed failure trigger; this PR does not patch Bun itself or assert that the separately reported Windows fixture failures share that root cause. Maintainer sponsorship and security review remain required before merge.

Verification

  • Bun 1.4.0: bun test tests/codex-integration/catalog-full-picker-order.test.ts tests/ci-workflows/macos-serial-lanes.test.ts tests/ci-workflows/ci-workflows.test.ts --timeout 60000 — 170 pass, 29 skip, 0 fail; Unix-only shell cases retain their existing Windows skip.
  • Executed tests/fixtures/macos-stall-observer.sh through Git Bash — all five observer scenarios passed, including reaping the diagnostic sleep while leaving the owner alive.
  • bun run typecheck, bun run structure:check, bun run privacy:scan, shell syntax checks and git diff --check passed.
  • bun run prepush was run against the actual dev comparison and failed: its test subprocess reached the 15-minute budget (exit 124), with Windows ACL/EBUSY/timeouts among the failures. This is not a full-suite pass.
  • Hosted baseline reproduced the original fix(kiro): avoid mixed-script estimator allocations #5248 tree under Bun 1.4.0 and shard 1/2: first-case cleanup completed, second-case catalog probing stalled, and the job reached 20 minutes. Native frames were symbolicated against the matching official ARM64 binary UUID to spawnSync / SpawnSyncEventLoop::tick_with_timeout.
  • The controlled follow-up changed only tests to ./tests. It passed 15,113 general tests and 194 serial tests, with zero failures, in under ten minutes. Both executions selected 748/1496 general files; all 173 file names through the original stopping point appear in identical order. The completed run then covered all 748 general files and four owned serial files.
  • After integration, focused workflow contracts passed 138 tests, 29 existing platform skips, zero failures; structure checks passed. The full prepush was rerun after the workflow edit and again exhausted its 900-second suite budget on Windows (exit 124), with unrelated Windows failures; it is not green.
  • At abdccbe59d, both hosted macOS shards finished without the silent hang. Shard 2 passed 13,958 general tests plus 44 serial tests. Shard 1 completed 14,523 general tests, zero assertion failures, but correctly exited 1 on one module-load error: the crash-classifier contract still searched for the old bare-root command. cb41ba3b4c corrects that overlooked anchor; its seven focused tests / 52 assertions pass.
  • Corrected shard 1 at exact head cb41ba3b4c (diagnostic run 35683370022, job 106604940668, contributor fork Actions) passed 14,528 general tests plus 194 serial tests, zero failures/errors. Shard 2 at abdccbe59d (diagnostic run 35682404784, job 106601967172, contributor fork Actions) is unchanged by the final one-line edit in the shard-1-owned classifier file; its 14,002 passes remain applicable. No tests were removed, no timeout was increased, and no failed attempt was converted to green by a workflow retry.

Evidence

  • Failing baseline capture (diagnostic run 35679762449, job 106593967562, contributor fork Actions) and passing controlled follow-up (diagnostic run 35681504245, job 106599252387, contributor fork Actions).
  • Pinned Bun synchronous wait and upstream directory-descriptor retention fix. The native capture proves the wait location; descriptor pressure is the source-supported trigger hypothesis, not a captured FD count.

Checklist

  • Scope stays focused and avoids unrelated cleanup.
  • Docs or release notes were updated when needed.
  • Security-sensitive changes were reviewed for secrets, auth, and unsafe defaults. Independent local review found no remaining blocking issue; explicit maintainer review is still pending.

@coderabbitai

coderabbitai Bot commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown
Contributor

✅ Deterministic PR hygiene checks passed.

@github-actions github-actions Bot added the chore Maintenance, CI, tests, refactors, or build changes (not a user-facing bug or feature). label Sep 22, 2026
@lidge-jun

Copy link
Copy Markdown
Owner

리뷰 · 우선순위 54 / 80

이 PR은 #4956에서 보이는 macOS CI 멈춤을 “고치는” 패치가 아닙니다. 호스트 macOS shard가 어떤 테스트를 통과한 뒤 출력을 멈추고 20분 벽에서 잘리는 문제를 나중에 볼 수 있게, 옆에 읽기 전용 관찰자만 붙입니다. run_macos_suite가 Bun 테스트를 돌리는 동안 scripts/ci/sample-macos-stall.sh를 백그라운드로 켜 두고, 로그가 약 60초 동안 안 늘면 소유 셸의 직계 Bun 자식이 하나인지 확인한 뒤, 그 자손 프로세스 이름만 찍고 sample로 스택을 한 번 뜹니다. argv·환경 변수는 안 찍고, 스위트에 시그널을 보내거나 재시도하지 않으며, 종료 코드도 원래 Bun 결과가 그대로입니다. 카탈로그 picker 픽스처에는 CI에서만 보이는 setup/writer/cleanup 단계 로그를 넣었고, observer 동작은 mock ps/sample/sleep 픽스처로 다섯 시나리오를 검증합니다. draft이고, 호스트 macOS에서 실제 스택 수집은 아직 안 했고, 작성자도 보안·스폰서 리뷰가 필요하다고 적어 두었습니다.

라인 - scripts/ci/sample-macos-stall.sh의 sample 출력은 argv를 빼도 스택 프레임·심볼 안에 시크릿·경로·요청 조각이 섞일 수 있습니다. 프로세스 목록은 comm만 쓰도록 잘 막았지만, 스택 덤프 쪽은 아직 메인테이너 보안 판단이 필요합니다.
라인 - 관찰자는 침묵을 한 번 잡은 뒤 샘플하고 바로 끝납니다. 같은 suite 안에서 잠깐 출력이 나왔다가 다시 멈추면 두 번째 멈춤은 안 잡힙니다. #4956처럼 “한 번 PASS 후 긴 침묵”이면 맞지만, 중간에 로그가 튀는 형태면 증거가 비일 수 있습니다.
라인 - 로컬·픽스처 검증은 있으나, 호스트 macOS runner에서 실제 sample·ps 경로가 기대한 증거를 남기는지는 PR 본문대로 아직 미검증입니다. 픽스처의 fake sample/ps만으로는 CI 실측을 대신할 수 없습니다.
라인 - PR이 draft이고 보안 체크리스트가 비어 있습니다. 진단용이라 런타임 제품 경로는 안 건드리지만, CI 로그에 네이티브 스택이 올라가는 변경이라 “준비됨” 표시 전에 한 번 더 보는 편이 안전합니다.

메인테이너의 판단이 필요한 지점

너의 추천

  • 방향은 맞고, 스위트 exit를 건드리지 않는 읽기 전용 진단 전제조건으로 잘 짜여 있습니다. 지금은 draft·보안·호스트 실측이 열려 있으니 바로 머지보다, 보안 리뷰와 호스트 macOS에서 샘플이 실제로 남는 한 run을 본 뒤 Ready로 올리는 쪽을 권합니다. types/config 중복 PR 이슈는 이 변경과 무관합니다.

이 댓글은 grok-bot이 작성했습니다

@lidge-jun

Copy link
Copy Markdown
Owner

discord 좀 들어와주세용

@luvs01 luvs01 changed the title ci: capture owned macOS suite stacks when progress stops ci: root macOS test discovery and diagnose silent process waits Sep 22, 2026
@luvs01
luvs01 requested a review from Ingwannu September 22, 2026 03:14
lidge-jun added a commit that referenced this pull request Sep 22, 2026
…#5509)

* chore(dev): retire automatic pre-push validation (#5500)

Carry #5500 and align both draft enforcement prompts with scoped validation. Clarify that inherited Git override isolation belongs to the fixture harness.

Co-authored-by: JUN <243035832+lidge-jun@users.noreply.github.com>

* ci: root macOS discovery and diagnose owned process waits (#5483)

Carry the rooted sharded suite and read-only stall observer without changing control-lane batching, suite budgets, or failure propagation.

Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>

* test: isolate Windows runtime discovery and reservation fixtures (#5484)

Carry #5484 while avoiding the unnecessary parent LOCALAPPDATA mutation. Preserve real SQLite contention, atomic publication, reservation assertions, and existing deadlines.

Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>

* fix(ci): bound translation email scanning (#5454)

Carry the at-sign-anchored sanitizer scan, explanatory rationale, and long-token regression without changing mention-defusing semantics.

Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>

* fix(ci): redact diagnostic paths and align localized command lists

Capture and redact sample output before bounded emission, retain diagnostic child ownership and regression fixtures, and add the missing test:changed command to six locale inventories. Local runtime validation remains NOT RUN by owner instruction.

* fix(ci): preserve legacy checklist bodies with durable re-attestation

Require author clear-and-retick saves bound to real head/base and persisted server-time checkpoints. Preserve pending state across events, invalidate stale evidence, and refresh live PR and saved proof before readiness. Keep body writes out of migration handling and cover mutation failures and sequential races.

Local runtime validation: NOT RUN by owner instruction. Independent static review passed; hosted verification and explicit maintainer security review remain pending.

* fix(ci): require saved attestation evidence before readiness promotion

The pre-ready readback compared the saved gate comment with the run's
expected state, but the gate mutated the listed comment object in place,
so a readback that shared it observed this run's own write. Replace the
object instead and require the saved state to be a finalized attestation
of the live head, base and body before any ready side effect.

A wording-only save that keeps its ticks now gets a specific action, so
the rejected shortcut is acknowledged in the gate comment instead of
being skipped as a byte-identical update.

Record the readback as a reviewed read, and move the self-contained
exhaustive-deps suppression block byte for byte into a sibling file to
keep ci-workflows.test.ts under its size cap.

Local runtime validation: NOT RUN by owner instruction; hosted CI verifies.

---------

Co-authored-by: JUN <243035832+lidge-jun@users.noreply.github.com>
Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>
@lidge-jun

Copy link
Copy Markdown
Owner

Thanks @luvs01. Part of this PR landed on dev through #5509 (squash commit 798c07c), with a Co-authored-by trailer crediting you.

What landed:

  • scripts/ci/sample-macos-stall.sh as a standalone diagnostic helper. The bundle also added prefix redaction and a byte cap to its emitted output.
  • The observer regression: tests/fixtures/macos-stall-observer.sh and its coverage in tests/ci-workflows/macos-serial-lanes.test.ts.
  • The CI-only phase boundaries in tests/codex-integration/catalog-full-picker-order.test.ts (unchanged from this branch).
  • The matching note in structure/ops/docs-and-release.md.

What did not land: the .github/workflows/ci.yml wiring that started the observer inside run_macos_suite, and the ./tests discovery root for the macOS shard invocation, along with the two assertion updates that pinned them. Current dev replaced that long-lived shard function with bounded fresh-process batches, so neither change applies to the current workflow, and the helper is intentionally left unwired.

Wiring stall observation into the batch runner is still open. It would need a new change against the current macOS batch steps, so this PR stays open for that discussion.

@lidge-jun

Copy link
Copy Markdown
Owner

Thank you, @luvs01. This PR is superseded by the lane bundle #5597, and its content is already on dev:

#5597 records this disposition (ALREADY ON DEV). Closing as superseded.

@lidge-jun lidge-jun closed this Sep 22, 2026
lidge-jun added a commit that referenced this pull request Sep 23, 2026
…down (#5483, #5549) (#5597)

* test(ci): move the PR trigger contract into a review-lanes test file

Move "PR checks reach every branch the target gate accepts" byte for byte
out of ci-workflows.test.ts, which sits near its file-size cap, into a
registered sibling. The following review-gate commits extend that contract
there instead of growing the capped file.

Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>

* fix(ci): treat private and generator members as code; match stacked parents by head repo

Comment-only detection treated any changed line starting with "#" or "*"
as a comment, so a src/ change adding a #private member or a *generator
method skipped the regression-test requirement. Track block-comment state
per diff line instead; anything ambiguous still counts as code.

The wrong-base gate exempted a PR as stacked when any open PR's head ref
matched its base ref, comparing that PR's BASE repository, which is always
this one. A fork PR from its own "main" therefore exempted every PR aimed
at main. Match the open PR's head repository against the base repository
and fail closed when it is unavailable.

Carries #5311 (via #5549).

Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>

* ci: run the expensive suite for any repository workflow or script change

The push trigger and the pull-request scope filter listed four workflow
files by name, so a change to any other workflow or to the gate scripts
under .github/scripts skipped the suite that exercises them. Replace the
four entries with .github/workflows/** and .github/scripts/** on both
paths, and pin the new list in the review-lanes contract.

Carries #5297 (via #5549).

Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>

* ci: add a manual release-gates lane that leaves diagnostic suites unrequested

A manual dispatch always requested the unsharded macOS control, and the
default lane also requested nine Windows suite shards, so collecting
ordinary gate evidence by hand also paid for both diagnostics. Add
lane=release-gates: the ordinary jobs keep their usual selection, while
macos-control now runs only for an explicit allowlist (empty, all,
macos-control) so an unknown future lane skips it. The aggregate mirrors
that allowlist for its dispatch expectation.

The review-lanes contract evaluates every job condition across events and
lanes, and executes the checked-in aggregate step to prove release-gates
accepts the diagnostic skips but still rejects a failed, cancelled or
missing requested producer. A manual lane does not change release
eligibility, which still requires push-event CI on the exact SHA.

Carries #4597 and its aggregate follow-up (via #5549).

Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>
Co-authored-by: JUN <bitkyc08@gmail.com>

* test: drain sandbox producers and ACL reaps before removing the preload root

The preload removed its isolated root in afterAll and again from a
synchronous exit hook, while native-main startup releases, config
hardening and Windows ACL child reaps could still be running against it.
Resolve those owners during protected setup, then have teardown drain the
producers and wait for the registered reaps before removing the root. The
exit fallback only retries an already drained root and otherwise leaves it
for ownership-checked stale recovery. A delayed-reap regression pins the
order, and a case lifecycle helper gives fixtures one shared stop/close.

The three files and the structure paragraph are byte-identical to the
copies carried by #5562, so either landing order merges cleanly.

Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>

* test: absorb only the case lifecycle's own abort reason during teardown

After close() began, createTestCaseLifecycle().run() swallowed any error
named AbortError, so an unrelated cancellation inside a case could read as
success. Absorb only abort.signal.reason, which signal listeners and an
aborted fetch reject with, and pin that a foreign AbortError still rejects.

This diverges from the helper copy in #5562 by this one condition; that
lane should take this version when it rebases.

---------

Co-authored-by: luvs01 <27862058+luvs01@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

chore Maintenance, CI, tests, refactors, or build changes (not a user-facing bug or feature).

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants