feat(tooling): local CI gate mirroring the hosted matrix (pnpm check:local-ci) - #92
Conversation
…local-ci) One command that proves what the hosted CI gate jobs prove, locally and in parallel: three Verify legs (Node 22.19/24/26, resolved from mise/nvm with no new tooling) plus a gates leg folding examples-check, release-gates, and the RSC micro-eval, each in its own git worktree with its own node_modules. The integration config now honors an external AGENT_BUNDLE_TEST_TIME_SCALE (never below the pool's own floor) so shared-machine runs can widen polling budgets. docs/local-ci.md documents the local-merge workflow and the honest exclusions (dependency-review, publish-side workflows, native smokes, OS skew).
…s for the census npm's shell launcher resolves its Node sibling relative to $0 without following symlinks, so the leg bin entries for npm/npx become exec wrappers to their real paths (caught by the gate's own first run: examples' rsc-hook.integration spawns raw `npm run build`). The census column now parses the repo reporter's JSON counts block instead of a console summary line that the reporter never prints.
|
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 98957dec3b
ℹ️ 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".
| try { | ||
| await execFile('git', ['rev-parse', '--is-inside-work-tree'], { cwd: legDirectory }); | ||
| await execFile('git', ['reset', '--hard', sha], { cwd: legDirectory }); |
There was a problem hiding this comment.
Verify the cached worktree before resetting it
If a leg directory exists but is not a registered worktree—for example after damaged worktree metadata or a previously created ordinary cache directory—git rev-parse --is-inside-work-tree walks up to the main checkout and succeeds. The subsequent reset --hard therefore runs against the developer's primary worktree and silently discards tracked uncommitted changes, despite the earlier message saying those changes are merely not covered. Verify that the reported worktree root is exactly legDirectory before resetting it; otherwise recreate the leg without touching the parent checkout.
Useful? React with 👍 / 👎.
| const override = process.env[`AGENT_BUNDLE_LOCAL_CI_NODE_${line.key}`]; | ||
| if (override !== undefined && override.length > 0) { | ||
| candidates.push(existsSync(join(override, 'node')) ? join(override, 'node') : override); | ||
| } |
There was a problem hiding this comment.
Resolve explicit Node overrides to absolute paths
When AGENT_BUNDLE_LOCAL_CI_NODE_22, _24, or _26 is a relative binary or bin-directory path, it is validated successfully from the caller's working directory but retained as relative. Later the script uses that value as a symlink target and executes it from each leg worktree, where it resolves to a different, nonexistent path, so the advertised override fails at the first pnpm step. Normalize the candidate with resolve() before storing it.
Useful? React with 👍 / 👎.
Summary
One command —
pnpm check:local-ci— that proves locally what the hosted CI gate proves, across all three Node majors, in parallel worktrees, faster than a single hosted Verify leg. Enables the local-merge workflow documented indocs/local-ci.md: local gate green ⇒ merge; hosted CI stays the async post-merge safety net.Hosted CI inventory → local mirror
verify-node22/24/26gates-node22gates-node22gates-node22Explicitly not covered (documented in
docs/local-ci.md): dependency-review (GitHub-side action), package-preview + npm release publish (publish-side effects, not checks), native-host-smoke (opt-in, needs signed-in host CLIs), and ubuntu-runner OS/glibc parity (--with-depspackage set is one-time machine setup; the main reason hosted CI remains the safety net).Mechanism
AGENT_BUNDLE_LOCAL_CI_NODE_*override →mise where→~/.nvm/versions/node→ current process, and version-checks the binary. This machine already had all three via mise..worktrees/local-ci/(already gitignored) with its ownnode_modules— native modules (rspack bindings) are never shared across Node ABIs; the content-addressed pnpm store is shared safely. pnpm runs on the leg's Node via a small exec wrapper (npm/npx likewise; npm's$0-relative launcher makes symlinks unusable — caught by this gate's own first run).npm_*/PNPM_*/COREPACK_*/CI/GITHUB_*/NODE*/strayAGENT_BUNDLE_*, and rebuilds PATH without anynode_modules/.bin.min(4, cores/(2·legs))= 4 workers/leg via the existingAGENT_BUNDLE_INTEGRATION_MAX_WORKERSknob; unitcores/legs= 32 via rstest--pool.maxWorkers; full runs pinAGENT_BUNDLE_TEST_TIME_SCALE=4(hosted's own scale).rstest.integration.config.tsnow honors an external time scale, never below the pool's own floor — default behavior unchanged.Measured (this branch, full run, machine concurrently loaded with burn-ins)
Wall time: 9m26s for the entire matrix + gates (warm legs; first-ever run incl. cold worktrees was comparable) vs hosted ~11m39s–16m for one run and ~9–12m for a single Verify leg. Fast path
--current-node-only: green in 5m37s.Census parity with hosted @ current main (560124a, from the green Verify (Node 24) leg logs): unit 1775 passed | 4 skipped (1779) and integration 556 passed | 2 skipped (558) — exact match on all three local legs. Note: hosted Verify (Node 22.19.0) on 560124a failed on
mcp-app-real.e2e.test.tspage.waitForRequest30s timeout — the known load-induced browser-timing class (the previous green main run was itself a re-run); all three local legs pass that test.Test plan
--current-node-onlyfast path: GREEN