Skip to content

feat(tooling): local CI gate mirroring the hosted matrix (pnpm check:local-ci) - #91

Closed
ScriptedAlchemy wants to merge 2 commits into
mainfrom
chore/local-ci-gate
Closed

feat(tooling): local CI gate mirroring the hosted matrix (pnpm check:local-ci)#91
ScriptedAlchemy wants to merge 2 commits into
mainfrom
chore/local-ci-gate

Conversation

@ScriptedAlchemy

Copy link
Copy Markdown
Owner

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 in docs/local-ci.md: local gate green ⇒ merge; hosted CI stays the async post-merge safety net.

Hosted CI inventory → local mirror

Hosted job (ci.yml) Local leg Steps
Verify (Node 22.19.0 / 24 / 26) verify-node22/24/26 install → playwright chrome → build → lint:package → typecheck → lint → test:unit → test:integration (same scripts, same order)
Examples check (Node 22.19) gates-node22 examples:check
Release gates (Node 22.19) gates-node22 check:release (pack:dry-run + audit:release + test:packed)
RSC runtime micro-eval (Node 22.19) gates-node22 eval:spot

Explicitly 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-deps package set is one-time machine setup; the main reason hosted CI remains the safety net).

Mechanism

  • Multi-Node without new tooling: resolves each hosted runtime line (22.19.x / 24.x / 26.x) from 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.
  • Isolation: each leg is a git worktree pinned to HEAD under .worktrees/local-ci/ (already gitignored) with its own node_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).
  • Env hygiene: leg env scrubs npm_*/PNPM_*/COREPACK_*/CI/GITHUB_*/NODE*/stray AGENT_BUNDLE_*, and rebuilds PATH without any node_modules/.bin.
  • Worker caps (96 cores, 3 verify legs): integration min(4, cores/(2·legs)) = 4 workers/leg via the existing AGENT_BUNDLE_INTEGRATION_MAX_WORKERS knob; unit cores/legs = 32 via rstest --pool.maxWorkers; full runs pin AGENT_BUNDLE_TEST_TIME_SCALE=4 (hosted's own scale). rstest.integration.config.ts now 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)

Leg Step Status Duration Tests
verify-node22 (22.19.0) install→lint pass 22s
verify-node22 test:unit pass 1m40s 1775/1779 passed, 4 skipped, 128 files
verify-node22 test:integration pass 4m35s 556/558 passed, 2 skipped, 51 files
verify-node24 (24.19.0) install→lint pass 21s
verify-node24 test:unit pass 1m36s 1775/1779 passed, 4 skipped, 128 files
verify-node24 test:integration pass 4m32s 556/558 passed, 2 skipped, 51 files
verify-node26 (26.8.1) install→lint pass 20s
verify-node26 test:unit pass 1m33s 1775/1779 passed, 4 skipped, 128 files
verify-node26 test:integration pass 4m23s 556/558 passed, 2 skipped, 51 files
gates-node22 examples:check pass 4m31s 173/179 passed, 6 skipped (2 suites)
gates-node22 check:release pass 4m37s 22/23 passed, 1 skipped, 8 files
gates-node22 eval:spot pass 12s 1/1 passed

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.ts page.waitForRequest 30s 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

  • Full gate end-to-end on this branch: GREEN (table above), exit code verified non-zero on failure (first run failed honestly on the npm-wrapper bug and skipped downstream steps)
  • --current-node-only fast path: GREEN
  • Census parity vs hosted current main verified from hosted logs
  • No published-package behavior changes → no changeset (workspace tooling + root test config only)

…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.
@changeset-bot

changeset-bot Bot commented Aug 31, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 98957de

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Aug 31, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-08-31T21:34:57.204361Z 98957de PR opened
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@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: 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".

Comment thread scripts/local-ci.mjs
const candidates = [];
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);

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 Resolve override paths before creating leg wrappers

When AGENT_BUNDLE_LOCAL_CI_NODE_* contains a relative binary or bin-directory path, such as ./.nodes/22/bin, the version check succeeds relative to the repository, but the same relative value is later used as a symlink target and embedded in wrappers executed from each worktree. Those paths then resolve relative to .worktrees/local-ci/.bin/... or the leg worktree, so the Node/pnpm launch fails. Normalize the override to an absolute path before adding it to candidates.

Useful? React with 👍 / 👎.

@ScriptedAlchemy

Copy link
Copy Markdown
Owner Author

Closing as an exact duplicate of #92, which squash-merged this same branch (chore/local-ci-gate, commits 89ebb67 + 98957de) to main as 3f8f08f while this PR was open on the same head ref.

Evidence:

No unique content to land; nothing to fix. Deleting the branch separately to complete the merge cleanup that was skipped while this PR held the head ref.

@ScriptedAlchemy
ScriptedAlchemy deleted the chore/local-ci-gate branch August 31, 2026 22:36
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