Commit Graph

6 Commits

Author SHA1 Message Date
Maxim 1b39c12e36 fix(scripts): stop the headless CLI gates skipping themselves on odd paths
Both scripts decided "am I the entrypoint?" by comparing `import.meta.url` to a
`file://`-concatenated `process.argv[1]`. `import.meta.url` is percent-encoded
and symlink-resolved; raw argv[1] is neither. So the comparison was false for
any checkout path containing a space, for any invocation through a symlink
(macOS /tmp is one), and on Windows — and a false guard skipped the whole CLI
block. Reproduced before fixing: the #4893 purity gate and the bundle-size
measurement both exited 0 having printed nothing and asserted nothing, which is
worse than a gate with holes because it manufactures confidence. The guard was
added by this PR so the modules could export internals to their new negative
tests; making the gates testable introduced a way for them not to run.

Both now compare real filesystem paths through an exported `isEntrypoint`:
`fileURLToPath` defeats the encoding and Windows forms, `fs.realpathSync` on
both sides defeats symlinks, and a `path.resolve` fallback keeps a nonexistent
argv[1] from throwing.

Each `node --test` suite gains five entry-guard tests, including an end-to-end
spawn of the real script through a symlinked package-root alias whose name
contains a space — the only case that catches the call site regressing back to
a string comparison (verified: it fails against the old expression). The unit
cases assert the naive comparison really would have failed, so none of them can
pass vacuously. Both negative gates were re-proven to still bite: a doctored
dist entry pulling streamdown fails the purity gate, and a stubbed dist entry
trips the measurement's plausibility floor.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-13 17:09:29 +02:00
Maxim 60d3ef1071 fix(react-native): externalize react-dom in the headless size measurement
The esbuild `external` list omitted `react-dom`, unlike react-core's
measure-copilotchat.mjs, so a stray web-oriented edge could be absorbed
into the figure the PR's bundle claim rests on.

Checked empirically before changing anything: `react-dom` is NOT reachable
from @copilotkit/react-native/headless today. A metafile run shows 0 of the
653 input modules are react-dom, and no module references it even
pre-resolution. The reported figure is therefore UNCHANGED — 94941 B gzip
(92.7 kB) before and after, byte for byte. The headline "92.8 kB -> 92.7 kB,
flat" claim is unaffected and stays comparable with previously reported
numbers.

The guard is still worth having. Simulating a stray edge measures the
inflation it prevents: +56.3 kB gzip via react-dom/client, +57.3 kB via
react-dom/server (not the ~130 kB estimated in review — that is closer to
the raw magnitude; react-dom-client.production.js is 536 kB raw). The
subtler case is a bare `react-dom` edge at +1.4 kB, small enough to read as
noise while still being a real regression.

No subpath entries: esbuild prefix-matches package paths, so `react-dom`
already covers react-dom/client and react-dom/server (verified on the
pinned 0.27.3; esbuild CHANGELOG 0.5.14 and 0.14.13). Listing them would
imply they were required.

Hoisted the list to an exported HEADLESS_EXTERNAL with per-entry rationale,
mirroring the sibling's DEFAULT_EXTERNAL, and made `external` an overridable
option so the new test can A/B it rather than assert on a literal. Both new
tests fail if react-dom is removed from the list.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 23:16:32 +02:00
Maxim 63a1c94fbb fix(react-native): make the headless size measurement fail loudly, not print 0.0 kB
measure-headless.mjs prints the number the PR's bundle claim rests on, and it
had three ways to report a broken run as a good one. All three reproduced:

1. No zero-output guard (the react-core sibling has one). A run whose bundle
   collapses to nothing measures ~20-35 B of gzip envelope, prints "0.0 kB"
   and exits 0 — reported into the CI job summary as a spectacular win. Note a
   zero-ONLY guard would not have caught the reproduction (35 B, not 0), so
   this adds a plausibility FLOOR of 8 kB alongside the zero check: ~11x below
   the real 92.7 kB, so legitimate size work can never trip it.

