Skip to content

ci: shard the dev lane on Linux and move Windows to the shipping boundary - #899

Open
lidge-jun wants to merge 5 commits into
devfrom
codex/260803-ci-dev-shard
Open

ci: shard the dev lane on Linux and move Windows to the shipping boundary#899
lidge-jun wants to merge 5 commits into
devfrom
codex/260803-ci-dev-shard

Conversation

@lidge-jun

@lidge-jun lidge-jun commented Aug 2, 2026

Copy link
Copy Markdown
Owner

What this changes

The dev/PR lane becomes a Linux lane, split four ways. Windows moves to the
shipping boundary. macOS keeps running the whole suite.

  • The suite runs as four bun test --shard=i/4 jobs on ubuntu-latest.
  • Everything that is not the suite — typecheck, privacy scan, GUI tests, lint,
    build, release-helper syntax, CLI smoke — moves into one gates job instead
    of being repeated per platform.
  • platform-macos runs the whole suite, unsharded, on every pull request.
  • platform-windows runs on promotion to main/preview and on
    workflow_dispatch, not on pull requests.
  • A new ci job aggregates every other job into one stable check name.
  • The GUI lint/build steps and the npm-global-smoke jobs run only when their
    own inputs changed.

Why

Measured on run 30748690567,
a fully green run of #880:

leg wall clock
ubuntu 5m 49s
macos 5m 23s
windows 16m 23s

The Windows leg decided when every pull request turned green. The comment
above its timeout-minutes records that ceiling being raised twice —
after #711 passed at 11.8min while #653 was killed at 12.0min (#717), then
again after #827 hit the 20-minute kill — rather than the gap being closed.

This is not a cost argument. Standard runners are free on public
repositories, so no minutes are saved. What changes is feedback latency and
the concurrency budget (20 concurrent jobs on the Free plan, 5 for macOS)
against roughly twenty open pull requests.

The cost, stated plainly

A Windows-only regression can now land on dev and be caught at promotion
instead of at review time. That is a real reduction in when Windows is
verified, and it is the price of the change.

