From 34650b76d2b3f379df87f5ad9d38038af4a89a25 Mon Sep 17 00:00:00 2001 From: Manuel Schiller <6340397+schiller-manuel@users.noreply.github.com> Date: Sun, 6 Sep 2026 00:07:27 +0200 Subject: [PATCH 1/9] agents --- AGENTS.md | 194 +++-------------------- benchmarks/AGENTS.md | 13 ++ docs/AGENTS.md | 4 + e2e/AGENTS.md | 9 ++ examples/AGENTS.md | 4 + packages/AGENTS.md | 16 ++ packages/history/AGENTS.md | 3 + packages/router-core/AGENTS.md | 12 ++ packages/router-generator/AGENTS.md | 6 + packages/router-plugin/AGENTS.md | 7 + packages/router-ssr-query-core/AGENTS.md | 9 ++ packages/start-client-core/AGENTS.md | 5 + packages/start-plugin-core/AGENTS.md | 7 + packages/start-server-core/AGENTS.md | 6 + 14 files changed, 125 insertions(+), 170 deletions(-) create mode 100644 benchmarks/AGENTS.md create mode 100644 docs/AGENTS.md create mode 100644 e2e/AGENTS.md create mode 100644 examples/AGENTS.md create mode 100644 packages/AGENTS.md create mode 100644 packages/history/AGENTS.md create mode 100644 packages/router-core/AGENTS.md create mode 100644 packages/router-generator/AGENTS.md create mode 100644 packages/router-plugin/AGENTS.md create mode 100644 packages/router-ssr-query-core/AGENTS.md create mode 100644 packages/start-client-core/AGENTS.md create mode 100644 packages/start-plugin-core/AGENTS.md create mode 100644 packages/start-server-core/AGENTS.md diff --git a/AGENTS.md b/AGENTS.md index 3368f3f100f..90a984c4bb2 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,181 +1,35 @@ # AGENTS.md -Read and follow [`CONTRIBUTING.md`](./CONTRIBUTING.md) before analyzing or -changing this repository. Its contribution and pull request requirements apply -to AI-assisted work. +## Scope -## Project overview +- Read applicable nested `AGENTS.md` files before editing or testing a subtree. +- Shared routing belongs in `packages/router-core`; check React, Solid, and Vue bindings for shared changes. Shared Start runtime/build logic lives in `packages/start-*-core`. -TanStack Router is a type-safe router with built-in caching and URL state management for React and Solid applications. This monorepo contains two main products: +## Work and validation -- **TanStack Router** - Core routing library with type-safe navigation, search params, and path params -- **TanStack Start** - Full-stack framework built on top of TanStack Router +- Use Node from `.nvmrc` and pnpm from root `package.json`. Install at the root with `CI=1 pnpm install --frozen-lockfile`. +- For sandbox-blocked registry/store access, escalate the same command; report the blocker if unavailable. Do not change stores, delete dependencies/lockfiles, use `--force`/`--ignore-scripts`, or weaken workspace trust/build policies to bypass it. +- Never manually edit `pnpm-lock.yaml` or any `routeTree.gen.ts`. Regenerate the lockfile with `pnpm install --no-frozen-lockfile` after intentional dependency changes; regenerate route trees through app builds/dev servers or the generator fixture harness. +- Always use control-flow braces. Run `pnpm format` before validation. +- Use Nx: workspace imports consume built packages, so direct runners can test stale dependencies. Inspect resolved targets; some are inferred. -## Setup commands +```sh +CI=1 NX_DAEMON=false pnpm nx run : --outputStyle=stream --skipRemoteCache +``` -- Install deps: `pnpm install` -- Setup e2e testing: `pnpm exec playwright install` -- Build packages: `pnpm build` (affected) or `pnpm build:all` (force all) -- Start dev server: `pnpm dev` -- Run tests: `pnpm test` +- Run one Nx command at a time. For a ~20-second startup/graph stall, stop, run `pnpm nx reset`, and retry once, then escalate. Do not apply that timeout to running tests. +- For code changes, run affected packages' `test:unit`, `test:types`, and `test:eslint` where available, including affected consumers; use `test:e2e` for browser/app behavior and `test:build` for exports/build changes. Root `pnpm test` includes the full e2e suite. Published-code changes require `pnpm changeset`. -## Code style +## Runtime rules -- TypeScript strict mode with extensive type safety -- Framework-agnostic core logic separated from React/Solid bindings -- Type-safe routing with search params and path params -- Use workspace protocol for internal dependencies (`workspace:*`) -- Always use curly braces for `if`, `else`, loops, and similar control statements. Never write one-line bodies like `if (foo) x = 1`. +- Import `isServer` from `@tanstack/router-core/isServer`. Conditional exports select: `development` → `undefined`; server (`workerd`, `worker`, `deno`, `node`, `bun`) → `true` except `NODE_ENV=test` → `undefined`; browser/fallback → `false`. `development` wins; `NODE_ENV=development` alone does not select it. +- Keep `isServer ?? router.isServer` directly in each branch condition for dead-code elimination; negate the whole expression for client branches. Never extract it into a variable (e.g. `const serverRendering = isServer ?? router.isServer`) or helper. Without a router, inline `isServer ?? typeof window === 'undefined'` (or the existing `document` check). Keep the constant first and use `??`, never `||`. +- **Never create UI/router reactivity on the server, including development/tests.** Branch before reactive setup; use direct reads and core non-reactive stores. Required transport listeners are allowed only with request-scoped lifetimes and cleanup. +- Shared route definitions/build metadata must not acquire request/user data. Keep it on the request's Router, context, or QueryClient. +- HMR can replace code while caches and pending work survive. Invalidate derived state or bypass caching in development; reject stale async results while preserving intentional HMR identities/state. +- Gate developer diagnostics and message construction with direct `process.env.NODE_ENV !== 'production'` checks. Preserve required validation/throws in production (e.g. detailed development error, compact `invariant()` failure). `!== 'development'` also enables the guarded code in tests; preserve existing dev-server mode gates. -## Dev environment tips +## Performance and final bundle pass -- This is a pnpm workspace monorepo with packages organized by functionality -- Nx provides caching, affected testing, targeting, and parallel execution for efficiency -- Use `pnpm nx show projects` to list all available packages -- Target specific packages: `pnpm nx run @tanstack/react-router:test:unit` -- Target multiple packages: `pnpm nx run-many --target=test:eslint --projects=@tanstack/history,@tanstack/router-core` -- Run affected tests only: `pnpm nx affected --target=test:unit` -- Exclude patterns: `pnpm nx run-many --target=test:unit --exclude="examples/**,e2e/**"` -- Navigate to examples and run `pnpm dev` to test changes: `cd examples/react/basic && pnpm dev` -- **Granular unit testing through Nx (recommended):** - - Specific files: `pnpm nx run @tanstack/react-router:test:unit -- tests/link.test.tsx tests/Scripts.test.tsx` - - Test patterns: `pnpm nx run @tanstack/react-router:test:unit -- tests/ClientOnly.test.tsx -t "should render fallback"` - - Name patterns: `pnpm nx run @tanstack/react-router:test:unit -- -t "navigation"` (all tests with "navigation" in name) - - Exclude patterns: `pnpm nx run @tanstack/react-router:test:unit -- --exclude="**/*link*" tests/` - - List tests: `pnpm nx run @tanstack/react-router:test:unit -- list tests/link.test.tsx` (or `-- list` for all) -- **Available test targets per package:** `test:unit`, `test:types`, `test:eslint`, `test:build`, `test:perf`, `build` -- **Testing strategy:** Package level (nx) → File-level args via nx → Test-level args (`-t`) via nx → Pattern-level args (`--exclude`) via nx -- **Agent execution guardrails (important):** - - Always prefer `pnpm nx ...` over `npx nx ...`. - - Prefer Nx targets over direct test runners so task dependencies (including required builds) remain in the graph. - - In sandbox, run Nx with `CI=1 NX_DAEMON=false pnpm nx run : --outputStyle=stream --skipRemoteCache` - - Run only one Nx command at a time. - - If an Nx command shows no output for ~20 seconds, stop, run `pnpm nx reset` once, and retry once. - - Do not loop retries indefinitely. If it still hangs or sandbox blocks graph/daemon behavior, request escalation immediately. - -## Testing instructions - -- **Critical**: Always run unit and type tests during development - do not proceed if they fail -- **Test types:** `pnpm test:unit`, `pnpm test:types`, `pnpm test:eslint`, `pnpm test:e2e`, `pnpm test:build` -- **Full CI suite:** `pnpm test:ci` -- **Fix formatting:** `pnpm format` -- **Efficient targeted testing workflow:** - 1. **Affected only:** `pnpm nx affected --target=test:unit` (compares to main branch) - 2. **Specific packages:** `pnpm nx run @tanstack/react-router:test:unit` - 3. **Specific files:** `pnpm nx run @tanstack/react-router:test:unit -- tests/link.test.tsx` - 4. **Specific patterns:** `pnpm nx run @tanstack/react-router:test:unit -- tests/link.test.tsx -t "preloading"` -- **Pro tips:** - - Use `pnpm nx run @tanstack/react-router:test:unit -- list` to explore available tests before running - - Use `-t "pattern"` to focus on specific functionality during development - - Use `--exclude` patterns to skip unrelated tests - - Keep all test filtering arguments behind `pnpm nx run ... -- ...` for maximum precision while preserving task dependencies -- **Example workflow:** `pnpm nx run @tanstack/react-router:test:unit` → `pnpm nx run @tanstack/react-router:test:unit -- tests/link.test.tsx` → `pnpm nx run @tanstack/react-router:test:unit -- tests/link.test.tsx -t "preloading"` - -## PR instructions - -- Always run `pnpm test:eslint`, `pnpm test:types`, and `pnpm test:unit` before committing -- Test changes in relevant example apps: `cd examples/react/basic && pnpm dev` -- Update corresponding documentation in `docs/` directory when adding features -- Add or update tests for any code changes -- Use internal docs links relative to `docs/` folder (e.g., `./guide/data-loading`) - -## Package structure - -**Core packages:** - -- `packages/router-core/` - Framework-agnostic core router logic -- `packages/react-router/`, `packages/solid-router/` - React/Solid bindings and components -- `packages/history/` - Browser history management - -**Tooling:** - -- `packages/router-cli/` - CLI tools for code generation -- `packages/router-generator/` - Route generation utilities -- `packages/router-plugin/` - Universal bundler plugins (Vite, Webpack, ESBuild, Rspack) -- `packages/virtual-file-routes/` - Virtual file routing system - -**Developer experience:** - -- `packages/router-devtools/`, `packages/*-router-devtools/` - Development tools -- `packages/eslint-plugin-router/` - ESLint rules for router - -**Validation adapters:** - -- `packages/zod-adapter/`, `packages/valibot-adapter/`, `packages/arktype-adapter/` - -**Start framework:** - -- `packages/*-start/`, `packages/start-*/` - Full-stack framework packages - -**Examples & testing:** - -- `examples/react/`, `examples/solid/` - Example applications (test changes here) -- `e2e/` - End-to-end tests (requires Playwright) -- `docs/router/`, `docs/start/` - Documentation with React/Solid subdirectories - -**Dependencies:** Uses workspace protocol (`workspace:*`) - core → framework → start packages - -## Common development tasks - -**Adding new routes:** - -- Use file-based routing in `src/routes/` directories -- Or use code-based routing with route definitions -- Run route generation with CLI tools - -**Testing changes:** - -- Build packages: `pnpm build` or `pnpm dev` (watch mode) -- Run example apps to test functionality -- Use devtools for debugging router state - -**Documentation updates:** - -- Update relevant docs in `docs/` directory -- Ensure examples reflect documentation changes -- Test documentation links and references -- Use relative links to `docs/` folder format - -## Framework-specific notes - -**React:** - -- Uses React Router components and hooks -- Supports React Server Components (RSC) -- Examples include React Query integration -- Package: `@tanstack/react-router` - -**Solid:** - -- Uses Solid Router components and primitives -- Supports Solid Start for full-stack applications -- Examples include Solid Query integration -- Package: `@tanstack/solid-router` - -## Environment requirements - -- **Node.js** - Required for development -- **pnpm** - Package manager (required for workspace features) -- **Playwright** - Required for e2e tests (`pnpm exec playwright install`) - -## Key architecture patterns - -- **Type Safety**: Extensive TypeScript for type-safe routing -- **Framework Agnostic**: Core logic separated from framework bindings -- **Plugin Architecture**: Universal bundler plugins using unplugin -- **File-based Routing**: Support for both code-based and file-based routing -- **Search Params**: First-class support for type-safe search parameters - -## Development workflow - -1. **Setup**: `pnpm install` and `pnpm exec playwright install` -2. **Build**: `pnpm build:all` or `pnpm dev` for watch mode -3. **Test**: Make changes and run relevant tests (use nx for targeted testing) -4. **Examples**: Navigate to examples and run `pnpm dev` to test changes -5. **Quality**: Run `pnpm test:eslint`, `pnpm test:types`, `pnpm test:unit` before committing - -## References - -- **Documentation**: https://tanstack.com/router -- **GitHub**: https://github.com/TanStack/router -- **Discord Community**: https://discord.com/invite/WrRKjPJ +- For runtime/memory changes, compare identical baseline/candidate workloads using the relevant [benchmark guide](benchmarks/AGENTS.md): client navigation, Start SSR, or memory. Add coverage for unmeasured mechanisms. +- **Last phase for every change affecting emitted client JavaScript**, including core/build transforms: complete the full [bundle-size-optimization skill](.agents/skills/bundle-size-optimization/SKILL.md) after correctness and performance validation. The full workflow is mandatory. diff --git a/benchmarks/AGENTS.md b/benchmarks/AGENTS.md new file mode 100644 index 00000000000..268e23d9cc3 --- /dev/null +++ b/benchmarks/AGENTS.md @@ -0,0 +1,13 @@ +# Benchmarks + +Read the relevant README before running benchmarks. Compare baseline and candidate with the same workload and production builds; add a focused case when existing scenarios miss the changed mechanism. Keep workloads and benchmark names stable for historical comparisons. + +| Changed behavior | Guide | Nx project | +| ------------------------------------------ | ---------------------------------- | -------------------------------------------------------- | +| Standalone Router navigation/rendering CPU | [client-nav](client-nav/README.md) | `@benchmarks/client-nav` | +| Start request/SSR CPU | [ssr](ssr/README.md) | `@benchmarks/ssr` | +| Client/server retention and allocations | [memory](memory/README.md) | `@benchmarks/memory-client`, `@benchmarks/memory-server` | + +Use `test:perf:` through Nx. Plain memory runs only smoke-test; memory metrics require CodSpeed. Client navigation benchmarks do not cover Start hydration or client server-function calls. + +For bundle-size work, follow the full optimization skill required by the root guide. Full API fixtures are maintained manually: when public hooks/components change, update matching `bundle-size/scenarios/*-full/src/routes/__root.tsx` fixtures. Save baseline results separately because each run overwrites `bundle-size/results/current.json`; report final gzip deltas and explain remaining growth. diff --git a/docs/AGENTS.md b/docs/AGENTS.md new file mode 100644 index 00000000000..ab2d4117e22 --- /dev/null +++ b/docs/AGENTS.md @@ -0,0 +1,4 @@ +# Documentation + +- Register new published pages in `router/config.json` or `start/config.json`; drafts need not be registered. +- Run `pnpm test:docs` from the repository root for Markdown link validation. It checks `docs/**/*.md`, not Markdown elsewhere. Relative links resolve from the containing file. diff --git a/e2e/AGENTS.md b/e2e/AGENTS.md new file mode 100644 index 00000000000..f373ecba4f3 --- /dev/null +++ b/e2e/AGENTS.md @@ -0,0 +1,9 @@ +# E2E tests + +- Read the project's `package.json` and test config. Inspect resolved targets with `CI=1 NX_DAEMON=false pnpm nx show project --json`: `nx.metadata.playwrightModes` generates modes, variants, and shards. The aggregate `test:e2e` can also run extra package-script suites. For focused Playwright runs, select an executable test target (a mode/shard target when inferred) and append filters after `--` (e.g. `-- tests/navigation.spec.ts --grep "redirect"`). +- Check Playwright's `webServer.command` to select dev or production coverage. A mode named `ssr` can still start a dev server. `e2e/eslint-plugin-start` uses Vitest instead of Playwright. +- For inferred Playwright modes, Nx builds first and supplies `MODE`, `E2E_TOOLCHAIN`, `E2E_DIST_DIR`, and `E2E_PORT_KEY`. Preserve these in server/config changes; let Nx own the build while `webServer.command` only starts the selected server. Target generation lives in [`scripts/nx/playwright-plugin.ts`](../scripts/nx/playwright-plugin.ts). +- Use the same `E2E_PORT_KEY`-aware key throughout config, app, and dummy-server setup/teardown. Clean only that key's port files, before allocation, when `TEST_WORKER_INDEX === undefined`; worker config reloads must not remap a running server's port. +- For new browser tests, reuse the local error-checking fixture. The shared `test` from `@tanstack/router-e2e-utils` fails on unexpected `console.error`, including hydration errors; allow only expected errors with `whitelistErrors`. +- HMR/watch tests edit real app sources. Reuse existing edit/restore helpers such as `createHmrFileEditor`, restore edits in teardown even after failures, and preserve the suites' single-worker and Nx `parallelism: false` settings. +- `@tanstack/router-e2e-utils:test:unit` is a placeholder. Validate helper changes through consuming e2e projects. diff --git a/examples/AGENTS.md b/examples/AGENTS.md new file mode 100644 index 00000000000..eb47fbd9256 --- /dev/null +++ b/examples/AGENTS.md @@ -0,0 +1,4 @@ +# Examples + +- Versioned dependencies on this repository's packages resolve locally through `pnpm-workspace.yaml` overrides. Keep published version ranges in example manifests; do not replace them with `workspace:*` as a linking fix. +- Root `pnpm dev` watches package builds. To run an example app, use its own `dev` script after building its workspace dependencies. diff --git a/packages/AGENTS.md b/packages/AGENTS.md new file mode 100644 index 00000000000..0c63c3a15c6 --- /dev/null +++ b/packages/AGENTS.md @@ -0,0 +1,16 @@ +# Packages + +- Route generation/code splitting live in `router-generator`/`router-plugin`; Start build transforms live in `start-plugin-core`. + +## Router bindings + +- Navigation/render identity is more than href or route membership: same-href navigations can have different history keys, and suspended renders must acknowledge only their own match publication. Preserve those identity checks when optimizing stores or render notifications. +- In navigation params/search, an explicit `undefined` can clear an inherited value; an absent key can inherit it. Preserve that distinction when comparing or memoizing options; React's `useValueStable` uses `deepEqual` with `ignoreUndefined: false`. + +## Tests + +- Inspect test commands: several `test:unit` scripts immediately exit or only echo a message. For these packages, validate behavior in the shared core or consuming e2e app; a successful placeholder target provides no unit coverage. +- For `*-router-ssr-query` changes, run `router-ssr-query-core` unit tests and the affected framework's `e2e/-start/query-integration` tests. The framework wrapper unit targets currently exit without running tests. +- For single-command Vitest targets, append filters after `--`, e.g. `-- tests/link.test.tsx -t "preloading"`. +- `solid-router` and `router-generator` chain two Vitest commands in `test:unit`. Trailing file/name/update arguments reach only the final command. Run the full target for verification: Solid needs browser and server suites; the generator needs fixture generation followed by typechecking. +- For public type changes, add `*.test-d.*` inference regressions and run both unit and type targets. React/Solid Router's TypeScript compiler matrix checks `src`; Vitest runs their inference tests. For changes to inference cost, follow the [type-safety performance guidance](../docs/router/guide/type-safety.md#performance-recommendations); `test:perf` measures runtime, not compiler cost. diff --git a/packages/history/AGENTS.md b/packages/history/AGENTS.md new file mode 100644 index 00000000000..509da6a4ec7 --- /dev/null +++ b/packages/history/AGENTS.md @@ -0,0 +1,3 @@ +# History + +Accepted browser push/replace operations update the in-memory location immediately and batch native writes into a microtask. Preserve coalescing: the latest href/state wins, but any queued push wins over replaces. Use `history.flush()` when native browser state must be current; verify subsequent updates still flush afterward in `tests/createBrowserHistory.test.ts`. diff --git a/packages/router-core/AGENTS.md b/packages/router-core/AGENTS.md new file mode 100644 index 00000000000..b6f0e669119 --- /dev/null +++ b/packages/router-core/AGENTS.md @@ -0,0 +1,12 @@ +# Router core + +- `RouterCore.update` gates shared route-tree caching with `process.env.NODE_ENV !== 'development'`. `loadRouteChunk` checks promise ownership outside production so obsolete lazy imports cannot overwrite HMR changes. Preserve both behaviors; validate hot updates while imports are pending. +- Route hooks, lifecycle callbacks, and abort listeners can navigate synchronously. Recheck the internal navigation owner after callbacks and awaits before publishing state/assets. Public loader abort signals belong to shared data generations; self-aborting a loader does not discard its result or error. Preserve loader ownership when transferring work between preload, navigation, cache, and background loads; see [`tests/preload-public-signal-lifetime.test.ts`](tests/preload-public-signal-lifetime.test.ts). +- Reusing loader work/data does not reuse `beforeLoad`: each active preload and subsequent navigation reruns it. Keep its returned context separate from reusable route-context contributions; see [`tests/public-preload-lane-contract.test.ts`](tests/public-preload-lane-contract.test.ts). +- Code emitted into HTML (`?script-string` entries and RawStream factories) executes without module scope. Keep it self-contained; pass dependencies explicitly. Validate emitted strings through the existing execution tests after refactors (`RawStream`, `scroll-restoration-script`, `tsr-script-teardown`). +- Before changing SSR streaming or hydration transport, read [`src/ssr/STREAMING.md`](src/ssr/STREAMING.md). Byte ordering, renderer safe points, and completion handling have framework-specific contracts. +- Backpressure tests are excluded from normal unit runs. For buffering/backpressure changes, run the gated suite below. Its memory case requires exposed GC; bypass Nx cache so a previous run with the gate disabled cannot count as validation. + +```sh +RUN_BACKPRESSURE_PERF=1 CI=1 NX_DAEMON=false pnpm nx run @tanstack/router-core:test:unit --outputStyle=stream --skipRemoteCache --skipNxCache -- tests/transformStreamBackpressure.perf.test.ts --pool=forks --execArgv=--expose-gc +``` diff --git a/packages/router-generator/AGENTS.md b/packages/router-generator/AGENTS.md new file mode 100644 index 00000000000..1cc70bf3707 --- /dev/null +++ b/packages/router-generator/AGENTS.md @@ -0,0 +1,6 @@ +# Route generator + +- Preserve the existing `safeFileWrite` path: temporary-file rename, modification-time conflict retry, and post-rename stat. Keep unchanged-write suppression and main/shadow cache rotation; these protect concurrent edits, avoid watcher loops, and discard deleted routes. +- Run the complete `test:unit` target after generator changes. Its runtime phase generates fixture route trees before typechecking their consumers; `test:types` excludes the generator fixtures. +- Add generation cases under `tests/generator//`. Read [`tests/generator.test.ts`](tests/generator.test.ts) for per-case configuration and preprocessing. Change route inputs/configuration or templates, then regenerate route trees and expected snapshots through the test harness. +- `preprocess()` recreates selected route files for `file-modification*` and `custom-scaffolding`. Edit their templates/configuration/preprocessing when changing those inputs; edits to recreated files are overwritten. diff --git a/packages/router-plugin/AGENTS.md b/packages/router-plugin/AGENTS.md new file mode 100644 index 00000000000..e954110819e --- /dev/null +++ b/packages/router-plugin/AGENTS.md @@ -0,0 +1,7 @@ +# Router bundler plugin + +- Preserve one initialization of shared module state across reference and split chunks. Shared extraction includes transitive dependencies and whole destructured declarations; never extract `Route` or bindings that depend on it. The invariant suite in `tests/code-splitter.test.ts` covers these contracts. +- `handleRouteUpdate` is stringified and injected into app modules. Keep runtime dependencies inside the function or explicitly emit them; extracting a helper to plugin module scope breaks the generated function. `tests/handle-route-update.test.ts` executes that emitted code. +- Route HMR rebuilds indexes, clears `resolvePathCache`, replaces lazy-chunk ownership, and refreshes active routes while preserving Fast Refresh component identities. Keep these steps coordinated when adding derived state. +- For code-splitting changes, add inputs under `tests/code-splitter/test-files//`. Regenerate and review reference, virtual, and shared snapshots for every grouping in [`tests/code-splitter.test.ts`](tests/code-splitter.test.ts). +- Check [`tests/constants.ts`](tests/constants.ts) for framework coverage: the snapshot matrix currently covers React and Solid only. Validate affected Vue behavior in Vue router e2e apps. diff --git a/packages/router-ssr-query-core/AGENTS.md b/packages/router-ssr-query-core/AGENTS.md new file mode 100644 index 00000000000..a1799ae8f36 --- /dev/null +++ b/packages/router-ssr-query-core/AGENTS.md @@ -0,0 +1,9 @@ +# SSR query integration + +Register QueryClient teardown at SSR attachment, before loading/dehydration can fail. The transport subscribes after initial dehydration. Render completion flushes pending queries, unsubscribes, and closes the stream. Request cleanup also clears the QueryClient, including canceled streams and late dehydration results; preserve these distinct lifecycles in `tests/index.test.ts`. + +For query retention or cleanup changes, enable the GC tests that normal unit runs skip. `RUN_SSR_GC_TESTS=1` configures forked workers with exposed GC. Bypass Nx cache so a previous run with the gate disabled cannot count as validation. + +```sh +RUN_SSR_GC_TESTS=1 CI=1 NX_DAEMON=false pnpm nx run @tanstack/router-ssr-query-core:test:unit --outputStyle=stream --skipRemoteCache --skipNxCache -- tests/index.test.ts +``` diff --git a/packages/start-client-core/AGENTS.md b/packages/start-client-core/AGENTS.md new file mode 100644 index 00000000000..1e45453ddfb --- /dev/null +++ b/packages/start-client-core/AGENTS.md @@ -0,0 +1,5 @@ +# Start shared/client runtime + +- RawStream RPC responses use binary frames; request arguments/static responses use JSON/base64; SSR uses injected script factories. Pair framing changes in `src/client-rpc/frame-decoder.ts` with `../start-server-core/src/frame-protocol.ts`. For API/serialization changes, validate affected paths through RawStream e2e and encoder/decoder tests. +- Preserve decoder limits and cancellation scopes: canceling one raw stream must leave sibling streams usable; canceling the JSON stream cancels the whole response. Keep malformed/truncated-frame, unread-stream, and split-header coverage in `tests/frame-decoder.test.ts`. +- Context crosses a trust boundary. Use `safeObjectMerge`/`createNullProtoObject`; preserve trusted server middleware context over client-sent context, including SSR and FormData calls. The `server-functions-global-middleware` e2e suite covers that precedence. diff --git a/packages/start-plugin-core/AGENTS.md b/packages/start-plugin-core/AGENTS.md new file mode 100644 index 00000000000..cd73bbf3074 --- /dev/null +++ b/packages/start-plugin-core/AGENTS.md @@ -0,0 +1,7 @@ +# Start compiler and bundler integration + +- Virtual prefixes and query parameters can be part of a module's identity. Preserve them in resolution, loading, and module-cache keys; use `cleanId` only for physical-file matching, diagnostics, and file invalidation. Load compiler dependencies through the bundler host. +- For import-protection changes, read the [shared internals](src/import-protection/INTERNALS.md) and [Vite lifecycle](src/vite/import-protection-plugin/INTERNALS.md). Preserve adapter-specific deferral and survival checks; development has no build-time tree-shaking, and Vite's error/mock modes differ. Cover actual leaks and safe boundaries in cold/warm development and builds. +- Compiler output must match runtime contracts in `start-client-core`, `start-fn-stubs`, and `start-server-core`. For `createServerFn`, cover client, SSR caller, and server-provider output separately in [`tests/createServerFn/createServerFn.test.ts`](tests/createServerFn/createServerFn.test.ts). +- Use a built Start e2e app to validate generated imports and runtime behavior together; compiler snapshots alone do not exercise that integration. +- The shared compiler serves both Vite and Rsbuild. For shared compiler changes, check the relevant adapter suites in `tests/vite/` and `tests/rsbuild/`, plus affected Start e2e modes for build, server-function, or HMR behavior. diff --git a/packages/start-server-core/AGENTS.md b/packages/start-server-core/AGENTS.md new file mode 100644 index 00000000000..ae178f35b60 --- /dev/null +++ b/packages/start-server-core/AGENTS.md @@ -0,0 +1,6 @@ +# Start server runtime + +- Keep request assets out of cached manifests. Assets may mutate in place between head and Scripts rendering; compose their current contents without caching solely by object identity. Asset transforms must preserve the base manifest; request-dependent transforms use `cache: false`. +- Middleware can replace or wrap an SSR response before its body is read. Preserve response ownership with the existing `SsrResponse` helpers and `transferResponseBodyOwnership`; dispose abandoned bodies, and consume/cancel unused clone or tee branches. Rendering completion, serialization completion, and request cleanup are separate lifecycles; read [`../router-core/src/ssr/STREAMING.md`](../router-core/src/ssr/STREAMING.md) before changing them. +- Merge client-sent context with `safeObjectMerge`, placing trusted server middleware context last. Preserve this precedence for JSON, GET, FormData, and direct SSR calls; validate through the `server-functions-global-middleware` e2e suite. +- Changes to `src/frame-protocol.ts` must remain compatible with `../start-client-core/src/client-rpc/frame-decoder.ts`. Keep both packages' framing tests and relevant RawStream e2e coverage. From 07d86bc3a668eb6285fa135dc3b21b06470931b4 Mon Sep 17 00:00:00 2001 From: Manuel Schiller <6340397+schiller-manuel@users.noreply.github.com> Date: Sun, 6 Sep 2026 00:36:35 +0200 Subject: [PATCH 2/9] update --- AGENTS.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index 90a984c4bb2..4c939378357 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -7,6 +7,8 @@ ## Work and validation +- Start bug fixes with a failing regression test that uses only public APIs to reproduce and assert user- or developer-visible behavior. Do not manufacture bugs by mutating internals. Establish whether real usage can reach the failing state before adding runtime handling; simplify handling of states proven unreachable. +- During debugging/prototyping, keep fixes aligned with the PR's intended architecture. If making a test pass requires another flag, counter, copied deadline, or duplicate completion authority, stop and consolidate state and ownership. Remove superseded paths instead of accumulating patches and bundle growth. - Use Node from `.nvmrc` and pnpm from root `package.json`. Install at the root with `CI=1 pnpm install --frozen-lockfile`. - For sandbox-blocked registry/store access, escalate the same command; report the blocker if unavailable. Do not change stores, delete dependencies/lockfiles, use `--force`/`--ignore-scripts`, or weaken workspace trust/build policies to bypass it. - Never manually edit `pnpm-lock.yaml` or any `routeTree.gen.ts`. Regenerate the lockfile with `pnpm install --no-frozen-lockfile` after intentional dependency changes; regenerate route trees through app builds/dev servers or the generator fixture harness. From 2b250125e9bf60cf139147a1bf6cbf2fee9ff24e Mon Sep 17 00:00:00 2001 From: ladybluenotes Date: Sun, 6 Sep 2026 09:56:34 -0700 Subject: [PATCH 3/9] docs: extend agent performance review guidance --- .github/agent-guides/performance.md | 58 ++++++++++++++++++++++++ AGENTS.md | 10 ++-- benchmarks/AGENTS.md | 6 +++ e2e/AGENTS.md | 1 + packages/AGENTS.md | 10 ++++ packages/history/AGENTS.md | 2 + packages/react-start-rsc/AGENTS.md | 4 ++ packages/router-devtools-core/AGENTS.md | 5 ++ packages/router-plugin/AGENTS.md | 1 + packages/start-plugin-core/AGENTS.md | 1 + skills/bundle-size-optimization/SKILL.md | 24 ++++++---- 11 files changed, 109 insertions(+), 13 deletions(-) create mode 100644 .github/agent-guides/performance.md create mode 100644 packages/react-start-rsc/AGENTS.md create mode 100644 packages/router-devtools-core/AGENTS.md diff --git a/.github/agent-guides/performance.md b/.github/agent-guides/performance.md new file mode 100644 index 00000000000..a860d3e7a0f --- /dev/null +++ b/.github/agent-guides/performance.md @@ -0,0 +1,58 @@ +# Performance review + +Use this procedure for Router/Start performance audits and changes to runtime, memory, build, type-inference, or shipped-byte cost. Keep the requested scope; discovery does not authorize another fix or publication. + +## Evidence and coverage + +1. Pin the source revision and name the workload, metric, owning layer, and success condition. Reuse repository fixtures and installed tools. Attribute cost to code, configuration, a dependency, the toolchain, or the harness before choosing a fix; avoid speculative caches, indexes, concurrency, or dependency changes. +2. For multi-surface audits, keep a local coverage row per independently testable requirement: owner, source paths/revision, executed check, evidence, and unresolved boundary. Distinguish inventory/source review, executed mechanism, diagnostic control, verified implementation, and measured user impact. A checked package list does not establish exhaustive path coverage. +3. Establish a supported trigger and baseline before editing. Diagnostic counters must not create the work they claim to observe. Change one variable per control, check equivalent behavior, then run the same protocol on the final implementation. Use profiles or operation counts to explain timing, not a source-level suspicion as proof of a slowdown. +4. Independently inspect each material finding against its owning source and actual check. Promote proven regressions into existing behavioral tests or stable operation/artifact bounds; avoid noisy elapsed-time assertions. Preserve request isolation, side-effect ordering, required waits, and intentional cache/replay lifetimes. +5. Before completion, reconcile every coverage row with the original request. Report findings, rejected candidates, and remaining unverified or blocked surfaces. A diagnostic control is not a production fix; unavailable browser metrics, failed watch integration, or source-only adapter review remain explicit gaps. + +### Finding capture + +Keep non-blocking discoveries in the task's local evidence document. Give each a stable repository/symptom identity, supported trigger, expected/actual behavior, owning source/revision, measurement and limits, existing issue/PR, disposition, next check, and acceptance condition. Label unverified symptoms as observations. Search existing records and open/closed upstream work before creating a duplicate; distinguish an associated PR from a merged and currently verified fix. + +Retain the reason when a finding is rejected or excluded by the user's scope. Capture unrelated evidence without fixing it, adding it to the active task, or posting externally. Resume the current work. A blocker or changed correctness risk must be reported rather than parked. Keep private task evidence and machine paths out of public guidance and PRs. + +## Comparison provenance + +Record baseline/candidate revisions, benchmark source and workload, runtime/tool versions, framework, adapter, build mode, cache state, runner mode, command, raw outputs, aggregation, and noise. Keep scenario selections and measurement flags identical. Use separate results/dist directories for concurrent tasks; avoid timing against unrelated active builds when possible and report remaining contention. + +Separate three conclusions: the benchmark executed, its results are comparable, and the change caused the difference. Check the report's actual base, environment warnings, skipped cases, baseline substitutions, and logs/output artifacts. A successful job or cached result is not fresh execution evidence. Plain memory smoke runs do not supply CodSpeed memory metrics. + +Report operation counts, retained bytes, CPU, elapsed time, throughput, initial payload, and later loading cost separately. Do not convert fewer calls into an unmeasured speedup, raw-byte changes into gzip savings, or a larger RSS sample into a leak. Read sample count, variance/error, and relevant percentiles together; narrow or rerun noisy cases before making a comparative claim. + +## Build-tool checks + +Use the actual adapter and installed compiler version. The official bundle runner covers Vite and explicit Rsbuild scenarios; a Webpack change also needs a real `@tanstack/router-plugin/webpack` consumer. Start's Vite/Rsbuild adapters, direct Rspack, and Router's Webpack adapter are distinct validation surfaces. Inspect existing E2E project scripts/configuration to select executable targets rather than assuming an aggregate benchmark exists. + +For the changed mechanism, cover these boundaries: + +| Boundary | Check | +| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Build work | Profile resolve/load, parse, analysis, reference/virtual/shared generation, maps, and emit separately. Include ignored files and no-transform routes. Check per-transform reuse and AST mutation ownership before introducing a persistent cache. Compare cold builds, warm builds, and no-op rebuilds. | +| Production diagnostics | Distinguish build-host environment checks from conditions emitted into client code. Exercise production compiler mode with host `NODE_ENV` unset and production, plus development behavior. Check emitted diagnostics, absolute paths, and execution; production mode cannot erase an unconditional warning already generated by a plugin. | +| Source maps | Trace the actual loader chain and map consumer. Cover `devtool: false`, a source-map-enabled build, and incoming-map present/absent. Preserve source locations, `sourcesContent`, and downstream diagnostics; measure discarded map construction separately from JS output and wall time. | +| Metadata and assets | Trace indexes, source text, CSS, and stats to their readers and enabling options. Verify disabled/enabled consumers and output equivalence before removing work. Avoid making a second index or capture that no consumer reads. | +| Identity and invalidation | Exercise independent compiler instances, concurrent builds, aliases/query variants, route edits/add/remove, shared dependency and configuration changes, and error recovery. Preserve watch dependencies, module identity, HMR state, and cleanup; unchanged inputs should avoid unnecessary work without hiding changed inputs. | +| Shipped graph | Inspect resolved exports/entry conditions, ESM/CJS paths, module reasons, shared/async chunks, and concatenation bailouts. Verify unused code disappears while required CSS/global effects remain and shared modules initialize once. Measure initial and async payloads and execute navigation; do not infer all bundlers honor a Rolldown annotation. | + +Use the same fixture/configuration for baseline and candidate; vary one option deliberately when isolating a cause. Validate annotation and loader behavior from the installed dependency source or matching official documentation. A snapshot or one-shot build cannot establish watch/HMR correctness. Restore fixture edits and close owned watchers/compilers after failures. An environment failure such as `EMFILE` is a coverage blocker, not a library performance finding. + +Reference: Webpack's [build-performance](https://webpack.js.org/guides/build-performance/), [mode](https://webpack.js.org/configuration/mode/), and [tree-shaking](https://webpack.js.org/guides/tree-shaking/) documentation. + +## Browser and server measurements + +Verify the fixture before measuring: production versus development server, framework bootstrap/hydration support, native scheduling and browser capabilities, actual response consumption, and assertions that the intended path ran. A server-CPU fixture may not be a valid browser-hydration app. Release harness-owned routers, DOM references, console records, and request bodies before drawing retention conclusions; inspect reachability/retainers or repeated bounded workloads. + +For changed hydration/RPC/streaming behavior, exercise real HTTP/browser consumption, later navigation, cancellation, errors, and disposal as applicable. Keep CPU, Router `onRendered`, hydration completion, presentation/INP, fetch attempts, transferred bytes, and throughput distinct. Name tested engines; Playwright WebKit is not branded Safari. Reuse working app bootstrap and error-checking fixtures, correct harness defects, and rerun before attributing a failure to the product. + +## TypeScript cost + +Use the repository typecheck/project-reference setup and a representative consumer route tree at increasing sizes. Keep TypeScript version, compiler configuration, fixture, and cold/warm state identical between implementations. Preserve inference assertions and record check time, compiler work, and memory where the installed compiler exposes them; report unavailable measurements. Do not pass individual source files to `tsc` and thereby bypass project configuration. A passing typecheck does not establish unchanged compiler/editor performance. + +## Reviewing changes to this guidance + +Check that a representative task reaches the right procedure, an unrelated task does not, and a failed or unavailable check remains explicit. For behavioral validation, replay bounded tasks with the guidance revision and record the host/tools, selected checks, actions, result, and human corrections. Link and text checks prove document structure only; skill availability or reviewer count does not prove the agent applied the procedure or made fewer mistakes. diff --git a/AGENTS.md b/AGENTS.md index 4c939378357..a4d5df071a6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,17 +2,20 @@ ## Scope +- Read [CONTRIBUTING.md](CONTRIBUTING.md) before analyzing or changing this repository; its contribution and PR requirements apply. - Read applicable nested `AGENTS.md` files before editing or testing a subtree. - Shared routing belongs in `packages/router-core`; check React, Solid, and Vue bindings for shared changes. Shared Start runtime/build logic lives in `packages/start-*-core`. +- Before writes, inspect dirty files and existing worktrees/tasks. Preserve others' work, keep scratch checkouts outside the workspace's project-discovery tree, and isolate benchmark results/build outputs. Stop only processes owned by the task. Commit, push, and publication require user authorization; a verification procedure does not grant it. ## Work and validation - Start bug fixes with a failing regression test that uses only public APIs to reproduce and assert user- or developer-visible behavior. Do not manufacture bugs by mutating internals. Establish whether real usage can reach the failing state before adding runtime handling; simplify handling of states proven unreachable. +- Diagnostic counters and profiles may observe that supported path without changing its behavior. A diagnostic control proves only the measured mechanism; verify the final implementation through public behavior or the supported compiler-plugin boundary. - During debugging/prototyping, keep fixes aligned with the PR's intended architecture. If making a test pass requires another flag, counter, copied deadline, or duplicate completion authority, stop and consolidate state and ownership. Remove superseded paths instead of accumulating patches and bundle growth. - Use Node from `.nvmrc` and pnpm from root `package.json`. Install at the root with `CI=1 pnpm install --frozen-lockfile`. - For sandbox-blocked registry/store access, escalate the same command; report the blocker if unavailable. Do not change stores, delete dependencies/lockfiles, use `--force`/`--ignore-scripts`, or weaken workspace trust/build policies to bypass it. - Never manually edit `pnpm-lock.yaml` or any `routeTree.gen.ts`. Regenerate the lockfile with `pnpm install --no-frozen-lockfile` after intentional dependency changes; regenerate route trees through app builds/dev servers or the generator fixture harness. -- Always use control-flow braces. Run `pnpm format` before validation. +- Always use control-flow braces. Format changed files with the repository's Prettier configuration before validation; root `pnpm format` writes across the checkout, so reserve it for an authorized repository-wide formatting pass. - Use Nx: workspace imports consume built packages, so direct runners can test stale dependencies. Inspect resolved targets; some are inferred. ```sh @@ -33,5 +36,6 @@ CI=1 NX_DAEMON=false pnpm nx run : --outputStyle=stream --skipR ## Performance and final bundle pass -- For runtime/memory changes, compare identical baseline/candidate workloads using the relevant [benchmark guide](benchmarks/AGENTS.md): client navigation, Start SSR, or memory. Add coverage for unmeasured mechanisms. -- **Last phase for every change affecting emitted client JavaScript**, including core/build transforms: complete the full [bundle-size-optimization skill](.agents/skills/bundle-size-optimization/SKILL.md) after correctness and performance validation. The full workflow is mandatory. +- For audits and performance changes, follow the [evidence and coverage procedure](.github/agent-guides/performance.md). Keep source review, executed checks, diagnostic controls, and measured user impact distinct; retain unresolved coverage and unrelated findings without expanding the task. +- For runtime, memory, build, or type-inference cost changes, select the relevant [benchmark guide](benchmarks/AGENTS.md). Compare identical baseline/candidate workloads and add coverage when existing scenarios miss the changed mechanism. +- **Last phase for every change affecting emitted client JavaScript**, including core/build transforms: complete the full [bundle-size-optimization skill](skills/bundle-size-optimization/SKILL.md) after correctness and performance validation. The full workflow is mandatory within the accepted change scope. diff --git a/benchmarks/AGENTS.md b/benchmarks/AGENTS.md index 268e23d9cc3..c1367210450 100644 --- a/benchmarks/AGENTS.md +++ b/benchmarks/AGENTS.md @@ -10,4 +10,10 @@ Read the relevant README before running benchmarks. Compare baseline and candida Use `test:perf:` through Nx. Plain memory runs only smoke-test; memory metrics require CodSpeed. Client navigation benchmarks do not cover Start hydration or client server-function calls. +For build/compiler work, follow the [build-tool checks](../.github/agent-guides/performance.md#build-tool-checks); there is no aggregate build-CPU benchmark in the table above. For inference cost, use the [TypeScript measurement procedure](../.github/agent-guides/performance.md#typescript-cost). For hydration, RPC, paint, or real HTTP behavior, use the affected browser/app workload and validate its [fixture and metric boundaries](../.github/agent-guides/performance.md#browser-and-server-measurements). + +Before interpreting CodSpeed or local results, record the [comparison provenance](../.github/agent-guides/performance.md#comparison-provenance). Successful execution, comparable results, and causation are separate conclusions; skipped or baseline-substituted cases do not count as fresh candidate evidence. + For bundle-size work, follow the full optimization skill required by the root guide. Full API fixtures are maintained manually: when public hooks/components change, update matching `bundle-size/scenarios/*-full/src/routes/__root.tsx` fixtures. Save baseline results separately because each run overwrites `bundle-size/results/current.json`; report final gzip deltas and explain remaining growth. + +The official bundle runner covers Vite and explicit Rsbuild scenarios, not Webpack. For changes affecting the Webpack adapter or shared splitting/DCE behavior, also measure a real `@tanstack/router-plugin/webpack` consumer using the build-tool checks. Do not describe the official matrix as coverage of every bundler. diff --git a/e2e/AGENTS.md b/e2e/AGENTS.md index f373ecba4f3..7fd4fa2d44b 100644 --- a/e2e/AGENTS.md +++ b/e2e/AGENTS.md @@ -7,3 +7,4 @@ - For new browser tests, reuse the local error-checking fixture. The shared `test` from `@tanstack/router-e2e-utils` fails on unexpected `console.error`, including hydration errors; allow only expected errors with `whitelistErrors`. - HMR/watch tests edit real app sources. Reuse existing edit/restore helpers such as `createHmrFileEditor`, restore edits in teardown even after failures, and preserve the suites' single-worker and Nx `parallelism: false` settings. - `@tanstack/router-e2e-utils:test:unit` is a placeholder. Validate helper changes through consuming e2e projects. +- Before reusing a CPU benchmark as a browser/SSR fixture, verify framework bootstrap, actual hydration, native scheduling, response consumption, and cleanup against a working app. Correct and rerun harness failures before attributing them to Router/Start; see [browser and server measurements](../.github/agent-guides/performance.md#browser-and-server-measurements). diff --git a/packages/AGENTS.md b/packages/AGENTS.md index 0c63c3a15c6..a139fd3f279 100644 --- a/packages/AGENTS.md +++ b/packages/AGENTS.md @@ -7,6 +7,15 @@ - Navigation/render identity is more than href or route membership: same-href navigations can have different history keys, and suspended renders must acknowledge only their own match publication. Preserve those identity checks when optimizing stores or render notifications. - In navigation params/search, an explicit `undefined` can clear an inherited value; an absent key can inherit it. Preserve that distinction when comparing or memoizing options; React's `useValueStable` uses `deepEqual` with `ignoreUndefined: false`. +## Lifecycle and runtime cost + +- For optional features, measure disabled/idle work as well as enabled work. Gate unnecessary allocation, subscriptions, scans, and decoding before setup where safe; a handler that immediately returns still has registration cost. Preserve user handlers and off/on/off transitions. +- For listeners, subscriptions, observers, timers, caches, and patched globals, name the owner and cleanup path. Test overlapping instances, both disposal orders, replacement, and events after disposal. Verify which owner receives subsequent work, not just whether cleanup ran. Apply this to scroll restoration across router lifetimes as well as framework unmounts. +- For hydration changes, cover initial SSR hydration, CSR, later-mounted boundaries, unrelated ancestor updates, intentional option changes, and disposal. Define which event starts or resets a deadline; test inline/equivalent strategy values and browsers with and without the relevant scheduling API. Preserve intentional pending-UI waits and loader/context ordering. +- For head, Scripts, Asset, or injected-code changes, assert execution counts through attribute/content combinations, hydration, navigation, and removal. Final markup can hide duplicate side effects; distinguish elements, fetch attempts, transferred bytes, and executions. +- For repeated scans or recursive transforms, exercise increasing input sizes and supported empty, deep, wide, shared-reference, and cyclic inputs. Count work separately from elapsed time; preserve ordering, identity, and serialization behavior before retaining a faster implementation. +- For devtools core or wrapper changes, read the [devtools guide](router-devtools-core/AGENTS.md), including router-prop replacement and closed-panel work. + ## Tests - Inspect test commands: several `test:unit` scripts immediately exit or only echo a message. For these packages, validate behavior in the shared core or consuming e2e app; a successful placeholder target provides no unit coverage. @@ -14,3 +23,4 @@ - For single-command Vitest targets, append filters after `--`, e.g. `-- tests/link.test.tsx -t "preloading"`. - `solid-router` and `router-generator` chain two Vitest commands in `test:unit`. Trailing file/name/update arguments reach only the final command. Run the full target for verification: Solid needs browser and server suites; the generator needs fixture generation followed by typechecking. - For public type changes, add `*.test-d.*` inference regressions and run both unit and type targets. React/Solid Router's TypeScript compiler matrix checks `src`; Vitest runs their inference tests. For changes to inference cost, follow the [type-safety performance guidance](../docs/router/guide/type-safety.md#performance-recommendations); `test:perf` measures runtime, not compiler cost. +- For measured inference changes, also follow the [TypeScript cost procedure](../.github/agent-guides/performance.md#typescript-cost); a passing typecheck is not evidence of unchanged compiler or editor performance. diff --git a/packages/history/AGENTS.md b/packages/history/AGENTS.md index 509da6a4ec7..e27df65b406 100644 --- a/packages/history/AGENTS.md +++ b/packages/history/AGENTS.md @@ -1,3 +1,5 @@ # History Accepted browser push/replace operations update the in-memory location immediately and batch native writes into a microtask. Preserve coalescing: the latest href/state wins, but any queued push wins over replaces. Use `history.flush()` when native browser state must be current; verify subsequent updates still flush afterward in `tests/createBrowserHistory.test.ts`. + +When changing native history wrappers or teardown, exercise two histories sharing one window, destroy them in both orders, and dispatch later native updates. A surviving history must keep receiving updates; destroying the second must not restore a wrapper owned by the already-destroyed first history. diff --git a/packages/react-start-rsc/AGENTS.md b/packages/react-start-rsc/AGENTS.md new file mode 100644 index 00000000000..40dda5bfb55 --- /dev/null +++ b/packages/react-start-rsc/AGENTS.md @@ -0,0 +1,4 @@ +# React Start RSC + +- For diagnostic slot previews and recursive payload transforms, validate supported shared-reference, deep, and cyclic inputs before timing them. Traversal state must actually bound repeated work; any reuse must preserve the consumer's serialization contract. +- Gate development diagnostics before constructing payloads, then measure their development cost. Treat replay needed by SSR and later serialization as a required lifetime until evidence shows retention beyond it; validate cancellation and request cleanup before calling it a leak. diff --git a/packages/router-devtools-core/AGENTS.md b/packages/router-devtools-core/AGENTS.md new file mode 100644 index 00000000000..2ae07c5eb1b --- /dev/null +++ b/packages/router-devtools-core/AGENTS.md @@ -0,0 +1,5 @@ +# Router devtools + +- Measure closed-panel setup and idle callbacks with large route/cache state as well as open-panel interactions. Development-only code still has a user-visible cost; distinguish default development exports from explicit production opt-ins. +- Count style-factory and template-processing work separately from inserted CSS. Before reusing styles, preserve document/shadow-root ownership, theme updates, HMR, and target disposal; stylesheet deduplication alone does not prove the construction work is absent. +- For React/Solid/Vue wrappers, replace router A with B while mounted, navigate each, then unmount and update again. Only the current router should drive the panel, and unmounted wrappers should receive no further work. Test floating and standalone wrappers when both are affected. diff --git a/packages/router-plugin/AGENTS.md b/packages/router-plugin/AGENTS.md index e954110819e..041bacea12b 100644 --- a/packages/router-plugin/AGENTS.md +++ b/packages/router-plugin/AGENTS.md @@ -5,3 +5,4 @@ - Route HMR rebuilds indexes, clears `resolvePathCache`, replaces lazy-chunk ownership, and refreshes active routes while preserving Fast Refresh component identities. Keep these steps coordinated when adding derived state. - For code-splitting changes, add inputs under `tests/code-splitter/test-files//`. Regenerate and review reference, virtual, and shared snapshots for every grouping in [`tests/code-splitter.test.ts`](tests/code-splitter.test.ts). - Check [`tests/constants.ts`](tests/constants.ts) for framework coverage: the snapshot matrix currently covers React and Solid only. Validate affected Vue behavior in Vue router e2e apps. +- For transform/build performance, source maps, production diagnostics, shared module state, or Webpack/Rspack behavior, follow the [build-tool checks](../../.github/agent-guides/performance.md#build-tool-checks). Snapshot output and Vite results do not establish actual Webpack loader, mode, cache, or chunk behavior. diff --git a/packages/start-plugin-core/AGENTS.md b/packages/start-plugin-core/AGENTS.md index cd73bbf3074..50dfd9cab29 100644 --- a/packages/start-plugin-core/AGENTS.md +++ b/packages/start-plugin-core/AGENTS.md @@ -5,3 +5,4 @@ - Compiler output must match runtime contracts in `start-client-core`, `start-fn-stubs`, and `start-server-core`. For `createServerFn`, cover client, SSR caller, and server-provider output separately in [`tests/createServerFn/createServerFn.test.ts`](tests/createServerFn/createServerFn.test.ts). - Use a built Start e2e app to validate generated imports and runtime behavior together; compiler snapshots alone do not exercise that integration. - The shared compiler serves both Vite and Rsbuild. For shared compiler changes, check the relevant adapter suites in `tests/vite/` and `tests/rsbuild/`, plus affected Start e2e modes for build, server-function, or HMR behavior. +- For build-time performance, use the [build-tool checks](../../.github/agent-guides/performance.md#build-tool-checks). Trace normalized indexes, asset text, CSS content, and other metadata from producer to actual consumers before retaining or removing them. Test the consuming feature both disabled and enabled, including inline CSS, and preserve full manifest/output behavior in Vite and Rsbuild. diff --git a/skills/bundle-size-optimization/SKILL.md b/skills/bundle-size-optimization/SKILL.md index d0f1ec574e5..6b34847c9cf 100644 --- a/skills/bundle-size-optimization/SKILL.md +++ b/skills/bundle-size-optimization/SKILL.md @@ -1,6 +1,6 @@ --- name: bundle-size-optimization -description: Use when working in this repository on JS bundle size, gzip regressions, benchmark scenarios, source attribution, treeshaking, dead code elimination, or Rolldown annotations. +description: Use when working in this repository on JS bundle size, gzip regressions, benchmark scenarios, source attribution, treeshaking, dead code elimination, or bundler annotations, including Webpack. --- # Bundle Size Optimization @@ -10,6 +10,10 @@ description: Use when working in this repository on JS bundle size, gzip regress Optimize measured client bundles, not source text. The source of truth is `@benchmarks/bundle-size:build`, `benchmarks/bundle-size/results/current.json`, and emitted JS in `benchmarks/bundle-size/dist/`. Use `benchmark:bundle-size:run` for local iterations. It uses the same measurement script and builds the selected packages through Nx. +The full measurement, attribution, and regression workflow applies within the accepted change scope. Optimize that change without acquiring unrelated work; larger architectural changes require matching user authority. Preserve others' edits and obtain explicit authorization for commits/pushes. Baseline comparison does not require committing or stashing the working implementation. + +For audits, comparison provenance, build-time cost, and adapter-specific verification, follow the [performance review guide](../../.github/agent-guides/performance.md). The official runner does not include Webpack: changes affecting that adapter or shared splitting/DCE also require its real-consumer checks. Do not substitute a Vite/Rsbuild result for Webpack evidence. + ## Commands | Need | Command | @@ -115,16 +119,14 @@ After each candidate, run focused perf benchmarks before bundle measurement. Rej When done optimizing: -1. Spawn 5 subagents to review the optimization diff against existing tests. Ask each to identify missing unit test cases that could fail with the current changes or newly uncovered edge cases, and missing performance benchmarks that could hide regressions. +1. Review the optimization diff against existing tests for missing behavior cases, newly uncovered edge cases, and performance coverage. Use independent reviewers for bounded questions when the task permits and the host supports them; reconcile their findings against source and checks. Without reviewers, perform a separate review pass and state that independence was not verified. 2. If a possible regression is unclear, ask the user or explore the codebase until the expected behavior is clear. -3. Use their input to add focused unit tests and benchmarks. -4. Commit only the tests/benchmarks/supporting test-script changes. -5. Stash the implementation changes. -6. Run tests, performance benchmarks, and the relevant bundle-size measurement, then write BEFORE results to `RESULT-optimization-{topic}.md`. -7. Pop the implementation changes. -8. Run the same tests, performance benchmarks, and bundle-size measurement, then append AFTER results to the same file. -9. When reviewing benchmark output, consider statistical quality: standard deviation, margin of error, variance/noise, sample count, and percentiles. Re-run or narrow conclusions when results are noisy. -10. Compare BEFORE and AFTER. If anything regressed, iterate until green or revert the regression. +3. Use the review findings to add focused unit tests and benchmarks. +4. Prepare isolated baseline and candidate worktrees/snapshots with the same tests and benchmark harness, differing only in the implementation under comparison. Build each snapshot's dependencies through Nx; use separate results/dist paths and preserve the working checkout. +5. Run tests, performance benchmarks, and the relevant bundle-size measurement on the baseline, then write BEFORE results and revision/environment details to local `RESULT-optimization-{topic}.md`. +6. Run the same checks on the candidate, then append AFTER results to the same file. Keep raw outputs and identify skipped/cached checks. Publish task evidence only when requested. +7. When reviewing benchmark output, consider statistical quality: standard deviation, margin of error, variance/noise, sample count, and percentiles. Re-run or narrow conclusions when results are noisy. +8. Compare BEFORE and AFTER. If anything regressed, iterate within the accepted scope or revert the task-owned regression. Recheck the final composed implementation after changes. Useful patterns: remove prod-only strings, remove unused exports, flatten wrappers, inline one-use helpers, avoid duplicate literals, improve treeshaking boundaries, simplify branches after preserving behavior. @@ -132,6 +134,8 @@ Useful patterns: remove prod-only strings, remove unused exports, flatten wrappe Rolldown removes code only when unused and side-effect-free. Property reads may trigger getters; storage/global access can observe or throw. +Validate annotation support and emitted effects with the installed bundler/minifier. For Webpack, use the [shipped-graph checks](../../.github/agent-guides/performance.md#build-tool-checks), including unused-import removal, required CSS/global effects, and one-time initialization across eager/lazy chunks. + | Annotation | Valid | Unsafe | | ----------------------------------------- | --------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | | `/* @__PURE__ */ call()` | immediately before a call/new expression whose unused result can be dropped | declarations, property reads, setup, storage, DOM/history/listener code | From 3368c3d014f900bfd75bf297eee35d1e0672c9b8 Mon Sep 17 00:00:00 2001 From: ladybluenotes Date: Sun, 6 Sep 2026 10:01:41 -0700 Subject: [PATCH 4/9] docs: consolidate performance guidance in shared guide --- .github/agent-guides/performance.md | 26 ++++++++++++++++++++++++- AGENTS.md | 3 +-- benchmarks/AGENTS.md | 6 ------ e2e/AGENTS.md | 1 - packages/AGENTS.md | 10 ---------- packages/history/AGENTS.md | 2 -- packages/react-start-rsc/AGENTS.md | 4 ---- packages/router-devtools-core/AGENTS.md | 5 ----- packages/router-plugin/AGENTS.md | 1 - packages/start-plugin-core/AGENTS.md | 1 - 10 files changed, 26 insertions(+), 33 deletions(-) delete mode 100644 packages/react-start-rsc/AGENTS.md delete mode 100644 packages/router-devtools-core/AGENTS.md diff --git a/.github/agent-guides/performance.md b/.github/agent-guides/performance.md index a860d3e7a0f..30d24a0c649 100644 --- a/.github/agent-guides/performance.md +++ b/.github/agent-guides/performance.md @@ -2,6 +2,8 @@ Use this procedure for Router/Start performance audits and changes to runtime, memory, build, type-inference, or shipped-byte cost. Keep the requested scope; discovery does not authorize another fix or publication. +Select the existing workload for [client navigation](../../benchmarks/client-nav/README.md), [Start SSR](../../benchmarks/ssr/README.md), or [memory](../../benchmarks/memory/README.md). Compare identical baseline/candidate workloads and add coverage when existing scenarios miss the changed mechanism. There is no aggregate build-CPU benchmark in that set: use the build-tool checks below. For hydration, RPC, paint, or real HTTP behavior, use the affected browser/app workload and the browser/server measurement procedure. For inference cost, use the TypeScript procedure. + ## Evidence and coverage 1. Pin the source revision and name the workload, metric, owning layer, and success condition. Reuse repository fixtures and installed tools. Attribute cost to code, configuration, a dependency, the toolchain, or the harness before choosing a fix; avoid speculative caches, indexes, concurrency, or dependency changes. @@ -26,7 +28,7 @@ Report operation counts, retained bytes, CPU, elapsed time, throughput, initial ## Build-tool checks -Use the actual adapter and installed compiler version. The official bundle runner covers Vite and explicit Rsbuild scenarios; a Webpack change also needs a real `@tanstack/router-plugin/webpack` consumer. Start's Vite/Rsbuild adapters, direct Rspack, and Router's Webpack adapter are distinct validation surfaces. Inspect existing E2E project scripts/configuration to select executable targets rather than assuming an aggregate benchmark exists. +Use the actual adapter and installed compiler version. For transform/build performance, source maps, production diagnostics, shared module state, or Webpack/Rspack behavior, exercise the affected adapter. The official bundle runner covers Vite and explicit Rsbuild scenarios; changes affecting Webpack or shared splitting/DCE also need a real `@tanstack/router-plugin/webpack` consumer. Start's Vite/Rsbuild adapters, direct Rspack, and Router's Webpack adapter are distinct validation surfaces. Snapshot output and Vite results do not establish actual Webpack loader, mode, cache, or chunk behavior. Inspect existing E2E project scripts/configuration to select executable targets rather than assuming an aggregate benchmark exists. For the changed mechanism, cover these boundaries: @@ -43,6 +45,28 @@ Use the same fixture/configuration for baseline and candidate; vary one option d Reference: Webpack's [build-performance](https://webpack.js.org/guides/build-performance/), [mode](https://webpack.js.org/configuration/mode/), and [tree-shaking](https://webpack.js.org/guides/tree-shaking/) documentation. +For Start metadata changes, trace normalized indexes, asset text, CSS content, and other metadata from producer to actual consumers before retaining or removing them. Test the consuming feature both disabled and enabled, including inline CSS, and preserve full manifest/output behavior in Vite and Rsbuild. + +## Runtime and lifecycle checks + +- For optional features, measure disabled/idle work as well as enabled work. Gate unnecessary allocation, subscriptions, scans, and decoding before setup where safe; a handler that immediately returns still has registration cost. Preserve user handlers and off/on/off transitions. +- For listeners, subscriptions, observers, timers, caches, and patched globals, name the owner and cleanup path. Test overlapping instances, both disposal orders, replacement, and events after disposal. Verify which owner receives subsequent work, not just whether cleanup ran. Apply this to scroll restoration across router lifetimes as well as framework unmounts. +- For native history wrappers or teardown, exercise two histories sharing one window, destroy them in both orders, and dispatch later native updates. A surviving history must keep receiving updates; destroying the second must not restore a wrapper owned by the already-destroyed first history. +- For hydration changes, cover initial SSR hydration, CSR, later-mounted boundaries, unrelated ancestor updates, intentional option changes, and disposal. Define which event starts or resets a deadline; test inline/equivalent strategy values and browsers with and without the relevant scheduling API. Preserve intentional pending-UI waits and loader/context ordering. +- For head, Scripts, Asset, or injected-code changes, assert execution counts through attribute/content combinations, hydration, navigation, and removal. Final markup can hide duplicate side effects; distinguish elements, fetch attempts, transferred bytes, and executions. +- For repeated scans or recursive transforms, exercise increasing input sizes and supported empty, deep, wide, shared-reference, and cyclic inputs. Count work separately from elapsed time; preserve ordering, identity, and serialization behavior before retaining a faster implementation. + +### Router devtools + +- Measure closed-panel setup and idle callbacks with large route/cache state as well as open-panel interactions. Development-only code still has a user-visible cost; distinguish default development exports from explicit production opt-ins. +- Count style-factory and template-processing work separately from inserted CSS. Before reusing styles, preserve document/shadow-root ownership, theme updates, HMR, and target disposal; stylesheet deduplication alone does not prove the construction work is absent. +- For React/Solid/Vue wrappers, replace router A with B while mounted, navigate each, then unmount and update again. Only the current router should drive the panel, and unmounted wrappers should receive no further work. Test floating and standalone wrappers when both are affected. + +### React Start RSC + +- For diagnostic slot previews and recursive payload transforms, validate supported shared-reference, deep, and cyclic inputs before timing them. Traversal state must actually bound repeated work; any reuse must preserve the consumer's serialization contract. +- Gate development diagnostics before constructing payloads, then measure their development cost. Treat replay needed by SSR and later serialization as a required lifetime until evidence shows retention beyond it; validate cancellation and request cleanup before calling it a leak. + ## Browser and server measurements Verify the fixture before measuring: production versus development server, framework bootstrap/hydration support, native scheduling and browser capabilities, actual response consumption, and assertions that the intended path ran. A server-CPU fixture may not be a valid browser-hydration app. Release harness-owned routers, DOM references, console records, and request bodies before drawing retention conclusions; inspect reachability/retainers or repeated bounded workloads. diff --git a/AGENTS.md b/AGENTS.md index a4d5df071a6..ce64db44b7d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -36,6 +36,5 @@ CI=1 NX_DAEMON=false pnpm nx run : --outputStyle=stream --skipR ## Performance and final bundle pass -- For audits and performance changes, follow the [evidence and coverage procedure](.github/agent-guides/performance.md). Keep source review, executed checks, diagnostic controls, and measured user impact distinct; retain unresolved coverage and unrelated findings without expanding the task. -- For runtime, memory, build, or type-inference cost changes, select the relevant [benchmark guide](benchmarks/AGENTS.md). Compare identical baseline/candidate workloads and add coverage when existing scenarios miss the changed mechanism. +- For performance audits or changes to runtime lifecycles, memory, build, type inference, or shipped bytes, follow the [performance review guide](.github/agent-guides/performance.md). It owns workload selection, lifecycle checks, Webpack/build checks, and evidence capture; keep diagnostic controls and measured user impact distinct. - **Last phase for every change affecting emitted client JavaScript**, including core/build transforms: complete the full [bundle-size-optimization skill](skills/bundle-size-optimization/SKILL.md) after correctness and performance validation. The full workflow is mandatory within the accepted change scope. diff --git a/benchmarks/AGENTS.md b/benchmarks/AGENTS.md index c1367210450..268e23d9cc3 100644 --- a/benchmarks/AGENTS.md +++ b/benchmarks/AGENTS.md @@ -10,10 +10,4 @@ Read the relevant README before running benchmarks. Compare baseline and candida Use `test:perf:` through Nx. Plain memory runs only smoke-test; memory metrics require CodSpeed. Client navigation benchmarks do not cover Start hydration or client server-function calls. -For build/compiler work, follow the [build-tool checks](../.github/agent-guides/performance.md#build-tool-checks); there is no aggregate build-CPU benchmark in the table above. For inference cost, use the [TypeScript measurement procedure](../.github/agent-guides/performance.md#typescript-cost). For hydration, RPC, paint, or real HTTP behavior, use the affected browser/app workload and validate its [fixture and metric boundaries](../.github/agent-guides/performance.md#browser-and-server-measurements). - -Before interpreting CodSpeed or local results, record the [comparison provenance](../.github/agent-guides/performance.md#comparison-provenance). Successful execution, comparable results, and causation are separate conclusions; skipped or baseline-substituted cases do not count as fresh candidate evidence. - For bundle-size work, follow the full optimization skill required by the root guide. Full API fixtures are maintained manually: when public hooks/components change, update matching `bundle-size/scenarios/*-full/src/routes/__root.tsx` fixtures. Save baseline results separately because each run overwrites `bundle-size/results/current.json`; report final gzip deltas and explain remaining growth. - -The official bundle runner covers Vite and explicit Rsbuild scenarios, not Webpack. For changes affecting the Webpack adapter or shared splitting/DCE behavior, also measure a real `@tanstack/router-plugin/webpack` consumer using the build-tool checks. Do not describe the official matrix as coverage of every bundler. diff --git a/e2e/AGENTS.md b/e2e/AGENTS.md index 7fd4fa2d44b..f373ecba4f3 100644 --- a/e2e/AGENTS.md +++ b/e2e/AGENTS.md @@ -7,4 +7,3 @@ - For new browser tests, reuse the local error-checking fixture. The shared `test` from `@tanstack/router-e2e-utils` fails on unexpected `console.error`, including hydration errors; allow only expected errors with `whitelistErrors`. - HMR/watch tests edit real app sources. Reuse existing edit/restore helpers such as `createHmrFileEditor`, restore edits in teardown even after failures, and preserve the suites' single-worker and Nx `parallelism: false` settings. - `@tanstack/router-e2e-utils:test:unit` is a placeholder. Validate helper changes through consuming e2e projects. -- Before reusing a CPU benchmark as a browser/SSR fixture, verify framework bootstrap, actual hydration, native scheduling, response consumption, and cleanup against a working app. Correct and rerun harness failures before attributing them to Router/Start; see [browser and server measurements](../.github/agent-guides/performance.md#browser-and-server-measurements). diff --git a/packages/AGENTS.md b/packages/AGENTS.md index a139fd3f279..0c63c3a15c6 100644 --- a/packages/AGENTS.md +++ b/packages/AGENTS.md @@ -7,15 +7,6 @@ - Navigation/render identity is more than href or route membership: same-href navigations can have different history keys, and suspended renders must acknowledge only their own match publication. Preserve those identity checks when optimizing stores or render notifications. - In navigation params/search, an explicit `undefined` can clear an inherited value; an absent key can inherit it. Preserve that distinction when comparing or memoizing options; React's `useValueStable` uses `deepEqual` with `ignoreUndefined: false`. -## Lifecycle and runtime cost - -- For optional features, measure disabled/idle work as well as enabled work. Gate unnecessary allocation, subscriptions, scans, and decoding before setup where safe; a handler that immediately returns still has registration cost. Preserve user handlers and off/on/off transitions. -- For listeners, subscriptions, observers, timers, caches, and patched globals, name the owner and cleanup path. Test overlapping instances, both disposal orders, replacement, and events after disposal. Verify which owner receives subsequent work, not just whether cleanup ran. Apply this to scroll restoration across router lifetimes as well as framework unmounts. -- For hydration changes, cover initial SSR hydration, CSR, later-mounted boundaries, unrelated ancestor updates, intentional option changes, and disposal. Define which event starts or resets a deadline; test inline/equivalent strategy values and browsers with and without the relevant scheduling API. Preserve intentional pending-UI waits and loader/context ordering. -- For head, Scripts, Asset, or injected-code changes, assert execution counts through attribute/content combinations, hydration, navigation, and removal. Final markup can hide duplicate side effects; distinguish elements, fetch attempts, transferred bytes, and executions. -- For repeated scans or recursive transforms, exercise increasing input sizes and supported empty, deep, wide, shared-reference, and cyclic inputs. Count work separately from elapsed time; preserve ordering, identity, and serialization behavior before retaining a faster implementation. -- For devtools core or wrapper changes, read the [devtools guide](router-devtools-core/AGENTS.md), including router-prop replacement and closed-panel work. - ## Tests - Inspect test commands: several `test:unit` scripts immediately exit or only echo a message. For these packages, validate behavior in the shared core or consuming e2e app; a successful placeholder target provides no unit coverage. @@ -23,4 +14,3 @@ - For single-command Vitest targets, append filters after `--`, e.g. `-- tests/link.test.tsx -t "preloading"`. - `solid-router` and `router-generator` chain two Vitest commands in `test:unit`. Trailing file/name/update arguments reach only the final command. Run the full target for verification: Solid needs browser and server suites; the generator needs fixture generation followed by typechecking. - For public type changes, add `*.test-d.*` inference regressions and run both unit and type targets. React/Solid Router's TypeScript compiler matrix checks `src`; Vitest runs their inference tests. For changes to inference cost, follow the [type-safety performance guidance](../docs/router/guide/type-safety.md#performance-recommendations); `test:perf` measures runtime, not compiler cost. -- For measured inference changes, also follow the [TypeScript cost procedure](../.github/agent-guides/performance.md#typescript-cost); a passing typecheck is not evidence of unchanged compiler or editor performance. diff --git a/packages/history/AGENTS.md b/packages/history/AGENTS.md index e27df65b406..509da6a4ec7 100644 --- a/packages/history/AGENTS.md +++ b/packages/history/AGENTS.md @@ -1,5 +1,3 @@ # History Accepted browser push/replace operations update the in-memory location immediately and batch native writes into a microtask. Preserve coalescing: the latest href/state wins, but any queued push wins over replaces. Use `history.flush()` when native browser state must be current; verify subsequent updates still flush afterward in `tests/createBrowserHistory.test.ts`. - -When changing native history wrappers or teardown, exercise two histories sharing one window, destroy them in both orders, and dispatch later native updates. A surviving history must keep receiving updates; destroying the second must not restore a wrapper owned by the already-destroyed first history. diff --git a/packages/react-start-rsc/AGENTS.md b/packages/react-start-rsc/AGENTS.md deleted file mode 100644 index 40dda5bfb55..00000000000 --- a/packages/react-start-rsc/AGENTS.md +++ /dev/null @@ -1,4 +0,0 @@ -# React Start RSC - -- For diagnostic slot previews and recursive payload transforms, validate supported shared-reference, deep, and cyclic inputs before timing them. Traversal state must actually bound repeated work; any reuse must preserve the consumer's serialization contract. -- Gate development diagnostics before constructing payloads, then measure their development cost. Treat replay needed by SSR and later serialization as a required lifetime until evidence shows retention beyond it; validate cancellation and request cleanup before calling it a leak. diff --git a/packages/router-devtools-core/AGENTS.md b/packages/router-devtools-core/AGENTS.md deleted file mode 100644 index 2ae07c5eb1b..00000000000 --- a/packages/router-devtools-core/AGENTS.md +++ /dev/null @@ -1,5 +0,0 @@ -# Router devtools - -- Measure closed-panel setup and idle callbacks with large route/cache state as well as open-panel interactions. Development-only code still has a user-visible cost; distinguish default development exports from explicit production opt-ins. -- Count style-factory and template-processing work separately from inserted CSS. Before reusing styles, preserve document/shadow-root ownership, theme updates, HMR, and target disposal; stylesheet deduplication alone does not prove the construction work is absent. -- For React/Solid/Vue wrappers, replace router A with B while mounted, navigate each, then unmount and update again. Only the current router should drive the panel, and unmounted wrappers should receive no further work. Test floating and standalone wrappers when both are affected. diff --git a/packages/router-plugin/AGENTS.md b/packages/router-plugin/AGENTS.md index 041bacea12b..e954110819e 100644 --- a/packages/router-plugin/AGENTS.md +++ b/packages/router-plugin/AGENTS.md @@ -5,4 +5,3 @@ - Route HMR rebuilds indexes, clears `resolvePathCache`, replaces lazy-chunk ownership, and refreshes active routes while preserving Fast Refresh component identities. Keep these steps coordinated when adding derived state. - For code-splitting changes, add inputs under `tests/code-splitter/test-files//`. Regenerate and review reference, virtual, and shared snapshots for every grouping in [`tests/code-splitter.test.ts`](tests/code-splitter.test.ts). - Check [`tests/constants.ts`](tests/constants.ts) for framework coverage: the snapshot matrix currently covers React and Solid only. Validate affected Vue behavior in Vue router e2e apps. -- For transform/build performance, source maps, production diagnostics, shared module state, or Webpack/Rspack behavior, follow the [build-tool checks](../../.github/agent-guides/performance.md#build-tool-checks). Snapshot output and Vite results do not establish actual Webpack loader, mode, cache, or chunk behavior. diff --git a/packages/start-plugin-core/AGENTS.md b/packages/start-plugin-core/AGENTS.md index 50dfd9cab29..cd73bbf3074 100644 --- a/packages/start-plugin-core/AGENTS.md +++ b/packages/start-plugin-core/AGENTS.md @@ -5,4 +5,3 @@ - Compiler output must match runtime contracts in `start-client-core`, `start-fn-stubs`, and `start-server-core`. For `createServerFn`, cover client, SSR caller, and server-provider output separately in [`tests/createServerFn/createServerFn.test.ts`](tests/createServerFn/createServerFn.test.ts). - Use a built Start e2e app to validate generated imports and runtime behavior together; compiler snapshots alone do not exercise that integration. - The shared compiler serves both Vite and Rsbuild. For shared compiler changes, check the relevant adapter suites in `tests/vite/` and `tests/rsbuild/`, plus affected Start e2e modes for build, server-function, or HMR behavior. -- For build-time performance, use the [build-tool checks](../../.github/agent-guides/performance.md#build-tool-checks). Trace normalized indexes, asset text, CSS content, and other metadata from producer to actual consumers before retaining or removing them. Test the consuming feature both disabled and enabled, including inline CSS, and preserve full manifest/output behavior in Vite and Rsbuild. From 3ec5cdfb5d3c061193b91a6ea21e3d1ac15daffc Mon Sep 17 00:00:00 2001 From: ladybluenotes Date: Sun, 6 Sep 2026 16:46:57 -0700 Subject: [PATCH 5/9] docs: capture Router and Start audit lessons --- .github/agent-guides/performance.md | 33 +++++++++++++++++++++--- AGENTS.md | 8 +++++- e2e/AGENTS.md | 1 + packages/AGENTS.md | 1 + packages/router-plugin/AGENTS.md | 1 + skills/bundle-size-optimization/SKILL.md | 2 ++ 6 files changed, 41 insertions(+), 5 deletions(-) diff --git a/.github/agent-guides/performance.md b/.github/agent-guides/performance.md index 30d24a0c649..8bd5b5a7843 100644 --- a/.github/agent-guides/performance.md +++ b/.github/agent-guides/performance.md @@ -1,6 +1,6 @@ # Performance review -Use this procedure for Router/Start performance audits and changes to runtime, memory, build, type-inference, or shipped-byte cost. Keep the requested scope; discovery does not authorize another fix or publication. +Use this procedure for Router/Start performance audits and changes to runtime lifetimes, hydration/RSC, HMR, dependencies, chunk caching, build, type-inference, or shipped-byte cost. Select checks for the changed mechanism; this is not a requirement to audit unrelated surfaces. Keep the requested scope; discovery does not authorize another fix or publication. Select the existing workload for [client navigation](../../benchmarks/client-nav/README.md), [Start SSR](../../benchmarks/ssr/README.md), or [memory](../../benchmarks/memory/README.md). Compare identical baseline/candidate workloads and add coverage when existing scenarios miss the changed mechanism. There is no aggregate build-CPU benchmark in that set: use the build-tool checks below. For hydration, RPC, paint, or real HTTP behavior, use the affected browser/app workload and the browser/server measurement procedure. For inference cost, use the TypeScript procedure. @@ -9,7 +9,7 @@ Select the existing workload for [client navigation](../../benchmarks/client-nav 1. Pin the source revision and name the workload, metric, owning layer, and success condition. Reuse repository fixtures and installed tools. Attribute cost to code, configuration, a dependency, the toolchain, or the harness before choosing a fix; avoid speculative caches, indexes, concurrency, or dependency changes. 2. For multi-surface audits, keep a local coverage row per independently testable requirement: owner, source paths/revision, executed check, evidence, and unresolved boundary. Distinguish inventory/source review, executed mechanism, diagnostic control, verified implementation, and measured user impact. A checked package list does not establish exhaustive path coverage. 3. Establish a supported trigger and baseline before editing. Diagnostic counters must not create the work they claim to observe. Change one variable per control, check equivalent behavior, then run the same protocol on the final implementation. Use profiles or operation counts to explain timing, not a source-level suspicion as proof of a slowdown. -4. Independently inspect each material finding against its owning source and actual check. Promote proven regressions into existing behavioral tests or stable operation/artifact bounds; avoid noisy elapsed-time assertions. Preserve request isolation, side-effect ordering, required waits, and intentional cache/replay lifetimes. +4. Independently inspect each material finding against its owning source and actual check. Promote proven regressions into existing behavioral tests or stable operation/artifact bounds; avoid noisy elapsed-time assertions. Assert the effect that exposed the bug: execution count, requests, retained resources, or intermediate failure, as well as final output. Preserve request isolation, side-effect ordering, required waits, and intentional cache/replay lifetimes. 5. Before completion, reconcile every coverage row with the original request. Report findings, rejected candidates, and remaining unverified or blocked surfaces. A diagnostic control is not a production fix; unavailable browser metrics, failed watch integration, or source-only adapter review remain explicit gaps. ### Finding capture @@ -18,6 +18,8 @@ Keep non-blocking discoveries in the task's local evidence document. Give each a Retain the reason when a finding is rejected or excluded by the user's scope. Capture unrelated evidence without fixing it, adding it to the active task, or posting externally. Resume the current work. A blocker or changed correctness risk must be reported rather than parked. Keep private task evidence and machine paths out of public guidance and PRs. +Git history identifies where the responsible code changed. Establishing when the bug began requires reproducing behavior with that revision's dependencies and configuration; otherwise report the code origin and the earliest verified failing revision separately. + ## Comparison provenance Record baseline/candidate revisions, benchmark source and workload, runtime/tool versions, framework, adapter, build mode, cache state, runner mode, command, raw outputs, aggregation, and noise. Keep scenario selections and measurement flags identical. Use separate results/dist directories for concurrent tasks; avoid timing against unrelated active builds when possible and report remaining contention. @@ -47,15 +49,35 @@ Reference: Webpack's [build-performance](https://webpack.js.org/guides/build-per For Start metadata changes, trace normalized indexes, asset text, CSS content, and other metadata from producer to actual consumers before retaining or removing them. Test the consuming feature both disabled and enabled, including inline CSS, and preserve full manifest/output behavior in Vite and Rsbuild. +### HMR and watch lifetimes + +- Verify the fixture's framework refresh integration before attributing a failure to Router or the compiler. Exercise repeated component, route-option, server-function, and CSS edits where affected; check visible changes and intentional state preservation. A window sentinel or document-request count must distinguish hot updates from a full reload; allow reloads only for transitions whose contract requires them. +- For route deletion or configuration replacement, capture generated source, errors, and emitted assets from the same compilation. Check the first affected compilation as well as eventual recovery. Replace configuration while generation is pending, then dispatch later file events; only the current generator should write, and metadata for deleted routes must be removed. One final successful build can hide an earlier stale build or duplicate work. +- Restore source edits through the existing HMR helper after failures. Await the expected source/build/browser state instead of adding fixed sleeps or manual reloads that hide broken updates. + +### Dependency upgrades + +Compare the same consumer on the existing and candidate dependency stacks. Record actual resolved versions, required companion runtimes, and supported peer contracts from matching upstream sources; a passing fixture does not override a documented compatibility requirement. Keep upgrade effects separate from application configuration changes. + +For a failure on both stacks, investigate the existing integration or fixture before calling it an upgrade regression. For a candidate-only failure, reduce a matched old/new reproduction, check upstream issues, and identify the owning layer before adding a workaround. Preserve evidence for an upstream fix; external publication requires authorization. Do not relax checks or dependency policy to make an upgrade pass. + +### Chunk caching and deployment recovery + +Measure cold-load bytes, request count, and loading time separately from downloads after unchanged and edited deployments. Use real HTTP cache headers and record cache state. Inspect resolved module paths before assigning vendor groups; workspace-linked packages may not match a `node_modules` rule. Treat chunk layout as application/framework policy unless the plugin contract explicitly owns it. + +Keep an old page open across deployment, then navigate to a previously unloaded route. Cover retained and removed old hashed assets. For chunk recovery, verify preload does not reload the page, navigation can recover through the bounded reload policy, a persistently missing chunk reaches the error UI without a loop, and unrelated errors do not trigger recovery. Reloading the page between deployments does not exercise this boundary. + ## Runtime and lifecycle checks - For optional features, measure disabled/idle work as well as enabled work. Gate unnecessary allocation, subscriptions, scans, and decoding before setup where safe; a handler that immediately returns still has registration cost. Preserve user handlers and off/on/off transitions. -- For listeners, subscriptions, observers, timers, caches, and patched globals, name the owner and cleanup path. Test overlapping instances, both disposal orders, replacement, and events after disposal. Verify which owner receives subsequent work, not just whether cleanup ran. Apply this to scroll restoration across router lifetimes as well as framework unmounts. +- For listeners, subscriptions, observers, timers, caches, and patched globals, name the creator, update owner, lifetime, and cleanup path. Start pending work or populate retained state before testing cancellation, failure, replacement, and disposal. Test overlapping instances, both disposal orders, and events after disposal. Verify which owner receives subsequent work, not just whether cleanup ran. Apply this to scroll restoration across router lifetimes as well as framework unmounts; a component unmount does not necessarily end a shared history's lifetime. - For native history wrappers or teardown, exercise two histories sharing one window, destroy them in both orders, and dispatch later native updates. A surviving history must keep receiving updates; destroying the second must not restore a wrapper owned by the already-destroyed first history. -- For hydration changes, cover initial SSR hydration, CSR, later-mounted boundaries, unrelated ancestor updates, intentional option changes, and disposal. Define which event starts or resets a deadline; test inline/equivalent strategy values and browsers with and without the relevant scheduling API. Preserve intentional pending-UI waits and loader/context ordering. +- For hydration changes, cover initial SSR hydration, CSR, later-mounted boundaries, unrelated ancestor updates, intentional option changes, and disposal. Define which event starts or resets a deadline; test inline/equivalent strategy values and browsers with and without the relevant scheduling API. Queue a real interaction while loading is pending before cancelling or removing the boundary; check obsolete replay records are released while surviving nested boundaries and successful replay still work. Preserve intentional pending-UI waits and loader/context ordering. - For head, Scripts, Asset, or injected-code changes, assert execution counts through attribute/content combinations, hydration, navigation, and removal. Final markup can hide duplicate side effects; distinguish elements, fetch attempts, transferred bytes, and executions. - For repeated scans or recursive transforms, exercise increasing input sizes and supported empty, deep, wide, shared-reference, and cyclic inputs. Count work separately from elapsed time; preserve ordering, identity, and serialization behavior before retaining a faster implementation. +Before adding a cache or index, check existing subtree APIs, adjacency data, and reuse within the current transform or request. Measure work at its real frequency: per root, file, project, route, or render. Preserve AST mutation ownership and complete diagnostic messages, locations, and ordering. A persistent cache also needs an owner, invalidation on edits/deletion/configuration changes, and a measured retention bound; fewer visits alone does not justify longer-lived state. + ### Router devtools - Measure closed-panel setup and idle callbacks with large route/cache state as well as open-panel interactions. Development-only code still has a user-visible cost; distinguish default development exports from explicit production opt-ins. @@ -66,6 +88,9 @@ For Start metadata changes, trace normalized indexes, asset text, CSS content, a - For diagnostic slot previews and recursive payload transforms, validate supported shared-reference, deep, and cyclic inputs before timing them. Traversal state must actually bound repeated work; any reuse must preserve the consumer's serialization contract. - Gate development diagnostics before constructing payloads, then measure their development cost. Treat replay needed by SSR and later serialization as a required lifetime until evidence shows retention beyond it; validate cancellation and request cleanup before calling it a leak. +- For asset decoding/preloading, return both rendered and unused RSC values through the supported server-function path. Check client navigation and cold/warm SSR, using fresh browser contexts against a reused server. Verify unused values do not load client JS or apply CSS, and later rendering still loads dependencies. Cover streams with and without serialized asset metadata and both renderable and composite components. +- For stylesheet changes, identify whether the framework renderer or bundler creates, updates, and removes each link. Prefer the existing owner's lifecycle over competing resource registration. Check repeated edits and property removal using computed styles and bounded link counts; visible correctness alone can conceal stale stylesheets. A Vite-specific ownership fix must preserve Rsbuild behavior. +- For deferred rendering or refresh, delay stylesheet delivery and verify content is styled when revealed, existing content remains visible while replacement is pending, and hydration reports no mismatch. Check production Vite and Rsbuild, including JavaScript-disabled SSR. Keep browser-only resource handling from making streamed SSR content depend on JavaScript. ## Browser and server measurements diff --git a/AGENTS.md b/AGENTS.md index ce64db44b7d..416d205c101 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -25,6 +25,12 @@ CI=1 NX_DAEMON=false pnpm nx run : --outputStyle=stream --skipR - Run one Nx command at a time. For a ~20-second startup/graph stall, stop, run `pnpm nx reset`, and retry once, then escalate. Do not apply that timeout to running tests. - For code changes, run affected packages' `test:unit`, `test:types`, and `test:eslint` where available, including affected consumers; use `test:e2e` for browser/app behavior and `test:build` for exports/build changes. Root `pnpm test` includes the full e2e suite. Published-code changes require `pnpm changeset`. +## Review and handoff + +- For PR work, read the current base/head, CI failures, human and automated review comments, and related work before editing. Evaluate concrete nitpicks against source and conventions as well as correctness findings; retain the reason for accepting or rejecting material feedback. +- For interacting PRs, record the tested revisions, check each independently where supported, and exercise their combined behavior in an isolated checkout. Rerun affected checks after integration repairs; a clean textual merge does not prove compatibility. When superseding PRs, account for their retained fixes/tests and preserve contributor credit; closing originals requires authorization. +- Explain the supported trigger, consequence, cause, resulting behavior, and material tradeoffs in the handoff. Keep PR descriptions focused on the problem and resulting behavior, following the required template without adding a validation narrative. Keep detailed test receipts in the handoff, distinguish local checks from remote CI, and leave contributor attestations to the contributor. + ## Runtime rules - Import `isServer` from `@tanstack/router-core/isServer`. Conditional exports select: `development` → `undefined`; server (`workerd`, `worker`, `deno`, `node`, `bun`) → `true` except `NODE_ENV=test` → `undefined`; browser/fallback → `false`. `development` wins; `NODE_ENV=development` alone does not select it. @@ -36,5 +42,5 @@ CI=1 NX_DAEMON=false pnpm nx run : --outputStyle=stream --skipR ## Performance and final bundle pass -- For performance audits or changes to runtime lifecycles, memory, build, type inference, or shipped bytes, follow the [performance review guide](.github/agent-guides/performance.md). It owns workload selection, lifecycle checks, Webpack/build checks, and evidence capture; keep diagnostic controls and measured user impact distinct. +- For performance audits, runtime lifecycles, hydration/RSC, HMR, dependency upgrades, chunk caching, build, type inference, or shipped bytes, follow the [performance review guide](.github/agent-guides/performance.md). It owns workload selection, lifecycle checks, Webpack/build checks, and evidence capture; keep diagnostic controls and measured user impact distinct. - **Last phase for every change affecting emitted client JavaScript**, including core/build transforms: complete the full [bundle-size-optimization skill](skills/bundle-size-optimization/SKILL.md) after correctness and performance validation. The full workflow is mandatory within the accepted change scope. diff --git a/e2e/AGENTS.md b/e2e/AGENTS.md index f373ecba4f3..fb96a8a47e4 100644 --- a/e2e/AGENTS.md +++ b/e2e/AGENTS.md @@ -6,4 +6,5 @@ - Use the same `E2E_PORT_KEY`-aware key throughout config, app, and dummy-server setup/teardown. Clean only that key's port files, before allocation, when `TEST_WORKER_INDEX === undefined`; worker config reloads must not remap a running server's port. - For new browser tests, reuse the local error-checking fixture. The shared `test` from `@tanstack/router-e2e-utils` fails on unexpected `console.error`, including hydration errors; allow only expected errors with `whitelistErrors`. - HMR/watch tests edit real app sources. Reuse existing edit/restore helpers such as `createHmrFileEditor`, restore edits in teardown even after failures, and preserve the suites' single-worker and Nx `parallelism: false` settings. +- For HMR, hydration/RSC assets, or deployment-cache tests, select the matching [performance guide checks](../.github/agent-guides/performance.md). They define repeated-edit, cancellation, warm-server, stylesheet-readiness, and old-tab cases that a successful initial render misses. - `@tanstack/router-e2e-utils:test:unit` is a placeholder. Validate helper changes through consuming e2e projects. diff --git a/packages/AGENTS.md b/packages/AGENTS.md index 0c63c3a15c6..15b27c4df53 100644 --- a/packages/AGENTS.md +++ b/packages/AGENTS.md @@ -10,6 +10,7 @@ ## Tests - Inspect test commands: several `test:unit` scripts immediately exit or only echo a message. For these packages, validate behavior in the shared core or consuming e2e app; a successful placeholder target provides no unit coverage. +- Check test discovery and TypeScript/ESLint includes for newly added files. A passing source-only compiler matrix does not typecheck tests outside its includes; use the applicable test configuration and inspect executed cases. Report missing coverage explicitly and keep existing compiler/lint rules intact. - For `*-router-ssr-query` changes, run `router-ssr-query-core` unit tests and the affected framework's `e2e/-start/query-integration` tests. The framework wrapper unit targets currently exit without running tests. - For single-command Vitest targets, append filters after `--`, e.g. `-- tests/link.test.tsx -t "preloading"`. - `solid-router` and `router-generator` chain two Vitest commands in `test:unit`. Trailing file/name/update arguments reach only the final command. Run the full target for verification: Solid needs browser and server suites; the generator needs fixture generation followed by typechecking. diff --git a/packages/router-plugin/AGENTS.md b/packages/router-plugin/AGENTS.md index e954110819e..1e4bcdd7549 100644 --- a/packages/router-plugin/AGENTS.md +++ b/packages/router-plugin/AGENTS.md @@ -3,5 +3,6 @@ - Preserve one initialization of shared module state across reference and split chunks. Shared extraction includes transitive dependencies and whole destructured declarations; never extract `Route` or bindings that depend on it. The invariant suite in `tests/code-splitter.test.ts` covers these contracts. - `handleRouteUpdate` is stringified and injected into app modules. Keep runtime dependencies inside the function or explicitly emit them; extracting a helper to plugin module scope breaks the generated function. `tests/handle-route-update.test.ts` executes that emitted code. - Route HMR rebuilds indexes, clears `resolvePathCache`, replaces lazy-chunk ownership, and refreshes active routes while preserving Fast Refresh component identities. Keep these steps coordinated when adding derived state. +- For HMR adapter changes, execute generated statements for first import and later hot evaluation; first import must not modify a different router's same-ID route. Match the adapter's actual accept/dispose data lifecycle, including Webpack's fresh disposal data. Follow the [HMR/watch checks](../../.github/agent-guides/performance.md#hmr-and-watch-lifetimes) for browser state, pending work, route deletion, and configuration replacement. - For code-splitting changes, add inputs under `tests/code-splitter/test-files//`. Regenerate and review reference, virtual, and shared snapshots for every grouping in [`tests/code-splitter.test.ts`](tests/code-splitter.test.ts). - Check [`tests/constants.ts`](tests/constants.ts) for framework coverage: the snapshot matrix currently covers React and Solid only. Validate affected Vue behavior in Vue router e2e apps. diff --git a/skills/bundle-size-optimization/SKILL.md b/skills/bundle-size-optimization/SKILL.md index 6b34847c9cf..401604a5584 100644 --- a/skills/bundle-size-optimization/SKILL.md +++ b/skills/bundle-size-optimization/SKILL.md @@ -71,6 +71,8 @@ For named results, pass `--current benchmarks/bundle-size/results/runs//cu - If a change can affect several package families, pick the smallest scenario that imports the shared code for quick iteration, then spot-check the next most likely affected family before finalizing. - Before finalizing, run the full bundle-size benchmark without `--scenario` and compare all scenarios. Look for outliers/anomalies even when the targeted scenario improved. - Benchmark the changed mechanism directly, not just the public API around it. +- Confirm the affected code survives into the measured scenario. If stock scenarios omit an optional feature such as deferred hydration or RSC, add a focused supported consumer and report its cost separately; unchanged stock totals do not establish zero cost for that feature. +- For runtime/vendor chunk layout changes, follow the [chunk caching and deployment checks](../../.github/agent-guides/performance.md#chunk-caching-and-deployment-recovery). Report cold-load cost separately from repeat-deployment savings; a favorable transfer result does not establish faster loading. - Keep broad realistic scenarios as smoke/regression coverage; use focused cases for proof. - Compare baseline and current with the same benchmark file. Use a separate worktree when only implementation should differ. - Run noisy benchmark families separately with `-t `; all-in-one suites can perturb tiny operations. From b8124f59ab5efdd1480b161ebe7ad103b22919c8 Mon Sep 17 00:00:00 2001 From: ladybluenotes Date: Sun, 6 Sep 2026 16:54:10 -0700 Subject: [PATCH 6/9] docs: separate performance checks from personal workflows --- .github/agent-guides/performance.md | 36 +++++++----------------- AGENTS.md | 24 ++++++---------- benchmarks/AGENTS.md | 2 +- e2e/AGENTS.md | 2 +- packages/AGENTS.md | 2 +- skills/bundle-size-optimization/SKILL.md | 35 ++++++++++------------- skills/performance-review/SKILL.md | 33 ++++++++++++++++++++++ 7 files changed, 70 insertions(+), 64 deletions(-) create mode 100644 skills/performance-review/SKILL.md diff --git a/.github/agent-guides/performance.md b/.github/agent-guides/performance.md index 8bd5b5a7843..09b23949f3c 100644 --- a/.github/agent-guides/performance.md +++ b/.github/agent-guides/performance.md @@ -1,32 +1,16 @@ -# Performance review +# Performance checks -Use this procedure for Router/Start performance audits and changes to runtime lifetimes, hydration/RSC, HMR, dependencies, chunk caching, build, type-inference, or shipped-byte cost. Select checks for the changed mechanism; this is not a requirement to audit unrelated surfaces. Keep the requested scope; discovery does not authorize another fix or publication. +Reference for the [performance-review skill](../../skills/performance-review/SKILL.md). Read the sections relevant to the changed mechanism and affected consumers; the skill defines the investigation procedure. Select the existing workload for [client navigation](../../benchmarks/client-nav/README.md), [Start SSR](../../benchmarks/ssr/README.md), or [memory](../../benchmarks/memory/README.md). Compare identical baseline/candidate workloads and add coverage when existing scenarios miss the changed mechanism. There is no aggregate build-CPU benchmark in that set: use the build-tool checks below. For hydration, RPC, paint, or real HTTP behavior, use the affected browser/app workload and the browser/server measurement procedure. For inference cost, use the TypeScript procedure. -## Evidence and coverage +## Comparable measurements -1. Pin the source revision and name the workload, metric, owning layer, and success condition. Reuse repository fixtures and installed tools. Attribute cost to code, configuration, a dependency, the toolchain, or the harness before choosing a fix; avoid speculative caches, indexes, concurrency, or dependency changes. -2. For multi-surface audits, keep a local coverage row per independently testable requirement: owner, source paths/revision, executed check, evidence, and unresolved boundary. Distinguish inventory/source review, executed mechanism, diagnostic control, verified implementation, and measured user impact. A checked package list does not establish exhaustive path coverage. -3. Establish a supported trigger and baseline before editing. Diagnostic counters must not create the work they claim to observe. Change one variable per control, check equivalent behavior, then run the same protocol on the final implementation. Use profiles or operation counts to explain timing, not a source-level suspicion as proof of a slowdown. -4. Independently inspect each material finding against its owning source and actual check. Promote proven regressions into existing behavioral tests or stable operation/artifact bounds; avoid noisy elapsed-time assertions. Assert the effect that exposed the bug: execution count, requests, retained resources, or intermediate failure, as well as final output. Preserve request isolation, side-effect ordering, required waits, and intentional cache/replay lifetimes. -5. Before completion, reconcile every coverage row with the original request. Report findings, rejected candidates, and remaining unverified or blocked surfaces. A diagnostic control is not a production fix; unavailable browser metrics, failed watch integration, or source-only adapter review remain explicit gaps. +Compare the intended baseline and candidate implementations with the same benchmark source, workload, runtime/tool versions, framework, adapter, build mode, cache state, and runner settings. Keep scenario selections and measurement flags identical. Use separate results/dist directories and avoid unrelated active builds during timing runs. -### Finding capture +Check the benchmark's actual base, environment warnings, skipped cases, baseline substitutions, and cached results before comparing implementations. A successful job may reuse earlier measurements or skip the affected scenario. Plain memory smoke runs do not supply CodSpeed memory metrics. -Keep non-blocking discoveries in the task's local evidence document. Give each a stable repository/symptom identity, supported trigger, expected/actual behavior, owning source/revision, measurement and limits, existing issue/PR, disposition, next check, and acceptance condition. Label unverified symptoms as observations. Search existing records and open/closed upstream work before creating a duplicate; distinguish an associated PR from a merged and currently verified fix. - -Retain the reason when a finding is rejected or excluded by the user's scope. Capture unrelated evidence without fixing it, adding it to the active task, or posting externally. Resume the current work. A blocker or changed correctness risk must be reported rather than parked. Keep private task evidence and machine paths out of public guidance and PRs. - -Git history identifies where the responsible code changed. Establishing when the bug began requires reproducing behavior with that revision's dependencies and configuration; otherwise report the code origin and the earliest verified failing revision separately. - -## Comparison provenance - -Record baseline/candidate revisions, benchmark source and workload, runtime/tool versions, framework, adapter, build mode, cache state, runner mode, command, raw outputs, aggregation, and noise. Keep scenario selections and measurement flags identical. Use separate results/dist directories for concurrent tasks; avoid timing against unrelated active builds when possible and report remaining contention. - -Separate three conclusions: the benchmark executed, its results are comparable, and the change caused the difference. Check the report's actual base, environment warnings, skipped cases, baseline substitutions, and logs/output artifacts. A successful job or cached result is not fresh execution evidence. Plain memory smoke runs do not supply CodSpeed memory metrics. - -Report operation counts, retained bytes, CPU, elapsed time, throughput, initial payload, and later loading cost separately. Do not convert fewer calls into an unmeasured speedup, raw-byte changes into gzip savings, or a larger RSS sample into a leak. Read sample count, variance/error, and relevant percentiles together; narrow or rerun noisy cases before making a comparative claim. +Measure operation counts, retained bytes, CPU, elapsed time, throughput, initial payload, and later loading cost separately. Fewer calls do not necessarily reduce elapsed time, raw-byte reductions may increase gzip size, and a larger RSS sample alone does not establish a leak. Use sample count, variance/error, and relevant percentiles to distinguish a change from measurement noise; narrow or rerun noisy cases. ## Build-tool checks @@ -59,11 +43,11 @@ For Start metadata changes, trace normalized indexes, asset text, CSS content, a Compare the same consumer on the existing and candidate dependency stacks. Record actual resolved versions, required companion runtimes, and supported peer contracts from matching upstream sources; a passing fixture does not override a documented compatibility requirement. Keep upgrade effects separate from application configuration changes. -For a failure on both stacks, investigate the existing integration or fixture before calling it an upgrade regression. For a candidate-only failure, reduce a matched old/new reproduction, check upstream issues, and identify the owning layer before adding a workaround. Preserve evidence for an upstream fix; external publication requires authorization. Do not relax checks or dependency policy to make an upgrade pass. +For a failure on both stacks, investigate the existing integration or fixture before calling it an upgrade regression. For a candidate-only failure, reduce a matched old/new reproduction, check upstream issues, and identify the owning layer before adding a workaround. Do not relax checks or dependency policy to make an upgrade pass. ### Chunk caching and deployment recovery -Measure cold-load bytes, request count, and loading time separately from downloads after unchanged and edited deployments. Use real HTTP cache headers and record cache state. Inspect resolved module paths before assigning vendor groups; workspace-linked packages may not match a `node_modules` rule. Treat chunk layout as application/framework policy unless the plugin contract explicitly owns it. +Measure cold-load bytes, request count, and loading time separately from downloads after unchanged and edited deployments. Use real HTTP cache headers and control cache state. Inspect resolved module paths before assigning vendor groups; workspace-linked packages may not match a `node_modules` rule. Treat chunk layout as application/framework policy unless the plugin contract explicitly owns it. Keep an old page open across deployment, then navigate to a previously unloaded route. Cover retained and removed old hashed assets. For chunk recovery, verify preload does not reload the page, navigation can recover through the bounded reload policy, a persistently missing chunk reaches the error UI without a loop, and unrelated errors do not trigger recovery. Reloading the page between deployments does not exercise this boundary. @@ -100,8 +84,8 @@ For changed hydration/RPC/streaming behavior, exercise real HTTP/browser consump ## TypeScript cost -Use the repository typecheck/project-reference setup and a representative consumer route tree at increasing sizes. Keep TypeScript version, compiler configuration, fixture, and cold/warm state identical between implementations. Preserve inference assertions and record check time, compiler work, and memory where the installed compiler exposes them; report unavailable measurements. Do not pass individual source files to `tsc` and thereby bypass project configuration. A passing typecheck does not establish unchanged compiler/editor performance. +Use the repository typecheck/project-reference setup and a representative consumer route tree at increasing sizes. Keep TypeScript version, compiler configuration, fixture, and cold/warm state identical between implementations. Preserve inference assertions and measure check time, compiler work, and memory where the installed compiler exposes them. Do not pass individual source files to `tsc` and thereby bypass project configuration. A passing typecheck does not establish unchanged compiler/editor performance. ## Reviewing changes to this guidance -Check that a representative task reaches the right procedure, an unrelated task does not, and a failed or unavailable check remains explicit. For behavioral validation, replay bounded tasks with the guidance revision and record the host/tools, selected checks, actions, result, and human corrections. Link and text checks prove document structure only; skill availability or reviewer count does not prove the agent applied the procedure or made fewer mistakes. +When updating this guide, check that its triggers lead to the relevant procedures and that linked commands, fixtures, and references remain valid. Walk through an applicable change, an unrelated change, and a failed or unavailable check to identify missing instructions. Formatting and link checks alone do not establish that the procedures work. diff --git a/AGENTS.md b/AGENTS.md index 416d205c101..55eec0af8ca 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -5,32 +5,26 @@ - Read [CONTRIBUTING.md](CONTRIBUTING.md) before analyzing or changing this repository; its contribution and PR requirements apply. - Read applicable nested `AGENTS.md` files before editing or testing a subtree. - Shared routing belongs in `packages/router-core`; check React, Solid, and Vue bindings for shared changes. Shared Start runtime/build logic lives in `packages/start-*-core`. -- Before writes, inspect dirty files and existing worktrees/tasks. Preserve others' work, keep scratch checkouts outside the workspace's project-discovery tree, and isolate benchmark results/build outputs. Stop only processes owned by the task. Commit, push, and publication require user authorization; a verification procedure does not grant it. +- Keep scratch checkouts outside the workspace's project-discovery tree to avoid duplicate Nx projects. Use separate benchmark results and build-output directories for independent comparisons. -## Work and validation +## Build and test constraints -- Start bug fixes with a failing regression test that uses only public APIs to reproduce and assert user- or developer-visible behavior. Do not manufacture bugs by mutating internals. Establish whether real usage can reach the failing state before adding runtime handling; simplify handling of states proven unreachable. +- Regression coverage must establish a supported public trigger and observable failure. Tests that directly mutate internals do not establish that an application can reach the failing state. - Diagnostic counters and profiles may observe that supported path without changing its behavior. A diagnostic control proves only the measured mechanism; verify the final implementation through public behavior or the supported compiler-plugin boundary. -- During debugging/prototyping, keep fixes aligned with the PR's intended architecture. If making a test pass requires another flag, counter, copied deadline, or duplicate completion authority, stop and consolidate state and ownership. Remove superseded paths instead of accumulating patches and bundle growth. +- For asynchronous setup and cleanup, identify the authoritative state and lifetime before adding flags, timers, or completion callbacks. Preserve intentional ownership transfers and remove obsolete paths when replacing an implementation. - Use Node from `.nvmrc` and pnpm from root `package.json`. Install at the root with `CI=1 pnpm install --frozen-lockfile`. -- For sandbox-blocked registry/store access, escalate the same command; report the blocker if unavailable. Do not change stores, delete dependencies/lockfiles, use `--force`/`--ignore-scripts`, or weaken workspace trust/build policies to bypass it. +- Diagnose registry, store, and network failures separately from dependency incompatibilities. Preserve the lockfile and workspace trust/build policies when troubleshooting installation; bypassing scripts or policies changes the environment being tested. - Never manually edit `pnpm-lock.yaml` or any `routeTree.gen.ts`. Regenerate the lockfile with `pnpm install --no-frozen-lockfile` after intentional dependency changes; regenerate route trees through app builds/dev servers or the generator fixture harness. -- Always use control-flow braces. Format changed files with the repository's Prettier configuration before validation; root `pnpm format` writes across the checkout, so reserve it for an authorized repository-wide formatting pass. +- Always use control-flow braces. Format changed files with the repository's Prettier configuration; root `pnpm format` writes across the checkout. - Use Nx: workspace imports consume built packages, so direct runners can test stale dependencies. Inspect resolved targets; some are inferred. ```sh CI=1 NX_DAEMON=false pnpm nx run : --outputStyle=stream --skipRemoteCache ``` -- Run one Nx command at a time. For a ~20-second startup/graph stall, stop, run `pnpm nx reset`, and retry once, then escalate. Do not apply that timeout to running tests. +- Avoid overlapping Nx invocations that write to the same build outputs. If project-graph startup stalls, inspect logs and running processes before using `pnpm nx reset`; quiet output from running tests or benchmarks is not evidence of a graph stall. - For code changes, run affected packages' `test:unit`, `test:types`, and `test:eslint` where available, including affected consumers; use `test:e2e` for browser/app behavior and `test:build` for exports/build changes. Root `pnpm test` includes the full e2e suite. Published-code changes require `pnpm changeset`. -## Review and handoff - -- For PR work, read the current base/head, CI failures, human and automated review comments, and related work before editing. Evaluate concrete nitpicks against source and conventions as well as correctness findings; retain the reason for accepting or rejecting material feedback. -- For interacting PRs, record the tested revisions, check each independently where supported, and exercise their combined behavior in an isolated checkout. Rerun affected checks after integration repairs; a clean textual merge does not prove compatibility. When superseding PRs, account for their retained fixes/tests and preserve contributor credit; closing originals requires authorization. -- Explain the supported trigger, consequence, cause, resulting behavior, and material tradeoffs in the handoff. Keep PR descriptions focused on the problem and resulting behavior, following the required template without adding a validation narrative. Keep detailed test receipts in the handoff, distinguish local checks from remote CI, and leave contributor attestations to the contributor. - ## Runtime rules - Import `isServer` from `@tanstack/router-core/isServer`. Conditional exports select: `development` → `undefined`; server (`workerd`, `worker`, `deno`, `node`, `bun`) → `true` except `NODE_ENV=test` → `undefined`; browser/fallback → `false`. `development` wins; `NODE_ENV=development` alone does not select it. @@ -42,5 +36,5 @@ CI=1 NX_DAEMON=false pnpm nx run : --outputStyle=stream --skipR ## Performance and final bundle pass -- For performance audits, runtime lifecycles, hydration/RSC, HMR, dependency upgrades, chunk caching, build, type inference, or shipped bytes, follow the [performance review guide](.github/agent-guides/performance.md). It owns workload selection, lifecycle checks, Webpack/build checks, and evidence capture; keep diagnostic controls and measured user impact distinct. -- **Last phase for every change affecting emitted client JavaScript**, including core/build transforms: complete the full [bundle-size-optimization skill](skills/bundle-size-optimization/SKILL.md) after correctness and performance validation. The full workflow is mandatory within the accepted change scope. +- For performance audits, runtime lifecycles, hydration/RSC, HMR, dependency upgrades, chunk caching, build, or type-inference cost, use the [performance-review skill](skills/performance-review/SKILL.md). It selects the relevant measurements and regression checks. +- **Last phase for every change affecting emitted client JavaScript**, including core/build transforms: complete the full [bundle-size-optimization skill](skills/bundle-size-optimization/SKILL.md) after correctness and performance validation. Apply the workflow to the affected code and its consumers. diff --git a/benchmarks/AGENTS.md b/benchmarks/AGENTS.md index 268e23d9cc3..0ec73f7daff 100644 --- a/benchmarks/AGENTS.md +++ b/benchmarks/AGENTS.md @@ -10,4 +10,4 @@ Read the relevant README before running benchmarks. Compare baseline and candida Use `test:perf:` through Nx. Plain memory runs only smoke-test; memory metrics require CodSpeed. Client navigation benchmarks do not cover Start hydration or client server-function calls. -For bundle-size work, follow the full optimization skill required by the root guide. Full API fixtures are maintained manually: when public hooks/components change, update matching `bundle-size/scenarios/*-full/src/routes/__root.tsx` fixtures. Save baseline results separately because each run overwrites `bundle-size/results/current.json`; report final gzip deltas and explain remaining growth. +For bundle-size work, follow the full optimization skill required by the root guide. Full API fixtures are maintained manually: when public hooks/components change, update matching `bundle-size/scenarios/*-full/src/routes/__root.tsx` fixtures. Save baseline results separately because each run overwrites `bundle-size/results/current.json`; check final gzip deltas and investigate remaining growth. diff --git a/e2e/AGENTS.md b/e2e/AGENTS.md index fb96a8a47e4..a92cc8d45ba 100644 --- a/e2e/AGENTS.md +++ b/e2e/AGENTS.md @@ -6,5 +6,5 @@ - Use the same `E2E_PORT_KEY`-aware key throughout config, app, and dummy-server setup/teardown. Clean only that key's port files, before allocation, when `TEST_WORKER_INDEX === undefined`; worker config reloads must not remap a running server's port. - For new browser tests, reuse the local error-checking fixture. The shared `test` from `@tanstack/router-e2e-utils` fails on unexpected `console.error`, including hydration errors; allow only expected errors with `whitelistErrors`. - HMR/watch tests edit real app sources. Reuse existing edit/restore helpers such as `createHmrFileEditor`, restore edits in teardown even after failures, and preserve the suites' single-worker and Nx `parallelism: false` settings. -- For HMR, hydration/RSC assets, or deployment-cache tests, select the matching [performance guide checks](../.github/agent-guides/performance.md). They define repeated-edit, cancellation, warm-server, stylesheet-readiness, and old-tab cases that a successful initial render misses. +- For HMR, hydration/RSC assets, or deployment-cache tests, use the [performance-review skill](../skills/performance-review/SKILL.md) to select the applicable checks. They define repeated-edit, cancellation, warm-server, stylesheet-readiness, and old-tab cases that a successful initial render misses. - `@tanstack/router-e2e-utils:test:unit` is a placeholder. Validate helper changes through consuming e2e projects. diff --git a/packages/AGENTS.md b/packages/AGENTS.md index 15b27c4df53..e4e21a1e7b3 100644 --- a/packages/AGENTS.md +++ b/packages/AGENTS.md @@ -10,7 +10,7 @@ ## Tests - Inspect test commands: several `test:unit` scripts immediately exit or only echo a message. For these packages, validate behavior in the shared core or consuming e2e app; a successful placeholder target provides no unit coverage. -- Check test discovery and TypeScript/ESLint includes for newly added files. A passing source-only compiler matrix does not typecheck tests outside its includes; use the applicable test configuration and inspect executed cases. Report missing coverage explicitly and keep existing compiler/lint rules intact. +- Check test discovery and TypeScript/ESLint includes for newly added files. A passing source-only compiler matrix does not typecheck tests outside its includes; use the applicable test configuration and inspect executed cases. Keep existing compiler/lint rules intact when extending coverage. - For `*-router-ssr-query` changes, run `router-ssr-query-core` unit tests and the affected framework's `e2e/-start/query-integration` tests. The framework wrapper unit targets currently exit without running tests. - For single-command Vitest targets, append filters after `--`, e.g. `-- tests/link.test.tsx -t "preloading"`. - `solid-router` and `router-generator` chain two Vitest commands in `test:unit`. Trailing file/name/update arguments reach only the final command. Run the full target for verification: Solid needs browser and server suites; the generator needs fixture generation followed by typechecking. diff --git a/skills/bundle-size-optimization/SKILL.md b/skills/bundle-size-optimization/SKILL.md index 401604a5584..a7152f6591c 100644 --- a/skills/bundle-size-optimization/SKILL.md +++ b/skills/bundle-size-optimization/SKILL.md @@ -10,9 +10,9 @@ description: Use when working in this repository on JS bundle size, gzip regress Optimize measured client bundles, not source text. The source of truth is `@benchmarks/bundle-size:build`, `benchmarks/bundle-size/results/current.json`, and emitted JS in `benchmarks/bundle-size/dist/`. Use `benchmark:bundle-size:run` for local iterations. It uses the same measurement script and builds the selected packages through Nx. -The full measurement, attribution, and regression workflow applies within the accepted change scope. Optimize that change without acquiring unrelated work; larger architectural changes require matching user authority. Preserve others' edits and obtain explicit authorization for commits/pushes. Baseline comparison does not require committing or stashing the working implementation. +Apply the measurement, attribution, and regression workflow to the affected code and consumers. Compare baseline and candidate in isolated builds with identical workloads and separate outputs; a separate worktree is one way to do this. -For audits, comparison provenance, build-time cost, and adapter-specific verification, follow the [performance review guide](../../.github/agent-guides/performance.md). The official runner does not include Webpack: changes affecting that adapter or shared splitting/DCE also require its real-consumer checks. Do not substitute a Vite/Rsbuild result for Webpack evidence. +For audits, comparable measurements, build-time cost, and adapter-specific verification, use the [performance-review skill](../performance-review/SKILL.md) for the investigation and its applicable reference checks. Reuse completed checks for the same implementation and workload when continuing into bundle measurement. The official runner does not include Webpack: changes affecting that adapter or shared splitting/DCE also require its real-consumer checks. Do not substitute a Vite/Rsbuild result for Webpack evidence. ## Commands @@ -38,12 +38,11 @@ For named results, pass `--current benchmarks/bundle-size/results/runs//cu ## Rules -- Run one Nx command at a time. +- Avoid concurrent measurements or builds that share output directories; use Nx's task graph to coordinate package dependencies. - Use the runner instead of shell chains for environment variables, redirection, exit codes, log tails, and result queries. -- For agent runs, use `pnpm --silent benchmark:bundle-size:run ...` to omit the package-manager command echo. - The runner sets `CI=1` and `NX_DAEMON=false`. It saves full output in `tests.log` and `measure.log` beside `current.json`. - If a step fails, the runner stops and prints at most 40 log lines, limited to 8,000 characters. Read the reported log for more detail. -- Quiet stdout does not indicate an Nx hang. Inspect the reported log for progress before applying the Nx reset/retry guardrail. +- Quiet stdout does not indicate an Nx hang. Inspect the reported log for progress before resetting Nx. - Use `--test-projects` for the packages that changed. Pass unit-test file selectors and flags after `--`. - The runner does not select tests automatically. Keep type tests, performance benchmarks, and e2e tests separate. - Keep baseline and candidate scenario selections and measurement flags identical. Use a new `--name` for each retained snapshot. @@ -51,18 +50,14 @@ For named results, pass `--current benchmarks/bundle-size/results/runs//cu - Track `gzipBytes` first; also inspect `initialGzipBytes`, `rawBytes`, `brotliBytes`, `jsFiles`, and per-file `files`. - Dist paths use `scenarioDir`/`outDir`, not metric ids: `react-router.minimal` maps to `dist/react-router-minimal/`. - For tiny changes, measure after each candidate; gzip can move opposite raw bytes. -- To compare a base commit, measure the same scenario in a separate worktree. Pass its saved `current.json` path to `--baseline`. +- To compare a base commit, measure the same scenario in an isolated build, such as a separate worktree. Pass its saved `current.json` path to `--baseline`. - Use history for prior patterns and baselines, not source attribution. It is commit-level data. - Runtime performance and security may never be sacrificed for bundle size. -- Do not stop after the first verified win. Keep iterating through reasonable local, emitted-JS, and algorithmic candidates until measured regressions, readability, or risk rule out the remaining paths. - When inlining helpers or simplifying non-obvious logic, preserve readability with a short comment explaining the meaning/invariant, not the mechanics. - Before inlining or deleting a helper/function, use the TypeScript language-service script to check references: `pnpm ts:symbol-references -- --project /tsconfig.json --file --symbol `. If the helper is used elsewhere, inlining one use is usually not worth it for bundle size unless measurement proves otherwise. If no references remain, delete the helper and verify with the script. - Run unit/types tests for the package being modified plus relevant e2e tests under `e2e/`. -- Continue optimizing until further reductions would make code unreadable/unmaintainable, or no more reductions remain. A user-provided byte target is not required. -- Be willing to make large, risky architectural or algorithmic changes, but only within the runtime, security, readability, maintainability, and test constraints above. -- If you are unsure whether to land a passing change because runtime semantics might change, add unit/e2e tests and/or ask clarifying questions. If codebase exploration can answer the question, explore instead. For each question, provide your recommended answer. +- Evaluate candidates against correctness, measured benefit, readability, and maintenance cost. Resolve uncertain runtime behavior through source review and focused tests before retaining a change. - If runtime performance implications are unclear, add a focused Vitest benchmark (`*.bench.ts`) comparing candidate implementations across realistic and edge-case inputs, like `packages/router-core/tests/closing-tag-detection.bench.ts`; verify implementations produce identical results before `bench()` cases. -- If you learn a reusable bundle-size pattern, hit a tooling gap, or lack analysis capability, ask the user before updating this skill or the benchmark scripts. ## Benchmark Rules @@ -71,10 +66,10 @@ For named results, pass `--current benchmarks/bundle-size/results/runs//cu - If a change can affect several package families, pick the smallest scenario that imports the shared code for quick iteration, then spot-check the next most likely affected family before finalizing. - Before finalizing, run the full bundle-size benchmark without `--scenario` and compare all scenarios. Look for outliers/anomalies even when the targeted scenario improved. - Benchmark the changed mechanism directly, not just the public API around it. -- Confirm the affected code survives into the measured scenario. If stock scenarios omit an optional feature such as deferred hydration or RSC, add a focused supported consumer and report its cost separately; unchanged stock totals do not establish zero cost for that feature. -- For runtime/vendor chunk layout changes, follow the [chunk caching and deployment checks](../../.github/agent-guides/performance.md#chunk-caching-and-deployment-recovery). Report cold-load cost separately from repeat-deployment savings; a favorable transfer result does not establish faster loading. +- Confirm the affected code survives into the measured scenario. If stock scenarios omit an optional feature such as deferred hydration or RSC, add a focused supported consumer and measure its cost separately; unchanged stock totals do not establish zero cost for that feature. +- For runtime/vendor chunk layout changes, follow the [chunk caching and deployment checks](../../.github/agent-guides/performance.md#chunk-caching-and-deployment-recovery). Measure cold-load cost separately from repeat-deployment savings; a favorable transfer result does not establish faster loading. - Keep broad realistic scenarios as smoke/regression coverage; use focused cases for proof. -- Compare baseline and current with the same benchmark file. Use a separate worktree when only implementation should differ. +- Compare baseline and current with the same benchmark file; isolate the builds so only the implementation differs. - Run noisy benchmark families separately with `-t `; all-in-one suites can perturb tiny operations. - For branchy fast paths, include best-case, worst-case, and expected mixed distributions. - Batch ultra-fast operations inside one benchmark iteration when single calls are dominated by timer/outlier noise. @@ -90,7 +85,7 @@ Before calling an optimization final, prove which exact production hunks should 1. Snapshot the unoptimized baseline and the full candidate metrics. 2. Split the production diff into logical hunks or dependent hunk groups. Include syntax-only and readability-only edits if they can affect emitted code. 3. Benchmark each independent hunk alone against the same baseline. Benchmark relevant combinations when hunks only matter together or interact. -4. For each hunk/group, record bundle metrics and focused performance results when runtime cost could change. +4. For each hunk/group, measure bundle metrics and runtime performance where affected. 5. Keep only changes that improve bundle size or performance, or are required for correctness/tests/style and do not regress measured results. Revert neutral or harmful optimization-only changes. 6. Rebuild and remeasure the final composed version. It must not be larger or slower than the pre-attribution candidate unless the retained change is explicitly required for correctness or style. @@ -121,14 +116,14 @@ After each candidate, run focused perf benchmarks before bundle measurement. Rej When done optimizing: -1. Review the optimization diff against existing tests for missing behavior cases, newly uncovered edge cases, and performance coverage. Use independent reviewers for bounded questions when the task permits and the host supports them; reconcile their findings against source and checks. Without reviewers, perform a separate review pass and state that independence was not verified. -2. If a possible regression is unclear, ask the user or explore the codebase until the expected behavior is clear. +1. Review the optimization diff against existing tests for missing behavior cases, newly uncovered edge cases, and performance coverage. Verify review findings against source and executable checks. +2. Resolve uncertain behavior from the relevant APIs, implementation, and tests before accepting the optimization. 3. Use the review findings to add focused unit tests and benchmarks. 4. Prepare isolated baseline and candidate worktrees/snapshots with the same tests and benchmark harness, differing only in the implementation under comparison. Build each snapshot's dependencies through Nx; use separate results/dist paths and preserve the working checkout. -5. Run tests, performance benchmarks, and the relevant bundle-size measurement on the baseline, then write BEFORE results and revision/environment details to local `RESULT-optimization-{topic}.md`. -6. Run the same checks on the candidate, then append AFTER results to the same file. Keep raw outputs and identify skipped/cached checks. Publish task evidence only when requested. +5. Run tests, performance benchmarks, and the relevant bundle-size measurement on the baseline. +6. Run the same checks on the candidate and compare against the baseline. Confirm that skipped or cached checks have not omitted the changed behavior. 7. When reviewing benchmark output, consider statistical quality: standard deviation, margin of error, variance/noise, sample count, and percentiles. Re-run or narrow conclusions when results are noisy. -8. Compare BEFORE and AFTER. If anything regressed, iterate within the accepted scope or revert the task-owned regression. Recheck the final composed implementation after changes. +8. Compare baseline and candidate results. Investigate regressions and recheck the final composed implementation after changes. Useful patterns: remove prod-only strings, remove unused exports, flatten wrappers, inline one-use helpers, avoid duplicate literals, improve treeshaking boundaries, simplify branches after preserving behavior. diff --git a/skills/performance-review/SKILL.md b/skills/performance-review/SKILL.md new file mode 100644 index 00000000000..f581f49c8d3 --- /dev/null +++ b/skills/performance-review/SKILL.md @@ -0,0 +1,33 @@ +--- +name: performance-review +description: Use for TanStack Router/Start repository performance investigations and changes to resource lifetimes, hydration/RSC, HMR, compiler/editor cost, dependencies, or chunk caching. Covers measurement and regression checks; use bundle-size-optimization for emitted-byte optimization. +--- + +# Performance review + +Investigate a specific Router/Start mechanism with a comparable baseline and observable behavior. Follow the repository's build/test constraints in [AGENTS.md](../../AGENTS.md). Documentation-only changes that do not affect these mechanisms need their documentation checks, not this investigation. + +## Select checks + +Read [comparable measurements](../../.github/agent-guides/performance.md#comparable-measurements) for every measurement, then the applicable sections below. Combine sections when a change crosses boundaries. + +| Mechanism | Reference | +| -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Runtime work, cleanup, hydration, RSC, or devtools | [Runtime and lifecycle checks](../../.github/agent-guides/performance.md#runtime-and-lifecycle-checks); for live requests/rendering, also [browser and server measurements](../../.github/agent-guides/performance.md#browser-and-server-measurements) | +| Compiler work, maps, diagnostics, metadata, or module identity | [Build-tool checks](../../.github/agent-guides/performance.md#build-tool-checks) | +| Hot updates, route deletion, or watcher replacement | [HMR and watch lifetimes](../../.github/agent-guides/performance.md#hmr-and-watch-lifetimes) and the affected runtime/build checks | +| Dependency versions or bundler compatibility | [Dependency upgrades](../../.github/agent-guides/performance.md#dependency-upgrades) and the affected adapter checks | +| Vendor/runtime chunks or deployment recovery | [Chunk caching and deployment recovery](../../.github/agent-guides/performance.md#chunk-caching-and-deployment-recovery) | +| TypeScript inference or compiler cost | [TypeScript cost](../../.github/agent-guides/performance.md#typescript-cost) | + +## Investigate and verify + +1. Choose a representative workload and metric for the affected code. Reuse repository fixtures and installed tools. Attribute cost to code, configuration, a dependency, the toolchain, or the harness before choosing a fix; avoid speculative caches, indexes, concurrency, or dependency changes. +2. Exercise the affected packages and adapters through their real consumers. Source inspection or one adapter's passing tests cannot establish behavior in another adapter. +3. Establish a supported trigger and baseline before editing. Diagnostic counters must not create the work they claim to observe. Change one variable per control, check equivalent behavior, then run the same protocol on the final implementation. Use profiles or operation counts to explain timing, not a source-level suspicion as proof of a slowdown. +4. Verify each material finding against its owning source and actual check. Promote reproduced failures into existing behavioral tests or stable operation/artifact bounds; avoid noisy elapsed-time assertions. Assert the effect that exposed the bug: execution count, requests, retained resources, or intermediate failure, as well as final output. Preserve request isolation, side-effect ordering, required waits, and intentional cache/replay lifetimes. +5. Rerun the regression and relevant performance checks on the final implementation. Experimental controls can isolate a cause but do not replace tests of the actual fix. + +When changes interact, test both their individual and combined behavior where applicable. Recheck the combined implementation after repairs; a clean textual merge does not establish behavioral compatibility. + +For emitted client JavaScript, continue through the [bundle-size-optimization skill](../bundle-size-optimization/SKILL.md). Use the same implementation for behavior checks and bundle measurements. From 5a6bdb2c87ddbada58d2045942c8d12c5c3f8c6c Mon Sep 17 00:00:00 2001 From: ladybluenotes Date: Mon, 7 Sep 2026 09:41:50 -0700 Subject: [PATCH 7/9] docs: move performance checks into the review skill --- packages/router-plugin/AGENTS.md | 2 +- skills/bundle-size-optimization/SKILL.md | 4 ++-- skills/performance-review/SKILL.md | 20 +++++++++---------- .../performance-review/references/checks.md | 4 ++-- 4 files changed, 15 insertions(+), 15 deletions(-) rename .github/agent-guides/performance.md => skills/performance-review/references/checks.md (95%) diff --git a/packages/router-plugin/AGENTS.md b/packages/router-plugin/AGENTS.md index 1e4bcdd7549..52d289fa3f7 100644 --- a/packages/router-plugin/AGENTS.md +++ b/packages/router-plugin/AGENTS.md @@ -3,6 +3,6 @@ - Preserve one initialization of shared module state across reference and split chunks. Shared extraction includes transitive dependencies and whole destructured declarations; never extract `Route` or bindings that depend on it. The invariant suite in `tests/code-splitter.test.ts` covers these contracts. - `handleRouteUpdate` is stringified and injected into app modules. Keep runtime dependencies inside the function or explicitly emit them; extracting a helper to plugin module scope breaks the generated function. `tests/handle-route-update.test.ts` executes that emitted code. - Route HMR rebuilds indexes, clears `resolvePathCache`, replaces lazy-chunk ownership, and refreshes active routes while preserving Fast Refresh component identities. Keep these steps coordinated when adding derived state. -- For HMR adapter changes, execute generated statements for first import and later hot evaluation; first import must not modify a different router's same-ID route. Match the adapter's actual accept/dispose data lifecycle, including Webpack's fresh disposal data. Follow the [HMR/watch checks](../../.github/agent-guides/performance.md#hmr-and-watch-lifetimes) for browser state, pending work, route deletion, and configuration replacement. +- For HMR adapter changes, execute generated statements for first import and later hot evaluation; first import must not modify a different router's same-ID route. Match the adapter's actual accept/dispose data lifecycle, including Webpack's fresh disposal data. Follow the [HMR/watch checks](../../skills/performance-review/references/checks.md#hmr-and-watch-lifetimes) for browser state, pending work, route deletion, and configuration replacement. - For code-splitting changes, add inputs under `tests/code-splitter/test-files//`. Regenerate and review reference, virtual, and shared snapshots for every grouping in [`tests/code-splitter.test.ts`](tests/code-splitter.test.ts). - Check [`tests/constants.ts`](tests/constants.ts) for framework coverage: the snapshot matrix currently covers React and Solid only. Validate affected Vue behavior in Vue router e2e apps. diff --git a/skills/bundle-size-optimization/SKILL.md b/skills/bundle-size-optimization/SKILL.md index a7152f6591c..df6db8f1b26 100644 --- a/skills/bundle-size-optimization/SKILL.md +++ b/skills/bundle-size-optimization/SKILL.md @@ -67,7 +67,7 @@ For named results, pass `--current benchmarks/bundle-size/results/runs//cu - Before finalizing, run the full bundle-size benchmark without `--scenario` and compare all scenarios. Look for outliers/anomalies even when the targeted scenario improved. - Benchmark the changed mechanism directly, not just the public API around it. - Confirm the affected code survives into the measured scenario. If stock scenarios omit an optional feature such as deferred hydration or RSC, add a focused supported consumer and measure its cost separately; unchanged stock totals do not establish zero cost for that feature. -- For runtime/vendor chunk layout changes, follow the [chunk caching and deployment checks](../../.github/agent-guides/performance.md#chunk-caching-and-deployment-recovery). Measure cold-load cost separately from repeat-deployment savings; a favorable transfer result does not establish faster loading. +- For runtime/vendor chunk layout changes, follow the [chunk caching and deployment checks](../performance-review/references/checks.md#chunk-caching-and-deployment-recovery). Measure cold-load cost separately from repeat-deployment savings; a favorable transfer result does not establish faster loading. - Keep broad realistic scenarios as smoke/regression coverage; use focused cases for proof. - Compare baseline and current with the same benchmark file; isolate the builds so only the implementation differs. - Run noisy benchmark families separately with `-t `; all-in-one suites can perturb tiny operations. @@ -131,7 +131,7 @@ Useful patterns: remove prod-only strings, remove unused exports, flatten wrappe Rolldown removes code only when unused and side-effect-free. Property reads may trigger getters; storage/global access can observe or throw. -Validate annotation support and emitted effects with the installed bundler/minifier. For Webpack, use the [shipped-graph checks](../../.github/agent-guides/performance.md#build-tool-checks), including unused-import removal, required CSS/global effects, and one-time initialization across eager/lazy chunks. +Validate annotation support and emitted effects with the installed bundler/minifier. For Webpack, use the [shipped-graph checks](../performance-review/references/checks.md#build-tool-checks), including unused-import removal, required CSS/global effects, and one-time initialization across eager/lazy chunks. | Annotation | Valid | Unsafe | | ----------------------------------------- | --------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | diff --git a/skills/performance-review/SKILL.md b/skills/performance-review/SKILL.md index f581f49c8d3..c31b6a666fc 100644 --- a/skills/performance-review/SKILL.md +++ b/skills/performance-review/SKILL.md @@ -9,16 +9,16 @@ Investigate a specific Router/Start mechanism with a comparable baseline and obs ## Select checks -Read [comparable measurements](../../.github/agent-guides/performance.md#comparable-measurements) for every measurement, then the applicable sections below. Combine sections when a change crosses boundaries. - -| Mechanism | Reference | -| -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| Runtime work, cleanup, hydration, RSC, or devtools | [Runtime and lifecycle checks](../../.github/agent-guides/performance.md#runtime-and-lifecycle-checks); for live requests/rendering, also [browser and server measurements](../../.github/agent-guides/performance.md#browser-and-server-measurements) | -| Compiler work, maps, diagnostics, metadata, or module identity | [Build-tool checks](../../.github/agent-guides/performance.md#build-tool-checks) | -| Hot updates, route deletion, or watcher replacement | [HMR and watch lifetimes](../../.github/agent-guides/performance.md#hmr-and-watch-lifetimes) and the affected runtime/build checks | -| Dependency versions or bundler compatibility | [Dependency upgrades](../../.github/agent-guides/performance.md#dependency-upgrades) and the affected adapter checks | -| Vendor/runtime chunks or deployment recovery | [Chunk caching and deployment recovery](../../.github/agent-guides/performance.md#chunk-caching-and-deployment-recovery) | -| TypeScript inference or compiler cost | [TypeScript cost](../../.github/agent-guides/performance.md#typescript-cost) | +Read [comparable measurements](references/checks.md#comparable-measurements) for every measurement, then the applicable sections below. Combine sections when a change crosses boundaries. + +| Mechanism | Reference | +| -------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Runtime work, cleanup, hydration, RSC, or devtools | [Runtime and lifecycle checks](references/checks.md#runtime-and-lifecycle-checks); for live requests/rendering, also [browser and server measurements](references/checks.md#browser-and-server-measurements) | +| Compiler work, maps, diagnostics, metadata, or module identity | [Build-tool checks](references/checks.md#build-tool-checks) | +| Hot updates, route deletion, or watcher replacement | [HMR and watch lifetimes](references/checks.md#hmr-and-watch-lifetimes) and the affected runtime/build checks | +| Dependency versions or bundler compatibility | [Dependency upgrades](references/checks.md#dependency-upgrades) and the affected adapter checks | +| Vendor/runtime chunks or deployment recovery | [Chunk caching and deployment recovery](references/checks.md#chunk-caching-and-deployment-recovery) | +| TypeScript inference or compiler cost | [TypeScript cost](references/checks.md#typescript-cost) | ## Investigate and verify diff --git a/.github/agent-guides/performance.md b/skills/performance-review/references/checks.md similarity index 95% rename from .github/agent-guides/performance.md rename to skills/performance-review/references/checks.md index 09b23949f3c..cff3a7bbc16 100644 --- a/.github/agent-guides/performance.md +++ b/skills/performance-review/references/checks.md @@ -1,8 +1,8 @@ # Performance checks -Reference for the [performance-review skill](../../skills/performance-review/SKILL.md). Read the sections relevant to the changed mechanism and affected consumers; the skill defines the investigation procedure. +Reference for the [performance-review skill](../SKILL.md). Read the sections relevant to the changed mechanism and affected consumers; the skill defines the investigation procedure. -Select the existing workload for [client navigation](../../benchmarks/client-nav/README.md), [Start SSR](../../benchmarks/ssr/README.md), or [memory](../../benchmarks/memory/README.md). Compare identical baseline/candidate workloads and add coverage when existing scenarios miss the changed mechanism. There is no aggregate build-CPU benchmark in that set: use the build-tool checks below. For hydration, RPC, paint, or real HTTP behavior, use the affected browser/app workload and the browser/server measurement procedure. For inference cost, use the TypeScript procedure. +Select the existing workload for [client navigation](../../../benchmarks/client-nav/README.md), [Start SSR](../../../benchmarks/ssr/README.md), or [memory](../../../benchmarks/memory/README.md). Compare identical baseline/candidate workloads and add coverage when existing scenarios miss the changed mechanism. There is no aggregate build-CPU benchmark in that set: use the build-tool checks below. For hydration, RPC, paint, or real HTTP behavior, use the affected browser/app workload and the browser/server measurement procedure. For inference cost, use the TypeScript procedure. ## Comparable measurements From 1ed779307989d392a76e8957858b679807dee0b5 Mon Sep 17 00:00:00 2001 From: Sarah Gerrard Date: Tue, 8 Sep 2026 16:04:34 -0700 Subject: [PATCH 8/9] ci: refresh performance comparison with clean base results From 50f0728b58a1743a6aab52e3235d5a736fe4eb8f Mon Sep 17 00:00:00 2001 From: Sarah Gerrard Date: Thu, 24 Sep 2026 18:06:41 -0700 Subject: [PATCH 9/9] docs(skills): drop bundler-specific mention from skill description --- skills/bundle-size-optimization/SKILL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/skills/bundle-size-optimization/SKILL.md b/skills/bundle-size-optimization/SKILL.md index 67c435dd195..be5e058f50a 100644 --- a/skills/bundle-size-optimization/SKILL.md +++ b/skills/bundle-size-optimization/SKILL.md @@ -1,6 +1,6 @@ --- name: bundle-size-optimization -description: Use when working in this repository on JS bundle size, gzip regressions, benchmark scenarios, source attribution, treeshaking, dead code elimination, or bundler annotations, including Webpack. +description: Use when working in this repository on JS bundle size, gzip regressions, benchmark scenarios, source attribution, treeshaking, dead code elimination, or bundler annotations. --- # Bundle Size Optimization