2. `logLevel: "silent"` discarded `result.warnings` and there was no
   try/catch, so esbuild resolution problems escaped as an unhandled rejection
   printing esbuild's internal frames and `errors: [Getter/Setter]` instead of
   the messages. Silent is kept (as in the sibling) so stdout stays the single
   figure line CI quotes; warnings are now formatted to stderr and errors are
   re-thrown with esbuild's own formatted diagnostics.

3. An unbuilt dist died on a raw "Could not resolve" stack. A preflight on
   dist/headless.mjs now names `npx nx run @copilotkit/react-native:build`,
   and the catch adds the same hint when the entry specifier is what failed.

The measurement itself is untouched — same esbuild options, same synthetic
entry, same six symbols, same external list — and still reports 92.7 kB, so
comparability across PRs is preserved. A moved figure would have meant the
measurement changed rather than its guards.

Also adds the test hook RN lacked, mirroring react-core exactly:
scripts/__tests__/measure-headless.test.mjs under `node --test`, wired as
`test:scripts` and chained into `test`. Coverage targets the failure modes,
not the happy path. Both packages' vitest `include` globs are scoped to
`src/**`, so the .mjs test cannot collide with the jsdom setup — the reason
the sibling runs under node --test in the first place.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 23:05:01 +02:00
Maxim 19350ae6ac ci(react-native): restore useRenderToolCall to the headless size measurement
The RN headless entry now re-exports useRenderToolCall (render-tool
convergence), so the measurement's pending-state comment no longer applies.
Add the symbol back to the measured import surface and drop the TEMPORARY
note. Reported size is essentially flat (92.8 -> 92.7 kB gzip): the hook
reuses the renderer registry/resolver useRenderTool already pulls in.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-08 16:37:17 +02:00
Maxim 2bb1473207 ci(react-native): reframe useRenderToolCall omission as pending, not by design
The prior comment framed useRenderToolCall's absence from the size probe's
import list as a permanent design fact ("DOM-dependent … would fail the
build"). That is the same permanent-sounding rationale that let the stale
claim in src/index.ts go unrevisited for months. The export is missing only
because RN's headless entry does not export it yet; the render-tool
convergence on this branch adds it. Reframe the comment as TEMPORARY — REVISIT
so a future engineer restores the symbol and re-baselines the number once RN
headless exports it. Comment-only; measured size unchanged at 92.8 kB.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-08 14:57:27 +02:00
Maxim f34b7aa23c ci: measure @copilotkit/react-native bundle size (was absent from the glob)
react-native was missing from static_bundle_size.yml's package glob, so its
dist/ has never been measured despite being the consumer most exposed to the
#4893 regression (Metro does not tree-shake). This adds coverage:

- Extend the compressed-size-action glob to include react-native.
- New scripts/measure-headless.mjs: an esbuild-driven gzip signal for the
  @copilotkit/react-native/headless entry, mirroring react-core's
  measure-copilotchat.mjs (stdin + resolveDir, gzip sum, job-summary output).
- Wire a build + measure step into the copilotchat-import-size CI job.

First baseline: @copilotkit/react-native/headless = 92.8 kB gzip
(esbuild regression signal, not a Metro figure).

No limit fields (Phase 1 policy — see dev-docs/bundle-size.md).

esbuild added as a react-native devDependency (^0.27.0, matching react-core);
the root ">=0.25.4" override keeps the monorepo on a single esbuild (0.27.3).

Two corrections to the drafted script, verified by running it:
- Fed the entry via esbuild stdin with resolveDir=pkgRoot; a temp-dir entry
  cannot resolve @copilotkit/react-native/headless through workspace node_modules.
- Dropped useRenderToolCall from the import list — the RN headless surface
  deliberately does not export it (DOM-dependent; see src/index.ts).

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-08 14:51:55 +02:00