diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6eba4e8c1..ef102ebb6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,10 +18,11 @@ on: default: true concurrency: - # Only the newest next push needs evidence. Main pushes and explicit release - # qualification keep unique groups; a pending main run must never be replaced. + # Let the active next run finish, including jobs waiting for macOS capacity. + # New next pushes replace only the pending run. PRs still cancel stale runs; + # main pushes and explicit release qualification keep unique groups. group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || (github.event_name == 'push' && github.ref == 'refs/heads/next' && 'push-next') || format('push-{0}', github.run_id) }} - cancel-in-progress: ${{ github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref == 'refs/heads/next') }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} jobs: changes: diff --git a/docs/reference/ci-test-selection.md b/docs/reference/ci-test-selection.md index d821b9875..c0dceaa59 100644 --- a/docs/reference/ci-test-selection.md +++ b/docs/reference/ci-test-selection.md @@ -109,9 +109,16 @@ prerelease gate requires that lane explicitly. macOS ARM qualification remains separate from candidate tests because their repeated setup is small compared with the compiler's transport and bundle checks. -`next` pushes share a cancelling concurrency group; each new push cancels its -predecessor. `main` pushes retain unique, non-cancelling groups. Manual full runs -have unique groups and cannot be cancelled by a later `next` push. +`next` pushes share a non-cancelling concurrency group: the active run finishes, +including jobs waiting for macOS capacity. GitHub retains at most one pending run; +a newer push replaces that pending run, so intermediate revisions may not run. +As described above, a cancelled or missing predecessor forces full selection, +so replacing a pending run cannot omit its merged changes from coverage. +This preserves all selected macOS coverage without changing runner capacity or +the PR layout. It does not reserve macOS slots or guarantee a queue wait bound. +PRs still cancel stale runs for the same PR. `main` pushes retain unique, +non-cancelling groups. Manual full runs have unique groups and cannot be cancelled +by a later `next` push. ## Historical replay diff --git a/docs/reference/v5-model-coverage.md b/docs/reference/v5-model-coverage.md index 1dd7bf9d5..60b1e212f 100644 --- a/docs/reference/v5-model-coverage.md +++ b/docs/reference/v5-model-coverage.md @@ -36,7 +36,7 @@ must be attached to the eventual exact integration revision. | Property and critical states | Model coverage | Implementation/regression boundary | Remaining qualification | | --- | --- | --- | --- | -| CI cancellation must not lose merged-change coverage; only exact-head full evidence may qualify a prerelease | No maintained GitHub scheduler model. Finite fixture interleavings cover predecessor success/failure/cancellation, two competing full/scoped runs and two rerun attempts | `ci.yml` shares only the next-push cancellation group and requires a successful before run for selection. `selectPrereleaseCiRun` reads current attempts; `requireBranchChecks` binds suite and job check IDs. `ci-trusted-policy`, `prerelease-plan` and `release-prepare-gate` regressions reject cancelled predecessors, green subsets, stale checks, skipped hosts and changed attempts | API snapshots and GitHub group/event semantics are assumptions; races after the last read remain possible. Publication rechecks the frozen protected head and full evidence but does not transactionally lock GitHub. Hosted dispatch/cancellation checks qualify wiring; no scheduler liveness or formal refinement claim. | +| CI cancellation must not lose merged-change coverage; only exact-head full evidence may qualify a prerelease | No maintained GitHub scheduler model. Finite fixture interleavings cover predecessor success/failure/cancellation, two competing full/scoped runs and two rerun attempts | `ci.yml` serializes next pushes in one non-cancelling group (GitHub cancels only the replaced pending run), cancels stale PR runs, and requires a successful before run for selection. `selectPrereleaseCiRun` reads current attempts; `requireBranchChecks` binds suite and job check IDs. `ci-trusted-policy`, `prerelease-plan` and `release-prepare-gate` regressions reject cancelled predecessors, green subsets, stale checks, skipped hosts and changed attempts | API snapshots and GitHub group/event semantics are assumptions; races after the last read remain possible. Publication rechecks the frozen protected head and full evidence but does not transactionally lock GitHub. Hosted dispatch/cancellation checks qualify wiring; no scheduler liveness or formal refinement claim. | | Native log follower versus lifecycle mutation, frontend loss, stopped container and transport loss | No new state model: the follower observes without a mutation lease and grants no cleanup authority | `provider/graph/log_follow.rs` and its native counterpart pin Ready admission, container and start generation; `provider/engine/log_follow.rs` bounds framing and distinguishes real EOF from stopped-service idle cutoff. `lifecycle_log_follow_tests.rs` keeps the production transport on a fake Engine socket active while acquiring startup and operation leases, with a same-lock contention control and output while each lease is held. `native-log-follow-source.test.ts` guards the observer-only route; process tests cover cancellation, frontend loss, real output-pipe closure while idle, replacement-container restart (including retained adopted run/owner IDs) with one marker, NDJSON admission failures, truncated/partial output and slow idle-end delivery. `native-log-restart.test.ts` covers bounded re-admission, unchanged-container refusal and prompt down; `native-log-selection.test.ts` covers strict finite admission responses and owned-child cancellation/timeouts; advisory markers grant no authority | 4 KiB payload envelopes, 8 KiB wire lines, 64 Ki-character retained payload prefixes excluding Engine timestamps, 30-second restart wait, 1000-line new-container backlog. The portable lease test fakes identity/state checks; it does not exercise host process/disk auditing, graph admission or the VM relay. Real relay EOF on stop/down/restart, VM loss and concurrent lifecycle latency require Apple Silicon acceptance. No performance or whole-product parity claim. | | Foreground group interrupt; publisher exit before authenticated stop; dead owner's pending cleanup enrollment and bridge release | native-publisher-stop | [Process adapter](../../src/backends/native-project-process.ts) isolates Compose owners regardless of hooks; Rust `process::publisher_command` isolates publishers; `legacy_cleanup_enrollment` binds the old coordinator and inventories before a distinct recovery intent. [Group regression](../../tests/native-foreground-group-interrupt.test.ts) signals the whole group for fresh/restore owners; Rust real-publisher process, frontend-loss, exact child-reaping and enrollment tests check grouping, lifetime wakeups and stale selectors. | One graph, one publisher, one crash, one identity replacement and one audited successor boot; 168 states. Publisher exit observation is separate from lease-bound reaping/proof commitment; `NoUncommittedExit` rejects owner cleanup while proof is pending. A busy lease stutters without delaying stop control. SIGHUP requests retaining cleanup; a reaped publisher enters an observable publisher-down state until explicit stop. Native process tests and frontend framing/ps tests cover signal delivery and notices. Dependency release is a separate post-completion step; `NoStrandedReservation` rejects success with a held reservation. Real reservation-file regressions check retry, capacity reuse and refusal fences. Separate stopped-receipt and intent-completion commits are collapsed; TypeScript dispatch and Rust completion-digest regressions cover that interruption window. Publication-registry sync/rename is abstracted and covered by portable commit-interruption tests. Kernel identity, socket/inode proofs, guest observations, durable storage and existing next.13 home recovery remain implementation/Apple Silicon VM gates. No eventual recovery is asserted. | | Exclusive claims; competing writers; partial owner publication | graph-admission, registry-writer, publication-journal, missing-publication-lock, native-frontend-recovery | Admission/registry locks, pending publication and exact saved owner; related runtime ownership tests | Real filesystems, boot/incarnation identity and unsupported writers remain independent. | diff --git a/tests/ci-trusted-policy.test.ts b/tests/ci-trusted-policy.test.ts index c2197b2b2..b9a36bb06 100644 --- a/tests/ci-trusted-policy.test.ts +++ b/tests/ci-trusted-policy.test.ts @@ -315,12 +315,12 @@ test("retargeting reclassifies through the ordinary pull_request event", () => { expect(workflow.on.pull_request.types).toContain("edited"); }); -test("only next pushes share a cancelling group; main and dispatch remain unique", () => { +test("next pushes serialize without cancelling active runs; only PRs cancel", () => { expect(workflow.concurrency.group).toBe( "${{ github.workflow }}-${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.pull_request.number) || (github.event_name == 'push' && github.ref == 'refs/heads/next' && 'push-next') || format('push-{0}', github.run_id) }}" ); expect(workflow.concurrency["cancel-in-progress"]).toBe( - "${{ github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref == 'refs/heads/next') }}" + "${{ github.event_name == 'pull_request' }}" ); });