Skip to content

chore(ci): ignore generated sidecar bundles in edge checks - #5926

Open
JacobisEpic wants to merge 3 commits into
koala73:mainfrom
JacobisEpic:chore/5920-ignore-sidecar-edge-bundles
Open

chore(ci): ignore generated sidecar bundles in edge checks#5926
JacobisEpic wants to merge 3 commits into
koala73:mainfrom
JacobisEpic:chore/5920-ignore-sidecar-edge-bundles

Conversation

@JacobisEpic

Copy link
Copy Markdown
Contributor

Summary

Fixes #5920.

This PR centralizes edge-function candidate discovery in scripts/check-edge-function-bundles.mjs and makes both pre-push and CI use that checker.
The checker uses Git's tracked API inventory, which represents the files that can ship in a commit, rather than arbitrary working-tree JavaScript left by local desktop builds.
The sidecar builder, output locations, route table, packaging, and runtime behavior remain unchanged.

Type of change

  • Bug fix
  • New feature
  • New data source / feed
  • New map layer
  • Refactor / code cleanup
  • Documentation
  • CI / Build / Infrastructure

Affected areas

  • Map / Globe
  • News panels / RSS feeds
  • AI Insights / World Brief
  • Market Radar / Crypto
  • Desktop app (Tauri)
  • API endpoints (/api/*)
  • Config / Settings
  • Other: local and CI edge-function validation

Reproduction and root cause

The exact pre-push esbuild command selected 48 candidates and passed before sidecar generation.
Running node scripts/build-sidecar-handlers.mjs produced 34 ignored Node bundles with this signature:

api/<domain>/v1/[rpc].ts  # tracked edge source
api/<domain>/v1/[rpc].js  # ignored Node sidecar bundle

The same pre-push command then selected 82 files and failed with 36 browser-bundling errors, including unresolved node:crypto imports from the generated bundles.
The recursive API source invariant test and the CI edge loop also failed when run after generation.
Deleting only the 34 verified ignored bundles restored the original 48-candidate pass.

CI previously avoided the failure only because npm run test:data and the inline edge loop ran before the sidecar handler build in the unit job.

Filtering contract

The canonical selector now uses git ls-files -z -- api, sorts deterministically, and applies the existing edge-entry rules to tracked paths.
Tracked files are the correct deployment contract because an untracked file cannot reach CI or Vercel through a push.
This cannot hide a legitimate committed edge function: tracked JavaScript remains selected even when it has a same-basename TypeScript sibling, and a tracked similarly named api/v2/shipping/[rpc].js fixture remains selected.
The current repository has zero tracked JavaScript/TypeScript sibling pairs under api/.

The checker passes path arrays directly to esbuild, assigns extension-specific output names so legitimate JS/TS siblings cannot collide, cleans temporary output on success and failure, and fails if zero candidates are selected.
Pre-push, CI, and tests/edge-functions.test.mjs share this tracked inventory.

Regression coverage

tests/edge-function-bundles.test.mjs creates isolated temporary Git repositories and verifies:

  • a real tracked JavaScript edge entry remains included;
  • ignored generated sidecar residue with its TypeScript source is excluded;
  • stale ignored residue without a TypeScript source is excluded;
  • a normal tracked JavaScript entry remains included;
  • tracked JavaScript and TypeScript siblings are both included and bundled;
  • a tracked similarly named v2 [rpc].js entry is not hidden;
  • a path containing whitespace is handled correctly;
  • zero candidates fail closed;
  • a tracked browser-incompatible import still fails the real checker.

Workflow and pre-push wiring tests verify both surfaces call the shared checker and no working-tree find api discovery remains.

Mutation results

  • False-positive mutation: temporarily re-including the generated api/domain/v1/[rpc].js made the candidate assertion fail and the real checker fail on node:crypto.
  • False-negative mutation: temporarily excluding legitimate api/health.js made both the exact candidate assertion and real-checker inclusion assertion fail.

Both mutations were restored before final validation.

Validation

  • Clean tree: node scripts/check-edge-function-bundles.mjs passed with 48 tracked entrypoints.
  • Generated residue present: the same command passed with 48 tracked entrypoints while all 34 ignored bundles remained on disk.
  • Deliberate real edge failure: a temporary node:crypto import in tracked api/health.js made the checker exit 1, then the file was restored to its original SHA-256.
  • node --test tests/edge-function-bundles.test.mjs passed 4/4.
  • node --test tests/edge-functions.test.mjs passed 240/240 with generated residue present.
  • node --test tests/prepush-attest.test.mjs passed 35/35.
  • node --import tsx --test tests/ci-workflow-coverage.test.mts passed 16/16.
  • Pre-push contract and hook tests passed 39/39 with an enforced temporary macOS-compatible timeout shim.
  • Desktop configuration tests passed 169/169 with the repository TS loader.
  • npm run test:sidecar passed 306/306.
  • node scripts/build-sidecar-sebuf.mjs correctly reported the historical gateway as removed.
  • node scripts/build-sidecar-handlers.mjs built all 34 domain handlers.
  • The CI-equivalent sidecar output assertion found all 34 bundles.
  • npm run typecheck passed.
  • npm run typecheck:api passed.
  • npm run lint exited 0; changed files are Biome-clean, and the repository-wide command reported only existing warnings outside this patch.
  • bash -n .husky/pre-push passed.
  • git diff --check and git diff --cached --check passed.
  • The exact .husky/pre-push executable passed all scoped gates and 319 changed/wiring tests on the committed tree.

Maintainer-style adversarial review

  • A committed Vercel JavaScript function cannot be skipped because it has a TypeScript sibling.
  • The checker fails closed when discovery returns zero entries.
  • Complete, partial, repeated, and stale sidecar builds cannot affect the tracked candidate list.
  • The current sidecar builder emits only domain-first v1 handlers, while the regression proves similarly named tracked v2 JavaScript is still checked.
  • Generated bundles contain no stable intentional banner, so the Git deployment contract is stronger than content sniffing.
  • CI remains correct if the sidecar build step is reordered before the edge check.
  • macOS and Linux no longer depend on shell file-discovery semantics.
  • No sidecar producer, loader, route, packaging, Vercel runtime, or production function behavior changed.
  • The patch is limited to edge discovery, caller wiring, and regression coverage.

Checklist

  • Tested on worldmonitor.app variant - N/A, tooling-only change
  • Tested on tech.worldmonitor.app variant (if applicable) - N/A, tooling-only change
  • New RSS feed domains added to api/rss-proxy.js allowlist (if adding feeds) - N/A
  • No API keys or secrets committed
  • TypeScript compiles without errors (npm run typecheck)

Documentation Alignment Checklist

N/A.
This PR does not publish or change documentation claims, API contracts, generated docs, examples, Redis keys, CII, CRI, news, digest, or briefing behavior.

  • Claim ledger attached or linked - N/A
  • All required Audit Council role signoffs attached - N/A
  • Generated docs regenerated from proto where applicable - N/A
  • Fixture-backed examples recomputed - N/A
  • Redis writers/readers enumerated for every documented key - N/A

Screenshots

N/A.
This is a tooling-only change with no user-interface impact.

@vercel

vercel Bot commented Jul 31, 2026

Copy link
Copy Markdown

@JacobisEpic is attempting to deploy a commit to the World Monitor Team on Vercel.

A member of the Team first needs to authorize it.

koala73 added 2 commits August 4, 2026 17:34
…its CLI (koala73#5926)

Review findings on this PR. The approach verified clean under scrutiny --
selection is an exact set-equality match with the old pre-push rules, the
pathspec cannot match apiv2/, out-names do not collide, and nothing under api/
is generated at deploy time -- so everything below is about the gate's own
entry path and test surface.

- tests/edge-functions.test.mjs registered one it() PER DISCOVERED FILE for both
  api-wide guards, with no non-empty floor. `git ls-files` exits 0 with empty
  output on a non-matching pathspec, so empty discovery emitted zero assertions
  and the suite went green while checking nothing -- a `node:` import smuggled
  into an edge function would have shipped unnoticed. The pre-PR readdirSync
  threw instead, so this was a fail-open the PR introduced. Adds a discovery
  floor mirroring the checker's own zero-entry guard.

- The entry guard used `import.meta.url === pathToFileURL(path.resolve(argv[1]))`,
  which does not resolve symlinks while Node sets import.meta.url to the
  realpath. For an absolute symlinked argv[1] that returns false, main() never
  runs, and this merge-blocking gate exits 0 having bundled nothing. Not
  reachable from the current relative-path call sites, but the identical
  fail-open already shipped here once (koala73#4246). Switches to the repo's existing
  realpath-safe isMainModule(), as 7 other scripts already do.

- Nothing executed the CLI. The suite called the exported functions directly, so
  main(), the entry guard, --list, and the exit code -- everything CI and
  .husky/pre-push actually invoke -- were untested, and the wiring tests only
  assert the command line appears in the workflow/hook text. Adds five
  subprocess tests, including one that invokes through a symlink.

- isolatedGitEnv() was duplicated in the test file and had already drifted
  (the copy nulls GIT_CONFIG_GLOBAL/SYSTEM; the shipped gate does not). Exports
  the real one and layers the fixture-only overrides on top.

- Factors the underscore rule behind one predicate, documenting why the two
  selectors apply it at different depths (each mirrors the surface it replaced).

- AGENTS.md:209 still documented the edge check trigger as api/|server/|
  src/generated/ after the hook gained the checker-script path.

Verified: edge-function-bundles 9 pass (was 4), edge-functions 241 (was 240),
prepush-attest 35, ci-workflow-coverage 16, biome clean, and the real checker
still passes on this repo at 48 tracked entrypoints. Three mutants confirmed
killed -- reverting to the naive entry guard, gutting main() to a no-op, and
making discovery return empty.

Claude-Session: https://claude.ai/code/session_01GwG8L6jjf8wxjbUqAr23m5
Resolves the conflict blocking this PR. One real conflict, in
tests/ci-workflow-coverage.test.mts: both sides appended a test at the same
point in the same describe. They are unrelated -- this branch's
"shares tracked edge bundle discovery with pre-push" and main's
"routes Tauri config edits into the job that runs the one-binary gate (koala73#5908)"
-- so both are kept.

The other three files (.github/workflows/test.yml, .husky/pre-push,
tests/edge-functions.test.mjs) auto-merged. Checked the merged result rather
than trusting the auto-merge: the CI step and the pre-push branch both still
invoke the shared checker, and no working-tree `find api/` or readdirSync
discovery survived anywhere.

Verified on the merged tree: edge-function-bundles 9 pass, edge-functions 250,
prepush-attest 35, ci-workflow-coverage 19 (both conflicting tests present and
passing), and the real checker passes at 50 tracked entrypoints -- up from 48,
main having added two api/ entries.

Claude-Session: https://claude.ai/code/session_01GwG8L6jjf8wxjbUqAr23m5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

trust:safe Brin: contributor trust score safe

Projects

None yet

Development

Successfully merging this pull request may close these issues.

chore(ci): edge-bundle checks glob generated api/*/v1/[rpc].js sidecar bundles left by local desktop builds

2 participants