What keeps it acceptable:

  • dev is not published. main and preview are, and release.yml already
    refuses to publish a commit without a successful run of this workflow — so
    the Windows leg still gates everything that ships.
  • The Windows npm-global-smoke leg (1m 53s) still runs on any change to a
    packaged input, which includes src/**. An ordinary source PR keeps a
    Windows signal.

If Windows-only regressions turn out to be frequent on dev, the right
response is to put Windows back on the PR lane sharded, not to defend this
arrangement.

Concurrency: this trades more jobs for a shorter path

jobs per run peak simultaneous
before 7 6
non-packaging PR 9 7
source or packaging PR 12 up to 10
promotion 13 up to 10

"Faster CI" usually implies "less CI"; here it does not, and the peak goes up.

Required checks

dev has no branch protection configured today, so nothing needs re-pointing.
If it is enabled later, ci is the check to require — shard names change
with the shard count and platform legs come and go by trigger, so neither is
stable.

⚠️ Requiring ci also means removing the workflow-level paths: filter, or
moving the gate to an always-triggered workflow. A docs-only PR does not
trigger this workflow, so no ci check would be created and the PR would sit
pending forever. Harmless now, a trap afterwards.

Two details worth a reviewer's attention

base: ${{ github.ref }} on the filter step. Left unset, paths-filter
compares against the repository default branch (main), so a push to dev
would be diffed against main and every area touched since the last promotion
would keep reading as changed. The scoped jobs would run anyway, the saving
would not happen, and nothing would look wrong.

The gate treats skipped as a pass. That is required, not sloppy: a
trigger-scoped or area-filtered job reports skipped, and GitHub reports
skipped jobs as success regardless. The gate uses an allowlist (success or
skipped pass, everything else fails) so a result type added later cannot slip
through, and if: always() keeps the gate from being skipped along with a
failed dependency and reporting success by omission.

Security review requested

Per MAINTAINERS.md, GitHub Actions changes require explicit security review.
Specific points:

  • dorny/paths-filter is a new third-party dependency, pinned to the full
    commit SHA de90cc6f (verified to equal tag v3.0.2). A no-dependency
    fallback using git diff --name-only is documented in the devlog if the
    dependency is unwanted.
  • The changes job takes pull-requests: read, a permission this workflow did
    not previously hold. It is scoped to that one job; the action cannot read a
    PR's file list without it.
  • select-windows-runner and its security commentary are unchanged. The
    self-hosted routing, and the note that it is a cost control rather than a
    security boundary, carry forward verbatim.
  • No job gains contents: write. No secret is newly exposed.

Verification

  • actionlint: clean.
  • bun x tsc --noEmit: exit 0.
  • bun run test: 6960 pass, 8 skip, 0 fail, 33933 assertions across 480 files.
  • bun run privacy:scan: passed.
  • Shard tiling proven on this suite: 120 files per shard, union 480, no file
    in two shards
    , none missed.
  • tests/ci-workflows.test.ts: 66 pass, 733 assertions (692 before).

Every new pin was driven red before being kept — shard/divisor drift, a job
missing from the gate's needs, a deleted base:, a dropped
pull-requests: read, and a path present in one trigger list but not the other
each fail the suite.

Planning record

devlog/_plan/260803_ci_dev_lane_sharding/ carries the diff-level plan and the
evidence. It includes the audit trail: an independent review ran four rounds
and failed the plan three times. Every blocker was a local fact asserted
without being checked — tests said not to need React that import JSX, a phase
boundary that would have deleted two platforms in a commit claiming otherwise,
a duplicated YAML key, and the base: default above. The corrections are
recorded rather than tidied away.

Summary by CodeRabbit

  • CI Improvements
    • Faster Linux validation through four parallel test shards.
    • macOS continues running the complete test suite as a cross-platform check.
    • Windows checks now run during promotions or manual dispatches.
    • GUI and packaging checks run only when relevant files change.
    • CI triggers now include relevant assets and project documentation changes.
    • Added a final status gate for a consistent overall CI result.
  • Documentation
    • Added planning and verification documentation for the updated CI workflow.

@github-actions github-actions Bot added the chore Maintenance, CI, tests, refactors, or build changes (not a user-facing bug or feature). label Aug 2, 2026
@coderabbitai

coderabbitai Bot commented Aug 2, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@lidge-jun, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 5 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: ed38a084-b21d-408c-92a9-e6dea3ba54a7

📥 Commits

Reviewing files that changed from the base of the PR and between 32c56a8 and fd5ca50.

📒 Files selected for processing (7)
  • .github/workflows/ci.yml
  • .github/workflows/release.yml
  • devlog/_plan/260803_ci_dev_lane_sharding/002_audit_synthesis.md
  • devlog/_plan/260803_ci_dev_lane_sharding/010_linux_shard_matrix.md
  • devlog/_plan/260803_ci_dev_lane_sharding/011_platform_legs.md
  • devlog/_plan/260803_ci_dev_lane_sharding/040_ship_and_review.md
  • tests/ci-workflows.test.ts
📝 Walkthrough

Walkthrough

CI now runs four Linux test shards, centralized checks, unconditional macOS validation, conditional Windows validation, affected-path GUI and packaging checks, and a final aggregate gate. Workflow tests and development plans define the job structure, filters, timeouts, dependencies, and validation criteria.

Changes

CI development lane

Layer / File(s) Summary
Linux shards and aggregate gates
.github/workflows/ci.yml, devlog/_plan/260803_ci_dev_lane_sharding/*, tests/ci-workflows.test.ts
Ubuntu tests run in four deterministic shards. Shared checks run once in gates. The ci job accepts only successful or intentional skipped dependencies. Tests verify timeouts, shard configuration, dependencies, and aggregate behavior.
Platform-specific validation
.github/workflows/ci.yml, devlog/_plan/260803_ci_dev_lane_sharding/011_platform_legs.md, tests/ci-workflows.test.ts
macOS runs the unsharded full suite on every trigger. Windows runs on manual dispatches and pushes to main or preview, with workspace cleanup and CLI smoke coverage retained.
Affected-path filtering
.github/workflows/ci.yml, devlog/_plan/260803_ci_dev_lane_sharding/030_affected_scoping.md, tests/ci-workflows.test.ts
Pull-request and push filters include assets/**, README.md, and LICENSE. The changes job exposes GUI and packaging outputs. Conditional GUI and packaging checks use those outputs.
CI planning and review records
devlog/_plan/260803_ci_dev_lane_sharding/*
The development plans record shard evidence, workflow audits, implementation phases, scope boundaries, and ship-and-review validation requirements.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant PullRequestOrPush
  participant Changes
  participant LinuxShards
  participant Gates
  participant PlatformJobs
  participant CI
  PullRequestOrPush->>Changes: Detect GUI and packaging changes
  PullRequestOrPush->>LinuxShards: Start four Linux test shards
  PullRequestOrPush->>Gates: Run shared checks
  PullRequestOrPush->>PlatformJobs: Run macOS, or eligible Windows, validation
  Changes->>Gates: Control GUI checks
  Changes->>PlatformJobs: Control packaging smoke checks
  LinuxShards->>CI: Report shard results
  Gates->>CI: Report shared-check results
  PlatformJobs->>CI: Report platform results
  CI->>CI: Accept success or intentional skip only
Loading

Possibly related PRs

Suggested reviewers: ingwannu, wibias

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main CI changes: Linux sharding and moving Windows validation to the shipping boundary.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/260803-ci-dev-shard

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/ci.yml:
- Around line 143-144: Add persist-credentials: false to every checkout step in
.github/workflows/ci.yml at line ranges 143-144, 202-203, 233-234, 290-291,
355-356, and 390-391, ensuring none persist the GITHUB_TOKEN in local Git
configuration.

In `@devlog/_plan/260803_ci_dev_lane_sharding/000_plan.md`:
- Line 3: Replace the future-dated audit entries with the actual August 2, 2026
date: update the plan-open date in
devlog/_plan/260803_ci_dev_lane_sharding/000_plan.md lines 3-3, the
evidence-collection date in
devlog/_plan/260803_ci_dev_lane_sharding/001_shard_evidence.md lines 3-5, and
the audit date in
devlog/_plan/260803_ci_dev_lane_sharding/002_audit_synthesis.md lines 5-6.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 0095ce35-ad1a-457c-81f8-8e7f4029cb61

📥 Commits

Reviewing files that changed from the base of the PR and between 68fe94e and d535636.

📒 Files selected for processing (9)
  • .github/workflows/ci.yml
  • devlog/_plan/260803_ci_dev_lane_sharding/000_plan.md
  • devlog/_plan/260803_ci_dev_lane_sharding/001_shard_evidence.md
  • devlog/_plan/260803_ci_dev_lane_sharding/002_audit_synthesis.md
  • devlog/_plan/260803_ci_dev_lane_sharding/010_linux_shard_matrix.md
  • devlog/_plan/260803_ci_dev_lane_sharding/011_platform_legs.md
  • devlog/_plan/260803_ci_dev_lane_sharding/030_affected_scoping.md
  • devlog/_plan/260803_ci_dev_lane_sharding/040_ship_and_review.md
  • tests/ci-workflows.test.ts

Comment thread .github/workflows/ci.yml
@@ -0,0 +1,230 @@
# dev CI lane: shard on Linux, move Windows off the PR path

Opened 2026-08-03 against `dev@f9b9440c551e3d7f3e2041098caa2ee4de57698e` (v2.10.0).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Correct the future-dated audit records.

The current date is August 2, 2026. These records state that work occurred on August 3, 2026. Future-dated plans, evidence, and audit results undermine the traceability that these documents provide.

  • devlog/_plan/260803_ci_dev_lane_sharding/000_plan.md#L3-L3: Use the actual plan-open date.
  • devlog/_plan/260803_ci_dev_lane_sharding/001_shard_evidence.md#L3-L5: Use the actual evidence-collection date.
  • devlog/_plan/260803_ci_dev_lane_sharding/002_audit_synthesis.md#L5-L6: Use the actual audit date.
📍 Affects 3 files
  • devlog/_plan/260803_ci_dev_lane_sharding/000_plan.md#L3-L3 (this comment)
  • devlog/_plan/260803_ci_dev_lane_sharding/001_shard_evidence.md#L3-L5
  • devlog/_plan/260803_ci_dev_lane_sharding/002_audit_synthesis.md#L5-L6
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@devlog/_plan/260803_ci_dev_lane_sharding/000_plan.md` at line 3, Replace the
future-dated audit entries with the actual August 2, 2026 date: update the
plan-open date in devlog/_plan/260803_ci_dev_lane_sharding/000_plan.md lines
3-3, the evidence-collection date in
devlog/_plan/260803_ci_dev_lane_sharding/001_shard_evidence.md lines 3-5, and
the audit date in
devlog/_plan/260803_ci_dev_lane_sharding/002_audit_synthesis.md lines 5-6.

…flow

The Windows leg decides when a pull request turns green. On the last
all-green run the suite took 5m49s on ubuntu, 5m23s on macos, and 16m23s
on windows, so a contributor waits roughly three times longer than the
verdict actually took to produce. The 30-minute ceiling on that job, and
the two increases before it, are the same problem being paid for rather
than fixed.

This unit plans the change: four Linux shards for the suite, one job for
the platform-independent gates, macOS kept whole and unsharded as the
control that would notice if the shards stopped being independent, and
Windows moved to promotion and dispatch where a maintainer consumes it.
One aggregate check named `ci` covers whatever the job graph happens to
be, so shard counts and platform legs can move without a required check
name moving with them.

No workflow is edited here. The docs carry the diff-level specification
and the evidence behind it: shard tiling proven on this suite (four
shards, 120 files each, union 480, no overlap), the runner and
required-check behavior quoted from the official documentation, and the
measured baseline the result will be compared against.

An independent review ran four rounds against this plan and failed it
three times. Every blocker was a local fact asserted without being
checked: tests said not to need React that import JSX, a phase boundary
that would have deleted two platforms in a commit claiming it did not, a
duplicated YAML key, and a changed-files action left on its default base
that would have diffed dev against main and saved nothing. The rounds
and their dispositions are recorded with the plan, since the corrections
are more useful to the next person than a clean-looking document.
One matrix job ran the whole gate on three platforms, and the Windows leg
took 16m23s against ubuntu's 5m49s and macos's 5m23s on the last all-green
run. It decided when every pull request turned green. The comment above
its timeout records the ceiling being raised twice rather than the gap
being closed, most recently to 30 minutes.

The suite now runs as four Linux shards. `bun test --shard=i/4` sorts
files by path and deals them round-robin, so the split is deterministic
and the four shards cover the suite exactly once: 120 files each, union
480, no file in two shards. Everything that is not the suite — typecheck,
privacy scan, GUI tests, lint, build, release-helper syntax, CLI smoke —
moves to a single `gates` job instead of being repeated per platform.

macOS keeps running the whole suite, unsharded. That is deliberate: the
shards each cover a quarter of the files, which assumes no test depends on
a sibling having run in the same pool, and this leg is the control that
would notice if that assumption broke. It is also the fastest leg on the
board, so there was never a reason to trim it.

Windows moves to promotion and dispatch. Its coverage is not dropped —
release.yml already refuses to publish a commit without a successful run
of this workflow, so main and preview still gate on it, and the Windows
npm-global smoke still runs on every push. The trade is that a
Windows-only regression can now reach dev and be caught at promotion
rather than at review. dev is not published; that is what makes the trade
acceptable rather than free.

A new `ci` job is the one check that means "CI passed". Shard names move
with the shard count and the platform legs come and go by trigger, so
neither is a stable thing to require in branch protection. It depends on
every other job and asserts each result with an allowlist: success and
skipped pass, anything else fails, so a result type added later cannot
slip through. `if: always()` is what keeps it from being skipped along
with a failed dependency and reporting success by omission.

The suite's workflow pins move with it. The exact `timeout-minutes:`
occurrence count is replaced by iterating the parsed jobs, which is what
its comment always claimed to check; the shard matrix and the `--shard`
divisor are pinned to each other, because drift between them would stop
running files while CI stayed green; and the gate's `needs` is compared
against the workflow's own job list rather than a hardcoded one, so
adding a job without gating it fails immediately. Both new pins were
driven red before being kept.
The sharded lane still paid for three npm-global smoke jobs and a GUI
lint and build on every run, including pushes that touched neither. This
scopes them to the areas that can actually break them.

The filtering is per job, never on the workflow trigger. A workflow
skipped by path filtering leaves its checks Pending forever, so a pull
request requiring them can never merge, while a job skipped by a
condition reports success. That asymmetry is why the coarse `paths:`
list in the `on:` block stays coarse and a `changes` job does the real
work.

`base: ${{ github.ref }}` is doing more than it looks. Left unset, the
action compares against the repository default branch, which is `main` —
so a push to `dev` would be diffed against `main` and every area touched
since the last promotion would keep reading as changed. The scoped jobs
would run on nearly every push, the saving would not happen, and nothing
would look wrong.

The packaging filter covers `src/**` deliberately. package.json ships
`src` and bin/ocx.mjs executes it, so a source change is a packaging
change; without that entry an ordinary source pull request would get no
Windows verification at all now that the Windows suite runs only at the
shipping boundary. That keeps the 1m53s Windows smoke on the common path
while the 16-minute suite stays off it.

Three tarball inputs — assets, README, LICENSE — were named by the
filter but absent from the trigger's own path list, which meant a change
to one of them never started this workflow and the filter never got to
see it. The trigger list grows from 14 entries to 17 to match, on both
push and pull_request, since checking one and not the other is how a
change lands on dev having been verified on the wrong trigger.

The pins move with the behavior. The GUI lint and build assertions
became substring checks that a permanently-false condition would still
satisfy, so they now assert the condition itself: a gate that cannot
fire is dropped in every sense that matters, which is the outcome PR #97
already hit once by a different route. The packaging filter is compared
as a whole list rather than sampled, and every pattern in it is checked
against the trigger's path list so an unreachable entry fails the suite.
Each new pin was driven red before being kept.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d535636a3c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/ci.yml
Comment on lines +330 to +333
if: >-
github.event_name == 'workflow_dispatch' ||
(github.event_name == 'push' &&
(github.ref == 'refs/heads/main' || github.ref == 'refs/heads/preview'))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Require shipping-boundary CI before publishing

When a commit already has a successful PR or dev-push CI run, a release can be dispatched immediately after that SHA is promoted to main or preview; .github/workflows/release.yml lines 140-147 accepts any successful ci.yml run for the SHA without constraining its event or branch. Because this condition skips platform-windows in those earlier runs, publishing can proceed while the new promotion run—and its Windows coverage—is still pending or even fails. Make the release gate select the successful main/preview promotion run or explicitly verify that its Windows job succeeded.

AGENTS.md reference: .github/AGENTS.md:L17-L18

Useful? React with 👍 / 👎.

Comment thread .github/workflows/ci.yml
Comment on lines +168 to +172
packaging:
- 'package.json'
- 'bun.lock'
- 'src/**'
- 'bin/**'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Include checkout attributes in the packaging filter

A PR that changes only .gitattributes still triggers this workflow, but this filter leaves packaging false and skips all three npm-global-smoke jobs. Because .gitattributes controls how tracked package inputs such as bin/ocx.mjs are materialized on each runner, an attribute change can introduce CRLF shebangs or other checkout transformations into the tarball without exercising npm pack, global installation, or ocx help on any platform. Add .gitattributes to the packaging filter and its asserted allowlist.

AGENTS.md reference: .github/AGENTS.md:L18-L18

Useful? React with 👍 / 👎.

The first sharded run went red on shard 1/4 and macos, and the failure
was mine rather than flaky: tests that fetch the served dashboard read
their session bootstrap out of gui/dist/index.html, and nothing in those
jobs built it.

The old three-platform job satisfied this by accident. It ran the suite
and the GUI build in the same job, so gui/dist always existed by the time
the tests ran. Splitting the suite away from the gates removed the
coincidence without removing the dependency, and area-scoping the gates'
build behind `gui/**` meant even that copy was conditional.

Reproduced on an untouched checkout of dev to be sure the split was the
cause: the same test fails there with no gui/dist present and passes
after `bun run build`. So the dependency is real and pre-existing; only
its accidental satisfaction disappeared.

The suite pin that would have caught this now exists: every job running
the root suite must have an unconditional GUI build step. It was driven
red before being kept.
@lidge-jun
lidge-jun force-pushed the codex/260803-ci-dev-shard branch from d535636 to 32c56a8 Compare August 2, 2026 17:47

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/ci.yml:
- Around line 367-370: Update the “Clean workspace (self-hosted only)” step so
git clean failures are not suppressed for existing worktrees. Preserve tolerance
only when no repository exists initially, but propagate any cleanup failure
after a repository is present so the workflow stops instead of continuing with
stale content.

In `@devlog/_plan/260803_ci_dev_lane_sharding/010_linux_shard_matrix.md`:
- Around line 98-105: Update all documented root-suite jobs to build the GUI in
their own workspace before tests: in
devlog/_plan/260803_ci_dev_lane_sharding/010_linux_shard_matrix.md lines 98-105,
add an unconditional Build GUI step after dependency installation and before
shard tests; in devlog/_plan/260803_ci_dev_lane_sharding/011_platform_legs.md
lines 105-110, remove the claim that gates supplies the build and add the
unconditional step before macOS tests; and at lines 152-159, add the same step
before Windows tests.

In `@devlog/_plan/260803_ci_dev_lane_sharding/040_ship_and_review.md`:
- Around line 11-20: Update the shell command block in “Run in this order” to
enable fail-fast behavior by adding “set -euo pipefail” before the listed
commands, preserving the existing sequence and prose.
- Around line 45-53: Update the two public-artifact scans in the ship-and-review
instructions so the diff scan includes devlog alongside .github and tests, and
both scans use the same declared sensitive patterns: /Users/, worktree, macmini,
and session. Keep the existing clean-result behavior unchanged.
- Around line 29-37: Replace the shard-validation loop in the documented plan
with a method that does not rely on unmatched test-name output from Bun. Use a
supported file-list/dry-run interface or reproduce Bun’s documented sharding
algorithm from the sorted test-file list, then validate both that no test file
appears in multiple shards and that the union of shard assignments equals all
files found by find tests -name '*.test.ts'.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 05c42f3a-7d70-4a08-ab6e-a845dbac0343

📥 Commits

Reviewing files that changed from the base of the PR and between d535636 and 32c56a8.

📒 Files selected for processing (9)
  • .github/workflows/ci.yml
  • devlog/_plan/260803_ci_dev_lane_sharding/000_plan.md
  • devlog/_plan/260803_ci_dev_lane_sharding/001_shard_evidence.md
  • devlog/_plan/260803_ci_dev_lane_sharding/002_audit_synthesis.md
  • devlog/_plan/260803_ci_dev_lane_sharding/010_linux_shard_matrix.md
  • devlog/_plan/260803_ci_dev_lane_sharding/011_platform_legs.md
  • devlog/_plan/260803_ci_dev_lane_sharding/030_affected_scoping.md
  • devlog/_plan/260803_ci_dev_lane_sharding/040_ship_and_review.md
  • tests/ci-workflows.test.ts

Comment thread .github/workflows/ci.yml Outdated
Comment thread devlog/_plan/260803_ci_dev_lane_sharding/010_linux_shard_matrix.md
Comment thread devlog/_plan/260803_ci_dev_lane_sharding/040_ship_and_review.md
Comment on lines +29 to +37
```bash
for s in 1 2 3 4; do
bun test --shard=$s/4 --test-name-pattern '____never_match____' 2>&1 \
| grep -oE '^tests/[^:]+\.test\.ts' | sort -u
done | sort | uniq -d
```

Empty output means no file is in two shards. Compare the union count against
`find tests -name '*.test.ts' | wc -l` for the other direction.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

set -eu
printf '%s\n' '--- target file ---'
cat -n devlog/_plan/260803_ci_dev_lane_sharding/040_ship_and_review.md | sed -n '20,45p'
printf '%s\n' '--- Bun/version references ---'
rg -n -i 'bun|shard|test-name-pattern|never_match' \
  --glob '!node_modules/**' \
  --glob '!dist/**' \
  --glob '!build/**' \
  . | head -250
printf '%s\n' '--- version files ---'
git ls-files | rg '(^|/)(package\.json|bun\.lockb?|\.bun-version|mise\.toml|package-lock\.json|yarn\.lock|pnpm-lock\.yaml|Dockerfile|.*\.yml|.*\.yaml)$' | head -100

Repository: lidge-jun/opencodex

Length of output: 28242


🌐 Web query:

Bun 1.3.14 bun test --shard --test-name-pattern no matching tests prints test file paths output behavior

💡 Result:

In Bun 1.3.14, when using bun test with both --shard and --test-name-pattern, the execution behavior is designed to filter the test suite while respecting sharding logic [1][2]. Specifically, the sharding mechanism in Bun (introduced to split test files across CI runners) operates by partitioning the list of discovered test files [1][2]. The filtering process works as follows: 1. Sharding is applied to the pool of test files first. Discovered test files are sorted by path, and then distributed using a round-robin assignment based on the provided shard index (e.g., --shard=M/N) [2]. 2. The --test-name-pattern (or -t) flag filters individual tests within those selected files by matching test names (prefixed by describe blocks) against a regex pattern [3][4][5]. Regarding "no matching tests": If the --test-name-pattern filter results in zero tests being matched within the files assigned to a specific shard, Bun typically reports the filtered status [5]. However, regarding the shard itself, if a shard is valid but ends up with zero files assigned to it (e.g., running --shard=5/5 when only 2 test files exist), Bun exits with a status code of 0 and prints a summary line instead of treating it as an error or "No tests found!" [2]. If you pass a --test-name-pattern that matches nothing in your project, the test runner will report that tests were filtered out [5]. Because sharding happens at the file discovery level and test name pattern matching happens at the test execution level, you will see output indicating the number of tests run and the number of tests filtered out [5]. It does not print raw test file paths as a list of "no matching tests"; rather, it provides a summary of the test execution results [2][5].

Citations:


🏁 Script executed:

set -eu
printf '%s\n' '--- package.json test configuration ---'
cat -n package.json | sed -n '1,75p'
printf '%s\n' '--- bunfig.toml ---'
cat -n bunfig.toml
printf '%s\n' '--- test wrapper outline and relevant source ---'
ast-grep outline scripts/test.ts --match '$_' --view compact 2>/dev/null || true
wc -l scripts/test.ts
cat -n scripts/test.ts | sed -n '1,220p'
printf '%s\n' '--- test file paths ---'
find tests -name '*.test.ts' -print | sort | sed -n '1,120p'

Repository: lidge-jun/opencodex

Length of output: 14571


🏁 Script executed:

set -eu
printf '%s\n' '--- Bun documentation references ---'
for url in \
  'https://bun.com/docs/test/filtering' \
  'https://bun.com/docs/test/discovery' \
  'https://bun.com/docs/guides/test/run-tests' \
  'https://github.com/oven-sh/bun/commit/d7a66f504135418c40c8cbec3931f4f3520b7345d.patch'
do
  printf '\nURL: %s\n' "$url"
  curl -LfsS --max-time 20 "$url" | rg -in -C 3 'test-name-pattern|filtered|shard|file|no tests|0 pass|summary' | head -100 || true
done
printf '%s\n' '--- local Bun availability only ---'
if command -v bun >/dev/null 2>&1; then bun --version; else echo 'bun not installed'; fi
printf '%s\n' '--- pipeline status probe ---'
set +e
printf '%s\n' 'Bun summary: 0 pass, 0 fail, 0 files' \
  | grep -oE '^tests/[^:]+\.test\.ts' \
  | sort -u
printf 'without_pipefail_pipeline_status=%s\n' "${PIPESTATUS[*]}"
set -o pipefail
printf '%s\n' 'Bun summary: 0 pass, 0 fail, 0 files' \
  | grep -oE '^tests/[^:]+\.test\.ts' \
  | sort -u
printf 'with_pipefail_pipeline_status=%s\n' "${PIPESTATUS[*]}"

Repository: lidge-jun/opencodex

Length of output: 50376


🏁 Script executed:

set -eu
log=/tmp/coderabbit-shell-logs/shell-output-2zZTZD
printf '%s\n' '--- concise prior probe output ---'
rg -n -C 2 'local Bun availability|bun not installed|^[0-9]+\.[0-9]+\.[0-9]+$|without_pipefail|with_pipefail|Ran .* files|filtered out' "$log" | tail -80 || true
printf '%s\n' '--- direct pipeline-status probe ---'
set +e
printf '%s\n' 'Bun summary: 0 pass, 0 fail, 0 files' | grep -oE '^tests/[^:]+\.test\.ts' | sort -u >/dev/null
printf 'without_pipefail_pipeline_status=%s\n' "${PIPESTATUS[*]}"
set -o pipefail
printf '%s\n' 'Bun summary: 0 pass, 0 fail, 0 files' | grep -oE '^tests/[^:]+\.test\.ts' | sort -u >/dev/null
printf 'with_pipefail_pipeline_status=%s\n' "${PIPESTATUS[*]}"
printf '%s\n' '--- relevant commit patch lines ---'
curl -LfsS --max-time 20 \
  'https://github.com/oven-sh/bun/commit/d7a66f504135418c40c8cbec3931f4f3520b7345d.patch' \
  | rg -n -C 3 'shard|test-name-pattern|filtered|Ran .* files|file path|test file' \
  | head -160 || true

Repository: lidge-jun/opencodex

Length of output: 50376


Replace the shard-validation method.

At lines 29-37, the command assumes that Bun 1.3.14 emits selected test-file paths for an unmatched --test-name-pattern. Bun emits a summary instead, so grep produces no files for every shard and empty output does not prove non-overlap.

Do not add set -o pipefail to this pipeline without changing the extraction method. grep returns status 1 when no path matches, so pipefail would fail every shard. Use a supported file-list or dry-run interface, or reproduce Bun’s documented sharding algorithm from the sorted test-file list. Enforce both duplicate detection and union coverage.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@devlog/_plan/260803_ci_dev_lane_sharding/040_ship_and_review.md` around lines
29 - 37, Replace the shard-validation loop in the documented plan with a method
that does not rely on unmatched test-name output from Bun. Use a supported
file-list/dry-run interface or reproduce Bun’s documented sharding algorithm
from the sorted test-file list, then validate both that no test file appears in
multiple shards and that the union of shard assignments equals all files found
by find tests -name '*.test.ts'.

Comment thread devlog/_plan/260803_ci_dev_lane_sharding/040_ship_and_review.md
Review found a hole this change opened outside the file it edited.
release.yml accepts any successful ci.yml run for the release SHA. After
a promotion, the pull-request run for that same commit is still there and
still green — and now deliberately Windows-free, since the Windows leg is
scoped to promotion and dispatch. So a publish could be satisfied by a
run that proved nothing about Windows while the promotion run carrying it
was still in flight, or had failed.

The gate now selects a `push` run on the release branch specifically. A
pull-request run for the same SHA no longer qualifies, and the error says
why so the next person does not read it as flakiness.

Also from review:

- `.gitattributes` joins the packaging filter. It decides how tracked
  package inputs are materialized on each runner, so an attribute change
  can put CRLF shebangs into the tarball without any source file moving.
- The self-hosted workspace wipe stops swallowing its own failure. A
  clean that fails on permissions left deleted files on disk, and the
  checkout after it then validated a tree that no longer exists in git.
  Only the no-repository case — first run on a fresh box — is tolerated.
- Every checkout sets `persist-credentials: false`, matching what the
  other workflows here already do. No job in this one pushes, and the
  self-hosted box keeps its checkout between jobs.

The plan documents carried the same GUI-build omission the first live run
found, so they are corrected too rather than left describing a workflow
that fails.

One review point is rebutted rather than applied: the devlog dates are
not future-dated. They read 2026-08-03 because that is the date in the
authoring timezone; the same moment is 2026-08-02 in UTC, which is what
the CI timestamps show. The reasoning is recorded with the audit trail.
@lidge-jun

Copy link
Copy Markdown
Owner Author

Thanks both — nine comments, seven fixed in fd5ca50f, one already fixed in 32c56a82, one rebutted with evidence.

Codex P1 (release gate) was the best find in this review, planning rounds included. It is a hole this PR opened outside the file it edits: release.yml selects a CI run by SHA and status alone, so after a promotion the pull-request run for that same commit is still there, still green, and now deliberately Windows-free. The publish gate could have been satisfied by a run that proved nothing about Windows while the promotion run carrying it was still in flight or had failed. That is exactly the coverage hole this PR promised not to open. The gate now requires a push run on the release branch, and the error message says why so it does not read as flakiness.

# Finding Disposition
Codex P1 Release gate accepts a Windows-free PR run Fixed — gate selects event == "push" on the release branch
Codex P2 .gitattributes missing from packaging filter Fixed — it decides how packaged inputs are materialized, so an attribute change can put CRLF shebangs in the tarball with no source file moving
CodeRabbit git clean -xffd . || true swallows wipe failures Fixed — only the no-repository case (fresh box) is tolerated now
CodeRabbit persist-credentials: false missing Fixed on all six checkouts; the other workflows here already did this and ci.yml was the gap
CodeRabbit Plan examples omit the GUI build Fixed — see below, the live run found this independently
CodeRabbit 040 gate block claims fail-fast without set -e Fixed
CodeRabbit 040 leak scan omits devlog, uses two pattern sets Fixed — one pattern set, scans the whole diff

Each fix is pinned in tests/ci-workflows.test.ts, driven red before being kept.

Rebutted: the "future-dated" devlog records. The dates read 2026-08-03 because that is the date in the authoring timezone. The same moment is 2026-08-02 in UTC, which is what the CI timestamps you compared against show:

local: 2026-08-03 02:55 KST
utc:   2026-08-02 17:55 UTC

The unit slug matches. Rewriting them to a UTC reading would make the record less accurate.

Worth noting separately: the first sharded run went red on shard 1/4 and macOS, and it was a real defect in this PR rather than flake. Tests that fetch the served dashboard read their session bootstrap out of gui/dist/index.html, and nothing in those jobs built it. The old three-platform job satisfied that dependency by accident — the suite and the GUI build shared a job. I confirmed the dependency is pre-existing by reproducing the failure on an untouched checkout of dev with no gui/dist, where it passes after bun run build. Fixed in 32c56a82, with a pin asserting every job that runs the root suite builds the GUI unconditionally.

That one is the reason the audit trail in devlog/_plan/260803_ci_dev_lane_sharding/002_audit_synthesis.md is worth reading: four rounds of pre-merge review could not have caught it, because the dependency existed only as a side effect of two things sharing a job. Running it found it in four minutes.

Current run: 4m26s wall clock against the 16m39s baseline, all shards green, Windows correctly skipped, ci gate green.

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.

1 participant