Skip to content

fix: Fix literal underscore paths under pathless layouts#7408

Merged
schiller-manuel merged 1 commit into
mainfrom
fix-7393
May 16, 2026
Merged

fix: Fix literal underscore paths under pathless layouts#7408
schiller-manuel merged 1 commit into
mainfrom
fix-7393

Conversation

@schiller-manuel

@schiller-manuel schiller-manuel commented May 16, 2026

Copy link
Copy Markdown
Collaborator

fixes #7393

Summary by CodeRabbit

  • Chores

    • Added a changeset marking a patch release.
  • Bug Fixes

    • Route generation now correctly preserves literal underscore segments when nested under pathless/layout routes.
  • Documentation

    • Clarified that leading/trailing underscores in explicit virtual route paths are treated literally and guidance on using layouts for pathless routes.
  • Tests

    • Added extensive tests and route fixtures covering escaped-underscore scenarios and pathless/layout removals.

Review Change Stack

@github-actions github-actions Bot added documentation Everything documentation related package: router-generator labels May 16, 2026
@coderabbitai

coderabbitai Bot commented May 16, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3668a974-d45e-423b-83fc-ffe07a229f14

📥 Commits

Reviewing files that changed from the base of the PR and between dafb3fd and f3dc432.

📒 Files selected for processing (34)
  • .changeset/quiet-ravens-explain.md
  • docs/router/routing/virtual-file-routes.md
  • packages/router-generator/src/filesystem/physical/getRouteNodes.ts
  • packages/router-generator/src/filesystem/virtual/getRouteNodes.ts
  • packages/router-generator/src/generator.ts
  • packages/router-generator/src/types.ts
  • packages/router-generator/src/utils.ts
  • packages/router-generator/tests/generator/physical-pathless-layout-escaped-underscore/routeTree.snapshot.ts
  • packages/router-generator/tests/generator/physical-pathless-layout-escaped-underscore/routes/[_]foo.tsx
  • packages/router-generator/tests/generator/physical-pathless-layout-escaped-underscore/routes/[__literal].tsx
  • packages/router-generator/tests/generator/physical-pathless-layout-escaped-underscore/routes/__layout.tsx
  • packages/router-generator/tests/generator/physical-pathless-layout-escaped-underscore/routes/__layout/[_]qux.tsx
  • packages/router-generator/tests/generator/physical-pathless-layout-escaped-underscore/routes/__root.tsx
  • packages/router-generator/tests/generator/physical-pathless-layout-escaped-underscore/routes/_layout.tsx
  • packages/router-generator/tests/generator/physical-pathless-layout-escaped-underscore/routes/_layout/[_]bar.tsx
  • packages/router-generator/tests/generator/physical-pathless-layout-escaped-underscore/routes/_layout/[__double].tsx
  • packages/router-generator/tests/generator/physical-pathless-layout-escaped-underscore/routes/_layout/__nested2.tsx
  • packages/router-generator/tests/generator/physical-pathless-layout-escaped-underscore/routes/_layout/__nested2/[_]deep.tsx
  • packages/router-generator/tests/generator/physical-pathless-layout-escaped-underscore/routes/_layout/_nested.tsx
  • packages/router-generator/tests/generator/physical-pathless-layout-escaped-underscore/routes/_layout/_nested/[_]baz.tsx
  • packages/router-generator/tests/generator/virtual-pathless-layout-escaped-underscore/routeTree.snapshot.ts
  • packages/router-generator/tests/generator/virtual-pathless-layout-escaped-underscore/routes.ts
  • packages/router-generator/tests/generator/virtual-pathless-layout-escaped-underscore/routes/__root.tsx
  • packages/router-generator/tests/generator/virtual-pathless-layout-escaped-underscore/routes/bar.tsx
  • packages/router-generator/tests/generator/virtual-pathless-layout-escaped-underscore/routes/baz.tsx
  • packages/router-generator/tests/generator/virtual-pathless-layout-escaped-underscore/routes/double-bar.tsx
  • packages/router-generator/tests/generator/virtual-pathless-layout-escaped-underscore/routes/double.tsx
  • packages/router-generator/tests/generator/virtual-pathless-layout-escaped-underscore/routes/escaped-bar.tsx
  • packages/router-generator/tests/generator/virtual-pathless-layout-escaped-underscore/routes/escaped.tsx
  • packages/router-generator/tests/generator/virtual-pathless-layout-escaped-underscore/routes/foo.tsx
  • packages/router-generator/tests/generator/virtual-pathless-layout-escaped-underscore/routes/layout.tsx
  • packages/router-generator/tests/generator/virtual-pathless-layout-escaped-underscore/routes/nested-layout.tsx
  • packages/router-generator/tests/generator/virtual-pathless-layout-escaped-underscore/tsr.config.json
  • packages/router-generator/tests/utils.test.ts
✅ Files skipped from review due to trivial changes (10)
  • packages/router-generator/tests/generator/physical-pathless-layout-escaped-underscore/routes/_layout/__nested2.tsx
  • packages/router-generator/tests/generator/virtual-pathless-layout-escaped-underscore/routes.ts
  • .changeset/quiet-ravens-explain.md
  • packages/router-generator/tests/generator/physical-pathless-layout-escaped-underscore/routes/layout/__nested2/[]deep.tsx
  • packages/router-generator/tests/generator/physical-pathless-layout-escaped-underscore/routes/__root.tsx
  • packages/router-generator/tests/generator/virtual-pathless-layout-escaped-underscore/routes/__root.tsx
  • packages/router-generator/tests/generator/virtual-pathless-layout-escaped-underscore/tsr.config.json
  • docs/router/routing/virtual-file-routes.md
  • packages/router-generator/tests/generator/physical-pathless-layout-escaped-underscore/routeTree.snapshot.ts
  • packages/router-generator/tests/generator/virtual-pathless-layout-escaped-underscore/routeTree.snapshot.ts
🚧 Files skipped from review as they are similar to previous changes (22)
  • packages/router-generator/tests/generator/physical-pathless-layout-escaped-underscore/routes/_layout/_nested.tsx
  • packages/router-generator/tests/generator/virtual-pathless-layout-escaped-underscore/routes/escaped-bar.tsx
  • packages/router-generator/tests/generator/virtual-pathless-layout-escaped-underscore/routes/foo.tsx
  • packages/router-generator/tests/generator/physical-pathless-layout-escaped-underscore/routes/layout/[]bar.tsx
  • packages/router-generator/tests/generator/virtual-pathless-layout-escaped-underscore/routes/bar.tsx
  • packages/router-generator/tests/generator/virtual-pathless-layout-escaped-underscore/routes/double-bar.tsx
  • packages/router-generator/src/types.ts
  • packages/router-generator/tests/generator/physical-pathless-layout-escaped-underscore/routes/_layout/[__double].tsx
  • packages/router-generator/tests/generator/virtual-pathless-layout-escaped-underscore/routes/baz.tsx
  • packages/router-generator/tests/generator/physical-pathless-layout-escaped-underscore/routes/__layout.tsx
  • packages/router-generator/tests/generator/virtual-pathless-layout-escaped-underscore/routes/escaped.tsx
  • packages/router-generator/tests/generator/physical-pathless-layout-escaped-underscore/routes/[__literal].tsx
  • packages/router-generator/tests/generator/physical-pathless-layout-escaped-underscore/routes/__layout/[_]qux.tsx
  • packages/router-generator/tests/generator/physical-pathless-layout-escaped-underscore/routes/[_]foo.tsx
  • packages/router-generator/tests/generator/virtual-pathless-layout-escaped-underscore/routes/layout.tsx
  • packages/router-generator/src/filesystem/virtual/getRouteNodes.ts
  • packages/router-generator/tests/utils.test.ts
  • packages/router-generator/tests/generator/virtual-pathless-layout-escaped-underscore/routes/nested-layout.tsx
  • packages/router-generator/tests/generator/physical-pathless-layout-escaped-underscore/routes/_layout.tsx
  • packages/router-generator/src/generator.ts
  • packages/router-generator/tests/generator/virtual-pathless-layout-escaped-underscore/routes/double.tsx
  • packages/router-generator/src/utils.ts

📝 Walkthrough

Walkthrough

Adds per-segment route-path metadata and propagates it through filesystem node construction, utils, and generator logic so bracket-escaped literal underscores (e.g., [_]foo) remain literal after removing pathless/layout segments; includes tests, fixtures, snapshots, docs, and a changeset.

Changes

Escaped Underscore Path Fix

Layer / File(s) Summary
Segment metadata types and core utils
packages/router-generator/src/types.ts, packages/router-generator/src/utils.ts
Adds exported RoutePathSegmentMetadata and helpers (counting, create/join metadata, per-segment underscore stripping) and updates inferFullPath to accept per-segment metadata.
Physical filesystem node metadata embedding
packages/router-generator/src/filesystem/physical/getRouteNodes.ts
Compute and attach _routePathSegmentMetadata for physical route nodes and virtual subtree nodes using create/join helpers.
Virtual filesystem node metadata embedding
packages/router-generator/src/filesystem/virtual/getRouteNodes.ts
Propagate parentOriginalRoutePath, align originalRoutePath for nested virtual routes, and attach _routePathSegmentMetadata for route and layout virtual nodes.
Generator logic integration with metadata
packages/router-generator/src/generator.ts
Import metadata helpers, add getRoutePathSegmentMetadataForPath, use metadata when determining isNonPath and when cleaning node.cleanedPath, and use count helpers for sorting.
Physical & virtual test fixtures and snapshots
packages/router-generator/tests/generator/physical-pathless-layout-escaped-underscore/*, packages/router-generator/tests/generator/virtual-pathless-layout-escaped-underscore/*
Add fixtures exercising escaped/double-underscore patterns under pathless layouts, plus generated routeTree snapshots and virtual routes configs.
Unit tests, docs, changeset
packages/router-generator/tests/utils.test.ts, docs/router/routing/virtual-file-routes.md, .changeset/quiet-ravens-explain.md
Extend tests for removeLayoutSegmentsAndUnderscoresWithEscape and escaped-underscore cases, update docs to clarify route() underscore literal behavior, and add a changeset for a patch release.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • TanStack/router#6250: Modifies virtual getRouteNodes to improve route/original path alignment for escaped underscores.
  • TanStack/router#6156: Updates router-generator escape/underscore handling in utils and generator code paths.

Suggested reviewers

  • nlynzaad
  • chorobin

Poem

"I’m a rabbit with a tiny map, 🐇
of underscores I gently tap,
Where layouts hide and segments slide,
metadata keeps the underscore’s pride.
Now each route hops home without a gap."

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.11% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: Fix literal underscore paths under pathless layouts' directly addresses the main issue: fixing the handling of literal underscores in paths nested under pathless layouts.
Linked Issues check ✅ Passed The PR comprehensively addresses issue #7393 by implementing segment metadata tracking and fixing escape-aware underscore removal throughout the generator pipeline [#7393].
Out of Scope Changes check ✅ Passed All changes are directly related to fixing the underscore escape handling issue: metadata helpers, generator updates, test fixtures, and documentation—no unrelated modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-7393

Comment @coderabbitai help to get the list of available commands and usage tips.

@nx-cloud

nx-cloud Bot commented May 16, 2026

Copy link
Copy Markdown
Contributor

View your CI Pipeline Execution ↗ for commit dafb3fd

Command Status Duration Result
nx affected --targets=test:eslint,test:unit,tes... ✅ Succeeded 6m 47s View ↗
nx run-many --target=build --exclude=examples/*... ✅ Succeeded 47s View ↗

☁️ Nx Cloud last updated this comment at 2026-05-16 02:01:40 UTC

@github-actions

github-actions Bot commented May 16, 2026

Copy link
Copy Markdown
Contributor

🚀 Changeset Version Preview

1 package(s) bumped directly, 8 bumped as dependents.

🟩 Patch bumps

Package Version Reason
@tanstack/router-generator 1.167.1 → 1.167.2 Changeset
@tanstack/react-start 1.168.1 → 1.168.2 Dependent
@tanstack/react-start-rsc 0.1.1 → 0.1.2 Dependent
@tanstack/router-cli 1.167.1 → 1.167.2 Dependent
@tanstack/router-plugin 1.168.1 → 1.168.2 Dependent
@tanstack/router-vite-plugin 1.167.1 → 1.167.2 Dependent
@tanstack/solid-start 1.168.1 → 1.168.2 Dependent
@tanstack/start-plugin-core 1.170.1 → 1.170.2 Dependent
@tanstack/vue-start 1.168.1 → 1.168.2 Dependent

@github-actions

github-actions Bot commented May 16, 2026

Copy link
Copy Markdown
Contributor

Bundle Size Benchmarks

  • Commit: 76eb673ee3e7
  • Measured at: 2026-05-16T01:55:40.382Z
  • Baseline source: history:13432ad9ff9f
  • Dashboard: bundle-size history
Scenario Current (gzip) Delta vs baseline Initial gzip Raw Brotli Trend
react-router.minimal 87.29 KiB 0 B (0.00%) 87.15 KiB 274.07 KiB 75.85 KiB ▁▁▁▁███████
react-router.full 90.82 KiB 0 B (0.00%) 90.68 KiB 285.58 KiB 78.89 KiB ▁▁▁▁███████
solid-router.minimal 35.51 KiB 0 B (0.00%) 35.39 KiB 106.36 KiB 31.97 KiB ▁▁▁▁███████
solid-router.full 40.23 KiB 0 B (0.00%) 40.10 KiB 120.58 KiB 36.14 KiB ▁▁▁▁███████
vue-router.minimal 53.29 KiB 0 B (0.00%) 53.15 KiB 151.51 KiB 47.85 KiB ▁▁▁▁███████
vue-router.full 58.41 KiB 0 B (0.00%) 58.28 KiB 167.69 KiB 52.39 KiB ▁▁▁▁███████
react-start.minimal 101.97 KiB 0 B (0.00%) 101.83 KiB 322.51 KiB 88.17 KiB ▁▁▁▃███████
react-start.full 105.41 KiB 0 B (0.00%) 105.27 KiB 332.84 KiB 91.19 KiB ▁▁▁▄███████
react-start.rsbuild.minimal 99.59 KiB 0 B (0.00%) 99.42 KiB 316.98 KiB 85.72 KiB ▁▁▁▄███████
react-start.rsbuild.full 102.89 KiB 0 B (0.00%) 102.72 KiB 327.41 KiB 88.49 KiB ▁▁▁▃███████
solid-start.minimal 49.61 KiB 0 B (0.00%) 49.48 KiB 152.48 KiB 43.78 KiB ▁▁▁▄███████
solid-start.full 55.40 KiB 0 B (0.00%) 55.27 KiB 169.39 KiB 48.72 KiB ▁▁▁▄███████

Current gzip tracks all emitted client JS chunks. Initial gzip tracks only the entry/import graph. Trend sparkline is historical current gzip ending with this PR measurement; lower is better.

@pkg-pr-new

pkg-pr-new Bot commented May 16, 2026

Copy link
Copy Markdown
More templates

@tanstack/arktype-adapter

npm i https://pkg.pr.new/@tanstack/arktype-adapter@7408

@tanstack/eslint-plugin-router

npm i https://pkg.pr.new/@tanstack/eslint-plugin-router@7408

@tanstack/eslint-plugin-start

npm i https://pkg.pr.new/@tanstack/eslint-plugin-start@7408

@tanstack/history

npm i https://pkg.pr.new/@tanstack/history@7408

@tanstack/nitro-v2-vite-plugin

npm i https://pkg.pr.new/@tanstack/nitro-v2-vite-plugin@7408

@tanstack/react-router

npm i https://pkg.pr.new/@tanstack/react-router@7408

@tanstack/react-router-devtools

npm i https://pkg.pr.new/@tanstack/react-router-devtools@7408

@tanstack/react-router-ssr-query

npm i https://pkg.pr.new/@tanstack/react-router-ssr-query@7408

@tanstack/react-start

npm i https://pkg.pr.new/@tanstack/react-start@7408

@tanstack/react-start-client

npm i https://pkg.pr.new/@tanstack/react-start-client@7408

@tanstack/react-start-rsc

npm i https://pkg.pr.new/@tanstack/react-start-rsc@7408

@tanstack/react-start-server

npm i https://pkg.pr.new/@tanstack/react-start-server@7408

@tanstack/router-cli

npm i https://pkg.pr.new/@tanstack/router-cli@7408

@tanstack/router-core

npm i https://pkg.pr.new/@tanstack/router-core@7408

@tanstack/router-devtools

npm i https://pkg.pr.new/@tanstack/router-devtools@7408

@tanstack/router-devtools-core

npm i https://pkg.pr.new/@tanstack/router-devtools-core@7408

@tanstack/router-generator

npm i https://pkg.pr.new/@tanstack/router-generator@7408

@tanstack/router-plugin

npm i https://pkg.pr.new/@tanstack/router-plugin@7408

@tanstack/router-ssr-query-core

npm i https://pkg.pr.new/@tanstack/router-ssr-query-core@7408

@tanstack/router-utils

npm i https://pkg.pr.new/@tanstack/router-utils@7408

@tanstack/router-vite-plugin

npm i https://pkg.pr.new/@tanstack/router-vite-plugin@7408

@tanstack/solid-router

npm i https://pkg.pr.new/@tanstack/solid-router@7408

@tanstack/solid-router-devtools

npm i https://pkg.pr.new/@tanstack/solid-router-devtools@7408

@tanstack/solid-router-ssr-query

npm i https://pkg.pr.new/@tanstack/solid-router-ssr-query@7408

@tanstack/solid-start

npm i https://pkg.pr.new/@tanstack/solid-start@7408

@tanstack/solid-start-client

npm i https://pkg.pr.new/@tanstack/solid-start-client@7408

@tanstack/solid-start-server

npm i https://pkg.pr.new/@tanstack/solid-start-server@7408

@tanstack/start-client-core

npm i https://pkg.pr.new/@tanstack/start-client-core@7408

@tanstack/start-fn-stubs

npm i https://pkg.pr.new/@tanstack/start-fn-stubs@7408

@tanstack/start-plugin-core

npm i https://pkg.pr.new/@tanstack/start-plugin-core@7408

@tanstack/start-server-core

npm i https://pkg.pr.new/@tanstack/start-server-core@7408

@tanstack/start-static-server-functions

npm i https://pkg.pr.new/@tanstack/start-static-server-functions@7408

@tanstack/start-storage-context

npm i https://pkg.pr.new/@tanstack/start-storage-context@7408

@tanstack/valibot-adapter

npm i https://pkg.pr.new/@tanstack/valibot-adapter@7408

@tanstack/virtual-file-routes

npm i https://pkg.pr.new/@tanstack/virtual-file-routes@7408

@tanstack/vue-router

npm i https://pkg.pr.new/@tanstack/vue-router@7408

@tanstack/vue-router-devtools

npm i https://pkg.pr.new/@tanstack/vue-router-devtools@7408

@tanstack/vue-router-ssr-query

npm i https://pkg.pr.new/@tanstack/vue-router-ssr-query@7408

@tanstack/vue-start

npm i https://pkg.pr.new/@tanstack/vue-start@7408

@tanstack/vue-start-client

npm i https://pkg.pr.new/@tanstack/vue-start-client@7408

@tanstack/vue-start-server

npm i https://pkg.pr.new/@tanstack/vue-start-server@7408

@tanstack/zod-adapter

npm i https://pkg.pr.new/@tanstack/zod-adapter@7408

commit: f3dc432

@schiller-manuel schiller-manuel merged commit 742941e into main May 16, 2026
19 checks passed
@schiller-manuel schiller-manuel deleted the fix-7393 branch May 16, 2026 02:08
@github-actions github-actions Bot mentioned this pull request May 16, 2026
birkskyum added a commit that referenced this pull request Jun 1, 2026
* fix: republish react-start-rsc dependency chain

* ci: changeset release

* chore: sync published start package versions

* ci: changeset release

* fix publishing

* ci: changeset release

* fix

* ci: apply automated fixes

* ci: changeset release

* refactor: switch router stores to atom get/set API (#7150)

* ci: changeset release

* refactor: shorten internal router store names (#7152)

* ci: changeset release

* chore(types): re-export SearchMiddleware type from react-router (#7087)

* fix(start-plugin-core): reuse deduped server function ids across compilers (#7153)

* ci: changeset release

* fix(router-core): avoid false notFound matches for proxied loader data (#7156)

* fix(router-core): avoid false notFound matches for proxied loader data

* test(react-start): cover proxied loader data notFound regression

* test(react-start): move notFound regression to rsc direct loader

* chore: add changeset

* ci: changeset release

* fix: reduce start SSR manifest asset duplication (#7157)

Co-authored-by: schiller-manuel <schiller-manuel@users.noreply.github.com>
Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>

* ci: changeset release

* docs: fix file extension syntax for prefix/suffix routing examples (#7149)

docs: fix file-based routing examples with literal dots

* chore: bump to h3 v2 rc.20 (#7140)

* ci: changeset release

* chore: stabilize tests (#7159)

* chore: add vite 8 to peer deps (#7160)

Co-authored-by: schiller-manuel <6340397+schiller-manuel@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>

* ci: changeset release

* chore(start-server-core): remove unnecessary `any` in `getRequestHeaders` (#7164)

* fix(router-generator): harden route file transform rewrites (#7167)

* fix docs (#7168)

* ci: changeset release

* fix(router-plugin): update vite-plugin-solid peer dependency to support version 3.0.0-0 (#7170)

* ci: changeset release

* fix(router-generator): normalize virtual physical subtree paths (#7169)

Co-authored-by: schiller-manuel <6340397+schiller-manuel@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>

* ci: changeset release

* fix: unify virtual module handling for Start Vite plugins (#7178)

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: CodeRabbit <noreply@coderabbit.ai>
Co-authored-by: schiller-manuel <schiller-manuel@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>

* ci: changeset release

* fix: add react-server server export for react-start (#7180)

* ci: changeset release

* fix(docs): correct server function name in example (#7173)

* fix react server exports for start and react-router (#7183)

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>

* chore: stabilize tests (#7185)

* ci: changeset release

* fix(router-core): avoid intermediate success state for async notFound (#7184)

* ci: changeset release

* Add a React Start server-components skill (#7181)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>

* chore: add sharding for playwright tests (#7187)

* add sharding

* fix dependnancy

* fix playwright shard port reuse

* full bust

* throw more runners into this run

* try to throw money at it and see the effect

* tune

* fix react-router shared route css persistence on nav (#7186)

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* ci: changeset release

* chore: stabilize test (#7192)

* fix(start): include Vite style.css when cssCodeSplit is disabled (#7191)

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* ci: changeset release

* chore: bump solid-js override 1.9.12 (#7202)

* chore: bump query override to 5.99.0 (#7203)

* fix(solid-router): use keyed Show in Outlet to fix child route rendering with useQuery (#7204)

* ci: changeset release

* docs(start): fix authenticated routes doc URL (#7214)

* chore(deps): vitest 4.1.4 (#7212)

* stabilize rsc tests (#7217)

* fix(react-router): prevent webpack static analysis of React.use with let binding (#7182)

* ci: changeset release

* stabilize test (#7220)

* chore: unify react-start basic e2e mode projects (#7206)

* feat(nx): support rsbuild in Playwright inference (#7221)

* test(e2e-rsc): migrate to playwrightModes and dynamic inferred dist (#7222)

* fix(nx): align playwright mode build target naming (#7223)

* docs(start): add missing space after comma in 'Handling requests with a body' (#7234)

* update intent workflow (#7243)

* rsbuild plugin (#7228)

Co-authored-by: neverland <chenjiahan.jait@bytedance.com>
Co-authored-by: Keven Arroyo <dake.3601@gmail.com>

* ci: changeset release

* fix: Split Start plugin core (#7249)

* ci: changeset release

* update intent workflow (#7244)

* fix: asset sorting (#7251)

* ci: changeset release

* chore(deps): update Rsbuild related deps to v2.0.1 (#7245)

* fix actions again? (#7252)

* inline css (#7253)

* ci: changeset release

* fix: do not import 'react-refresh/runtime' (#7255)

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>

* ci: changeset release

* chore(examples): checkin git outdated route-tree files for react and solid examples (#7257)

* add new bundlesize measurements for rsbuild (#7256)

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>

* refactor(router-plugin): upgrade unplugin to `v3` (#7258)

* refactor(router-plugin): upgrade unplugin to `v3`

* refactor(start-client-core): use a more explicit typing to `CustomFetch` type

* chore(examples): runtime enforce for needing the `VITE_CONVEX_URL`

* ci: changeset release

* fix: issue 7240 causing fouc (#7250)

* fix: server middleware type in solid-router (#7260)

* replace tsx by jiti (#7261)

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>

* ci: changeset release

* docs: Fix server function middleware prop (#7262)

* feat(query): add support for custom dehydrate and hydrate options in SSR integration (#7246)

Co-authored-by: schiller-manuel <6340397+schiller-manuel@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>

* ci: changeset release

* fix: use loader data goes undefined (#7265)

* fix: streaming when using Await component (#7264)

* ci: changeset release

* fix(solid-start): bundle solid-query packages during SSR to fix duplicate QueryClientContext (#6151) (#7267)

* ci: changeset release

* fix(solid-router): hydration mismatch for ssr='data-only' with pendingComponent (#7266)

* docs(router): fix typo in doc (#7268)

* ci: changeset release

* fix(router-core): wildcard nodes respect DFS priority like other nodes in route matching (#7273)

* ci: changeset release

* fix(solid-router): enable route component HMR for Solid

* ci: apply automated fixes

* Revert "ci: apply automated fixes"

This reverts commit 7122f28.

* Revert "fix(solid-router): enable route component HMR for Solid"

This reverts commit b86b061.

* fix(react-start-rsc): re-export renderable types from public entries (#7278)

* fix(react-start-rsc): re-export renderable types from public entries

* changeset - patch

* ci: changeset release

* fix: disabled topLevelVar (#7293)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>

* ci: changeset release

* fix: fix exports for react-start so useServerFn is available with RSC (#7292)

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>

* feat: match params (#7263)

* ci: changeset release

* fix(start-plugin-core): sort server fn manifest entries for deterministic build output (#7287)

Co-authored-by: Dor Alagem <doralagem@MacBook-Pro-sl-Dor.local>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Manuel Schiller <meisterpink@gmail.com>

* docs: remove redundant code example from query integration doc (#7298)

* ci: changeset release

* fix: Ignore fully type-only imports and re-exports when collecting im… (#7305)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>

* ci: changeset release

* feat: `strict: false` for server functions (#7277)

* feat: `strict: false` for server functions

Add a `strict` option to `createServerFn` for type-level server function serialization checks

* feat: `strict: false` for server functions [Self-Healing CI Rerun]

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>

* ci: changeset release

* fix: parse params union inference (#7306)

* ci: changeset release

* feat: rsc css (#7310)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>

* ci: changeset release

* fix(router-plugin): isolate route metadata per plugin instance (#7313)

* ci: changeset release

* docs: Improve key differences b/w Start Server Functions and Next.js Server Actions (#7312)

* fix(deps): move fetchdts from devDependencies to dependencies (#7317)

* Revise bug report template for clarity and requirements

Updated the bug report template to clarify the requirements for a reproducer project and modified some labels and descriptions for better clarity.

* ci: changeset release

* chore: fix duplicate "the" typo across router packages (#7323)

* feat: early hints (#7324)

* ci: changeset release

* feat: Link header (#7327)

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>

* ci: changeset release

* docs(skills): address 8 agent failure modes from user feedback (#7314)

* docs(skills): address 8 agent failure modes from external feedback

Adds new start-core/auth-server-primitives skill (sessions, cookies,
OAuth+PKCE, password-reset enumeration defense, CSRF, rate limiting,
session rotation) and updates 8 existing skills + matching docs to fix
patterns where agents produce insecure or wrong-framework output.

Skill changes:
- new: start-core/auth-server-primitives (server half of auth)
- router-core/auth-and-guards: route guard != RPC guard
- start-core/server-functions: wrong import path, RPC auth required,
  Cache-Control public is a cross-tenant leak, wrong-framework patterns
- start-core/middleware: wrong import path, sendContext shape vs access
  (3-layer wrong/still-wrong/correct), authMiddleware framing
- start-core/execution-model: file markers (server-only/client-only),
  module-level process.env is undefined under Worker SSR
- start-core/deployment: cloudflare env-at-request-time
- router-core/ssr: wrong file structures (next.js, react-router-dom)
- router-core/type-safety: wrong-framework imports + structures

Docs updated to mirror each skill change so source-of-truth and the
intent-indexed skill stay in sync. New authentication-server-primitives
guide is the long-form companion to the new skill.

intent validate: 30 skill files pass (was 29).

* ci: apply automated fixes

* docs(skills): address coderabbit review feedback

- Fix internal docs links to use correct relative paths instead of an
  absolute /start/latest/... URL and missing one ../ segment
- Remove blank line inside auth-and-guards blockquote (markdownlint MD028)
- Restore overload pattern in type-safety ValidateNavigateOptions and
  ValidateRedirectOptions examples; the casts I had introduced stripped
  generic context and contradicted the skill's own no-cast rule
- Add db.sessions.revokeAllForUser before create in login rotation
  snippets so the example matches the prose
- Soften useServerFn guidance: it's required only when the server
  function throws redirect/notFound; plain-data calls work directly and
  via useMutation/useQuery

* ci: apply automated fixes

* docs(skills): compress type-safety to stay under 500-line cap

Prettier's autofix expanded my single-line overload signatures across
multiple lines, pushing the file over the 500-line limit. Drop the
redundant fetchOrRedirect example (same pattern as useDelayedNavigate)
and describe ValidateRedirectOptions usage in prose instead.

* docs(skills): CSRF origin check should compare full origin, not host alone

Comparing only new URL(origin).host against APP_HOST silently accepts a
mismatched scheme — http://example.com would pass a check meant for
https://example.com. Compare the full origin (scheme + host + port)
against APP_ORIGIN instead. Same fix in skill and docs.

* docs(skills): make useDelayedNavigate callback truly return void

The callback returned the result of setTimeout (a timer handle), not
void as the public overload's return type implied. Wrap in a block so
the example matches the declared return type.

Skipped the related nitpick to add a separate redirect example — the
existing prose already describes the same overload pattern, and a
duplicate example would push the file close to the 500-line cap that
prettier autofix has been bumping us against.

* docs(skills): fix two real bugs in auth-server-primitives examples

1. Cookie parser truncated values containing '='. Signed cookies, JWTs,
   and base64-padded values all use '='. Use indexOf to split on the
   FIRST '=' only.

2. Login example short-circuited verifyPasswordHash on user-not-found,
   contradicting the prose's "same time, same error" claim — the
   no-user branch returned instantly while wrong-password spent ~100ms
   hashing, leaking account existence over the wire. Always verify
   against a hash; use a precomputed DUMMY_PASSWORD_HASH when the user
   is missing, then combine with the user-exists bit for the final ok.

Same fixes in the SKILL.md and the docs companion.

* docs(skills): address manuel's review on react-specific guides

- middleware.md, server-functions.md: drop cross-framework <framework>
  placeholders; this is the React-specific guide, just say
  @tanstack/react-start
- execution-model.md: drop the same trailing line about solid-start /
  vue-start paths
- hosting.md: remove the Cloudflare env-handling subsection — the
  general per-request rule lives in environment-variables.md and
  doesn't need to be repeated under a specific host
- environment-variables.md: mention the cloudflare:workers env binding
  as the canonical Cloudflare way to read env (including module scope),
  per Manuel's link to the Cloudflare docs
- deployment skill: same upgrade — show the cloudflare:workers env
  pattern alongside the per-request handler approach

* docs(skills): drop redundant server-only marker in session example

The file already imports from @tanstack/react-start/server, which is
on import protection's default client-deny specifier list. The
side-effect marker is redundant — drop it. Same fix in skill and docs.

* docs(skills): drop wrong-import-path mistakes — TS already catches them

Manuel pointed out that TypeScript catches both common wrong paths:
'@tanstack/react-router' has no exported member createServerFn /
createMiddleware, and '@tanstack/start' is "Cannot find module". Skill
space is precious; the items don't earn their slot if tsc handles them.

Removed:
- Common Mistake "Wrong import path" from server-functions and
  middleware skills (renumbered the remaining mistakes)
- The matching top-of-file CRITICAL line in both skills
- The "Import path" callouts in the middleware and server-functions
  docs

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>

* remove old intent artifacts (#7333)

* fix(start-server-core): fall back to GET handler for HEAD requests (RFC 9110 §9.3.2) (#7325)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>

* ci: changeset release

* test: add reproducer for #2514 (#7336)

* test: reproducer for #2547 (#7337)

* fix: fix plain TypeScript parser handling (#7342)

* ci: changeset release

* fix: disable rsbuild server compression (#7348)

* fix: update deps (#7340)

* ci: changeset release

* update bundlesize benchmark (#7356)

* fix: Bump jiti to 2.7.0 (#7355)

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: schiller-manuel <6340397+schiller-manuel@users.noreply.github.com>

* ci: apply automated fixes

* ci: changeset release

* Document server function strict serialization options (#7358)

* Document server function strict option

Agent-Logs-Url: https://github.com/TanStack/router/sessions/8deabe7a-7412-455e-9111-97a13cb4582e

Co-authored-by: schiller-manuel <6340397+schiller-manuel@users.noreply.github.com>

* Refine strict docs wording

Agent-Logs-Url: https://github.com/TanStack/router/sessions/e2857d6e-314e-496c-b99b-78c207c28c77

Co-authored-by: schiller-manuel <6340397+schiller-manuel@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: schiller-manuel <6340397+schiller-manuel@users.noreply.github.com>

* feat(start): CSRF middleware (#7373)

* fix(router-core): fix missing closing paren in CSS.supports check for view transition types (#7369)

* fix: fix jiti usage for tsconfig paths (#7382)

* Enable jiti tsconfig path aliases

Agent-Logs-Url: https://github.com/TanStack/router/sessions/0a481c9b-eb97-4543-acc5-71d43b97d386

Co-authored-by: schiller-manuel <6340397+schiller-manuel@users.noreply.github.com>

* Use fixture for jiti tsconfig aliases

Agent-Logs-Url: https://github.com/TanStack/router/sessions/182b9baa-9e54-4813-a322-c33a1da5417d

Co-authored-by: schiller-manuel <6340397+schiller-manuel@users.noreply.github.com>

* Track fixture path alias helper

Agent-Logs-Url: https://github.com/TanStack/router/sessions/182b9baa-9e54-4813-a322-c33a1da5417d

Co-authored-by: schiller-manuel <6340397+schiller-manuel@users.noreply.github.com>

* Add router-generator changeset

Agent-Logs-Url: https://github.com/TanStack/router/sessions/ab1a42cc-7326-4e36-a656-f01179221cee

Co-authored-by: schiller-manuel <6340397+schiller-manuel@users.noreply.github.com>

* Format virtual config fixture

Agent-Logs-Url: https://github.com/TanStack/router/sessions/22194166-b3a7-431e-b723-ad594d4b0405

Co-authored-by: schiller-manuel <6340397+schiller-manuel@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: schiller-manuel <6340397+schiller-manuel@users.noreply.github.com>

* Update bundle-size.yml

* Update labeler.yml

* Update bundle-size.yml

* ci: add pinGitHubActionDigests (#7387)

* chore(deps): pin dependencies (#7388)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* minor semver bump to all packages (#7395)

* ci: zizmor (#7389)

* chore(pnpm): update pnpm to v11 (#7392)

* chore: add CODEOWNERS file (#7394)

* chore: add CODEOWNERS file

* chore: add Nx and NPMRC

* Update .github/CODEOWNERS

---------

Co-authored-by: Nicolas Beaussart <nic.beaussart@gmail.com>

* fix: revert plugin changes, createCsrfMiddleware compilation, fix HMR tests (#7400)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Manuel Schiller <manuel.schiller@caligano.de>

* ci: align release workflow to query (#7404)

* ci: Version Packages (#7405)

ci: changeset release

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* feat(start): add inline CSS runtime controls and asset URL templates (#7380)

* ci: Version Packages (#7407)

* fix: Fix literal underscore paths under pathless layouts (#7408)

* ci: Version Packages (#7409)

ci: changeset release

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* feat(router-core): params.priority route option as tie breaker in matching algorithm (#7411)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>

* ci: Version Packages (#7413)

* fix(router-core): hydrate before initial client route match (#7416)

* ci: Version Packages (#7417)

* fix(router-plugin): detect typed root route context for HMR (#7420)

* ci: Version Packages (#7421)

* fix: fix route mismatch warnings and HMR route indexes (#7422)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>

* ci: Version Packages (#7423)

* security: stricter pnpm config blockExoticSubdeps & trustPolicy (#7425)

* docs(start): use router package for module declaration to type the request context (#7427)

* feat: deferred hydration (#7362)

* feat: deferred hydration

* fix

* tests

* solid tests

* Changes before error encountered

Agent-Logs-Url: https://github.com/TanStack/router/sessions/5263c469-75c2-4470-bd4c-86f9b43964f4

Co-authored-by: schiller-manuel <6340397+schiller-manuel@users.noreply.github.com>

* chore: address hydration review follow-ups

Agent-Logs-Url: https://github.com/TanStack/router/sessions/16e27113-ff01-4de8-aded-b9be9f6dd4ff

Co-authored-by: schiller-manuel <6340397+schiller-manuel@users.noreply.github.com>

* fix(start-client-core): correct import order in hydrateStart.ts

Co-authored-by: schiller-manuel <schiller-manuel@users.noreply.github.com>

* chore: remove tracked nx self-healing artifacts

Agent-Logs-Url: https://github.com/TanStack/router/sessions/95750760-1348-437c-8f73-cc45e899003a

Co-authored-by: schiller-manuel <6340397+schiller-manuel@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: schiller-manuel <6340397+schiller-manuel@users.noreply.github.com>
Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
Co-authored-by: schiller-manuel <schiller-manuel@users.noreply.github.com>

* chore: update @swc/core in example/react/quickstart-webpack-file-based (#7434)

* chore: update @swc/core in example/react/quickstart-webpack-file-based

* chore: update @swc/core in example/react/quickstart-webpack-file-based [Self-Healing CI Rerun]

---------

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>

* docs(start): compare deferred hydration to Astro islands (#7438)

Adds a short mental-model section answering the common question
about how TanStack Start's deferred hydration relates to Astro
islands.

* docs(start): compare deferred hydration to React selective hydration (#7442)

Adds a short comparison section answering the common question about how
TanStack Start's deferred hydration relates to React 18's selective
hydration. The framing: selective hydration controls the order of
inevitable hydration work; deferred hydration controls whether and when
that work happens at all.

* chore: update chokidar to v5 (#7439)

* chore: update @rolldown/pluginutils to 1.0.1 (#7440)

* chore: update zod to v4.4.3 (#7441)

* chore: update zod to v4.4.3

* fix(start): preserve route path defaults

* fix(examples): resolve zod 4 build failures

* chore: update express and webpack-dev-server (#7443)

* chore: update express to v5.2.1

* chore: update webpack-dev-server to v5.2.4

* ci: Version Packages (#7435)

ci: changeset release

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* chore: ignore nx generated folders (#7451)

* perf: optimize and test rewrite (#7448)

optimize and test rewrite

* fix: fix scroll restoration issues (#7447)

Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>

* ci: Version Packages (#7452)

* fix: Fix escaped underscore index route generation (#7453)

* ci: Version Packages (#7454)

* ci: fix release notes diff range after Release PR flow (#7456)

* fix(start): explicitly re-export public API to survive SSR cold-start cycle (#7466)

* fix: Fix hash scrolling with `resetScroll={false}` (#7464)

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* chore: enforce pnpm 11 (#7465)

* ci: Version Packages (#7467)

* chore: only run autofix on PRs (#7469)

* feat(start): support rsbuild iife client output (#7477)

* ci: Version Packages (#7478)

* fix: bundled dev support for vite (#7482)

* ci: Version Packages (#7483)

* fix(start): avoid encoded virtual adapter ids in vite dev (#7484)

* fix release (#7487)

* ci: Version Packages (#7485)

* chore: migrate changesets changelog generator (#7490)

* fix: Fix Hydrate re-exports to avoid circular HMR updates (#7492)

* ci: Version Packages (#7493)

* fix(start): emit boot-sibling chunks as scripts for IIFE entries (#7501)

Co-authored-by: Keven Arroyo <kevenarroyo@microsoft.com>

* ci: Version Packages (#7502)

* chore: stabilize tests (#7503)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>

* fix: fix streaming (#7497)

* ci: Version Packages (#7504)

ci: changeset release

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* ci: update all actions (#7506)

* ci: update all actions

* disable package manager cache

* fix: fix primitive beforeLoad errors (#7505)

* fix: fix primitive beforeLoad errors

* ci: apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>

* ci: Version Packages (#7508)

* feat(rsbuild): add RSC support (#7509)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>

* ci: Version Packages (#7513)

* fix(solid): resolve Solid 2 merge issues

* ci: apply automated fixes

* use v2 query

* fix solid v2 imports

* solid v2 port of new e2e

* solid v2

* nxignore solid v1 peer deps

* use "@rsbuild/plugin-solid": "^2.0.0-beta.0",

* fix hydrationscript

* add nohydration

* remove early return

* fix: solid-start hydration

* fix: update createEffect to return true for hydration signals

* remove nxignore

* add back nxignore for 3rdparty deps

* $_TSR stub to fix serialization tests

* Revert "$_TSR stub to fix serialization tests"

This reverts commit ef802e9.

* fix: update solid selective ssr links

* fix: stream solid Await fallback

* align solid-start basic by removing test:e2e

* remove unusese build:prerender build:spa

* fix: clean up solid hydrate fallback dom

* fix: resolve solid wrapper children

* fix: stabilize serialization stream e2e test

* fix: keep SSR globals through document parse

* html standard mode

* clean

* fix: pass solid ssr manifest

* use await fallback

---------

Co-authored-by: Tanner Linsley <tannerlinsley@gmail.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Manuel Schiller <manuel.schiller@caligano.de>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Flo <fpellet@ensc.fr>
Co-authored-by: James Howard <james@reetgood.co.uk>
Co-authored-by: schiller-manuel <schiller-manuel@users.noreply.github.com>
Co-authored-by: nx-cloud[bot] <71083854+nx-cloud[bot]@users.noreply.github.com>
Co-authored-by: Mohamed Khaled <mohamedkhaled012@yahoo.com>
Co-authored-by: Birk Skyum <74932975+birkskyum@users.noreply.github.com>
Co-authored-by: schiller-manuel <6340397+schiller-manuel@users.noreply.github.com>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Ulrich Stark <github@ustark.de>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: CodeRabbit <noreply@coderabbit.ai>
Co-authored-by: MoonBrillante <32852571+MoonBrillante@users.noreply.github.com>
Co-authored-by: Coding Cossack <108333654+CodingCossack@users.noreply.github.com>
Co-authored-by: Nicolas Beaussart <nic.beaussart@gmail.com>
Co-authored-by: Pavan Shinde <pavann97@gmail.com>
Co-authored-by: mixelburg <52622705+mixelburg@users.noreply.github.com>
Co-authored-by: Mukunda Rao Katta <mukunda.vjcs6@gmail.com>
Co-authored-by: Sarah Gerrard <gerrardsarah@gmail.com>
Co-authored-by: neverland <chenjiahan.jait@bytedance.com>
Co-authored-by: Keven Arroyo <dake.3601@gmail.com>
Co-authored-by: Sean Cassiere <33615041+SeanCassiere@users.noreply.github.com>
Co-authored-by: Abhishek Raj <abhi@raj.me>
Co-authored-by: Dominik Dorfmeister 🔮 <office@dorfmeister.cc>
Co-authored-by: Birk Skyum <birk.skyum@pm.me>
Co-authored-by: Keven Arroyo <kevenarroyo@microsoft.com>
Co-authored-by: Dor Alagem <dor3382@gmail.com>
Co-authored-by: Dor Alagem <doralagem@MacBook-Pro-sl-Dor.local>
Co-authored-by: Manuel Schiller <meisterpink@gmail.com>
Co-authored-by: Franklin Shera <fshera96@gmail.com>
Co-authored-by: Tom Smithhisler <tomsmithhisler@gmail.com>
Co-authored-by: dfedoryshchev <64079946+dfedoryshchev@users.noreply.github.com>
Co-authored-by: Sarah Gerrard <hello@sarahgerrard.me>
Co-authored-by: Zelys <zelys@dfkhelper.com>
Co-authored-by: Shkumbin Hasani <34962865+shkumbinhasani@users.noreply.github.com>
Co-authored-by: Lachlan Collins <1667261+lachlancollins@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Harry Whorlow <79278353+harry-whorlow@users.noreply.github.com>
Co-authored-by: Corbin Crutchley <git@crutchcorn.dev>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
jonastemplestein added a commit to iterate/iterate that referenced this pull request Jun 10, 2026
…rk (#1411)

# Golden-path `apps/os`: TanStack Start + oRPC + Cloudflare Workers

Relentlessly simplifies `apps/os` onto the golden path, fixes the
phantom
`/projects/new` route the sidebar linked to, and — found during review —
**closes a live production secret leak**. Full decision log:
`apps/os/docs/simplification-decisions.md`.

---

## 1. The bug that started this: a sidebar link to a 404

The sidebar linked to `/projects/new`, which 404'd. The route file was:

```tsx
// routes/_app/projects/[_]new.tsx   (the [_] escapes a leading underscore)
export const Route = createFileRoute("/_app/projects/_new")({ ... });
```

In `routeTree.gen.ts` this produced a route with **`path: ''`** — a
pathless
route that the type system still advertised as the linkable target
`/projects/new`, so `<Link to="/projects/new">` typechecked but **never
matched
at runtime**. It hit an upstream router-generator bug with escaped
underscores
under a pathless layout (TanStack/router#7408, #7453), and the version
we were
pinned to (`@tanstack/react-start@1.167.5` → bundled
`router-generator@1.166.17`)
predated the fix.

**Why it was bad:** typed-but-unmatchable routes are invisible to CI —
`tsc`
passes against a lie. The only way to catch it is to keep the generated
route
tree honest.

**Fix:**
- Renamed to a top-level `routes/_app/new-project.tsx` →
**`/new-project`**
  (also can't ever collide with a `$projectSlug`).
- Bumped the TanStack pair to the version with the generator fix
(`react-start 1.168.25` / `react-router 1.170.15`, bumped together
because
  `react-start` hard-pins `react-router`).
- Added `scripts/generate-route-tree.ts` + `routes:check`, wired into
  `typecheck`, so **CI now fails on a stale/phantom route tree**:

```ts
// regenerates routeTree.gen.ts with the SAME generator + config the vite build
// uses; --check restores the file and exits 1 if it drifted.
const before = readFileSync(routeTreePath, "utf8");
try { await new Generator({ config, root }).run(); after = readFileSync(routeTreePath, "utf8"); }
finally { if (checkOnly) writeFileSync(routeTreePath, before); }
if (before !== after && checkOnly) { console.error("routeTree.gen.ts is stale…"); process.exit(1); }
```

---

## 2. Removed the "apps framework" over-abstraction

`apps/os` predated the decision to make OS the only product app. It
still paid
for generality it no longer needs.

### 2a. `app.ts` (manifest + config) → `config.ts`; manifest deleted

A manifest object existed only to parameterize shared helpers by app
identity.
With one product app, every consumer can just say `"os"`:

```ts
// before — alchemy.run.ts
const ctx = await initAlchemy(manifest, AppConfig, env);
// after
const ctx = await initAlchemy("os", AppConfig, env);
```

```ts
// before — withEvlog took the whole AppManifest
withEvlog({ request, manifest, config, executionCtx }, …)
// after — a plain { name, slug }; log field names kept stable for dashboards
withEvlog({ request, app: { name: "@iterate-com/os", slug: "os" }, config, executionCtx: ctx }, …)
```

Config utilities (`redacted`/`publicValue`/`parseAppConfigFromEnv`/
`extractPublicConfigSchema`) and request logging moved out of the
`apps/*`
namespace to `@iterate-com/shared/config` and
`@iterate-com/shared/evlog`. The
old `@iterate-com/shared/apps/*` paths are now one-line re-export shims,
so
**apps/semaphore needs zero changes in this PR**.

### 2b. `AppContext` (hand-threaded) → `RequestContext` (TanStack
Start's own)

The old `AppContext` carried **every worker binding** as an optional
field,
threaded around next to — not through — TanStack Start's request
context. That
forced a defensive guard at every use site for bindings that are
*always* bound:

```ts
// before — in ~6 routers/capabilities
if (!context.stream) {
  throw new ORPCError("INTERNAL_SERVER_ERROR", { message: "STREAM … not configured." });
}
const ns = context.stream as unknown as StreamDurableObjectNamespace;
```

**Why it was bad:** a dozen impossible-error branches, an `AppContext`
type
listing 14 optional bindings, and two parallel notions of "context". The
golden-path answer is to read bindings where you use them:

```ts
// after — env is the documented module-level binding accessor
import { env } from "cloudflare:workers";
const ns = env.STREAM as unknown as StreamDurableObjectNamespace;
```

`RequestContext` (`src/request-context.ts`) is now *just* request-scoped
state
(config, db, log, auth principal/session, `waitUntil`, `ctx.exports`,
project
scope) **and is the actual TanStack Start request context** — the
`Register`
augmentation lives next to the type. `ctx.exports` is the one binding
kept on
the context, because Cloudflare only exposes it on `ExecutionContext`,
not as a
module import.

Net effect on the routers: agents `+10/−44`, projects `+22/−52`,
codemode `+3/−21`, streams `+5/−14` — they all got smaller.

### 2c. `entry.workerd.ts` (604 lines) → `worker.ts` (197 lines) +
focused modules

The entrypoint was a 600-line grab-bag. It's now a short, linear
dispatcher a
Cloudflare engineer would recognize at a glance — infra routes → evlog →
project ingress → stream RPC / capnweb → TanStack Start handler:

```ts
export default {
  async fetch(request, env, ctx) {
    const config = parseConfig(env); // per-request, not module scope — see §3 note
    const early = (await handleCaptunTunnelFetch(request, env, config))
      ?? (await handleDebugRoutes({ request, env, config }));
    if (early) return early;
    return withEvlog({ request, app: { name: "@iterate-com/os", slug: "os" }, config, executionCtx: ctx },
      async ({ log }) => {
        // … ingress → stream RPC → capnweb → handler.fetch(request, { context }) …
      });
  },
};
```

The debug endpoints, project-stream RPC, and ingress lookup moved into
`src/debug-routes.ts`, `src/domains/streams/project-stream-rpc.ts`, and
`src/ingress/lookup.ts`. `IterateApp` gained a `main` option (default
unchanged,
so semaphore is untouched). The `__internal` oRPC namespace and the
OpenAPI
reference plugin are now declared inline in `orpc/root.ts` /
`orpc/handler.ts`
instead of via a shared factory.

---

## 3. 🔒 Security: closed an unauthenticated secret leak (rotate secrets)

Review turned up that **`GET /api/__internal/debug` was unauthenticated
and
returned `process.env`** — which, under `nodejs_compat` (always on for
our
workers), contains the raw `APP_CONFIG` secret blob. **Confirmed live on
`os.iterate.com` and `semaphore.iterate.com`.**

```ts
// before — packages/shared/src/apps/internal-router.ts
export function createInternalDebugOutput() {
  if (typeof process === "undefined") return { runtime: "workerd" };
  return { runtime: "node", pid: process.pid, …,
    env: Object.fromEntries(Object.entries(process.env)…), // ← the whole secret blob, to anyone
    memoryUsage: process.memoryUsage() };
}
// after
export function createInternalDebugOutput() {
  // SECURITY: this route is UNAUTHENTICATED. Never return secrets here.
  return { runtime: "workerd" as const };
}
```

Gutted at the shared source so semaphore (still on the shared router) is
fixed
too; OS's inline `__internal` router does the same.

**Access check (Cloudflare Workers Observability, queried 2026-06-10):**
a path
filter `url.path eq /api/__internal/debug` returned **0 events**; the
only
`__internal` traffic was `trpc-cli-procedures` from our own `node` CLI.
That's
reassuring but **not proof** — effective visibility for rare requests
was only
~the last 24–35h (far shorter than the exposure window) and the dataset
is
ABR-sampled. **Action required: rotate the Cloudflare API token,
OpenAI/xAI/
Gemini keys, admin API secret, and Slack/Google OAuth secrets on both
apps, then
redeploy.**

> Note (also from review): config is now parsed **per request** in
`worker.ts`,
> not at module scope. Module-scope derivatives of secrets can go stale
across
> binding-only deploys (Cloudflare reuses isolates); a zod parse per
request is
> trivially cheap and always honors the current secret.

---

## 4. Review & verification

- **Two adversarial reviews** against re-read first-party docs
(Cloudflare,
  TanStack Start/Router/Query, oRPC). They caught, among other things, a
  **build blocker** — `@tanstack/devtools-vite@0.7.0`'s `removeDevtools`
transform rewrites a parenthesized JSX `return` into `return ( );` (a
syntax
error), which only `vite build` (deploy/preview) surfaces, not PR CI.
Pinned
  back to `0.6.0`.
- Monorepo `typecheck` / `lint` (0 warnings) / `format` / tests / a real
`vite build` all pass; **Cursor Bugbot clean**; Preview deploy + e2e
green.
- **Headless preview smoke test**
(`apps/os/docs/preview-agent-browser-smoke.md`):
superadmin sign-in → create a project via `/new-project` → **a real
agent
conversation** (typed a question in the browser, the agent DO + LLM
replied).

Two honest caveats are documented, not hidden: a transient `Project not
found`
I chased was an **expired short-lived OS session JWT**, not a bug; and
live
stream display needs a WebSocket that 500s on preview hosts (the
conversation
completes server-side; the WS-upgrade code is byte-identical to main →
flagged
as preview-infra follow-up, not claimed fixed).

---

## 5. "Wait, a simplification PR that's net +~930 lines?"

Correct, and worth unpacking — because **~74% of the net isn't
application code
at all**, and the code that *is* application logic mostly shrank.

`git diff origin/main…HEAD`: **91 files, +3709 / −2777 = net +932.** By
area:

| Area | net | what it is |
|---|---:|---|
| `pnpm-lock.yaml` | **+423** | dependency-graph churn from the TanStack
version bump. Zero hand-written lines. |
| `docs/` | **+267** | the decisions log + headless smoke-test guide you
asked for. Prose, not code. |
| `apps/os/src` | +123 | see below — dominated by *new* files that
replace deleted ones |
| `scripts/` | +61 | the new `generate-route-tree.ts` freshness check
(pure addition, didn't exist) |
| `packages/shared` | +57 | back-compat shims so semaphore is untouched
+ the moved modules |
| generated `routeTree.gen.ts` | 0 | |
| tests / other | +1 | |

`pnpm-lock.yaml` + `docs` alone are **+690 of the +932**. Strip those
and the
real source delta is **~+240**, almost all of it *deliberate new
infrastructure*, not retained complexity:

- `generate-route-tree.ts` (+61) — new CI guard against the class of bug
that
  started this PR.
- `request-context.ts` (+96) + `router-context.ts` (+18) — replace the
deleted
`context.ts` (−51) with a smaller, *correct* request context plus the
typed
accessors that work around an upstream `getGlobalStartContext` type bug.
- back-compat shims in `packages/shared/src/apps/*` — a few lines each
so
**semaphore changes by zero lines** instead of forcing a parallel
refactor
  into this PR.

The actual product logic got **smaller and flatter**:

- `entry.workerd.ts` (−604) → `worker.ts` (+197) plus three focused
extracted
  modules. The headline file shrank ~3×.
- the oRPC routers lost net ~120 lines of impossible-error binding
guards.
- `context.ts` (−51) and the 14-field `AppContext` are gone.

The line counters also **double-count pure moves**: `config.ts` shows as
`+501` (new path) and `−501` (old path → 4-line shim) — net ~0 churn
that
inflates *both* columns. Same for the evlog modules. Git doesn't detect
these
as renames because the old path still exists as a shim.

So: net +~930, but it's lockfile (+423) + requested docs (+267) + new
guard
rails and a security fix (+~240), against genuinely *less* and simpler
runtime
code. If you want the "pure simplification" number, it's the
`apps/os/src`
runtime logic, which is net-negative once you exclude the new
context/worker
scaffolding that replaced larger deleted files.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---

## 6. Update: merged `main` (itx) + semaphore migration

Since this branch opened, `main` landed **#1407 (itx)** which rewrote
the worker
entrypoint and replaced `src/capnweb/` with `src/itx/`. Rather than
rebase
through it, I **merged `main`** and reconciled:

- `worker.ts` now wires the itx handlers (`handleItxFetch`,
`handleProjectHostItxFetch`, `getItxCapHostIngressRule`, itx entrypoint
  exports) instead of the removed capnweb ones — keeping the clean split
  (`debug-routes.ts`, `project-stream-rpc.ts`, `ingress/lookup.ts`).
- The whole `src/itx/` subsystem was migrated onto the new `config.ts` /
`request-context.ts` (it was built on the now-deleted
`app.ts`/`context.ts`).
- Honored main's new **`no-raw-durable-object-binding-access`**
guardrail:
ingress code (projects router, integration-api) mints Project/Slack DO
stubs
  through helpers in the trusted `*-durable-object.ts` domain files
(`getProjectDurableObjectStub`, `getSlackIntegrationStub`) rather than
raw
`env.X.getByName`. Updated the rule's allowlist for the `worker.ts`
rename.

**Also migrated `apps/semaphore`** off the same
`@iterate-com/shared/apps/*`
framework (its own `app.ts`→`config.ts`,
`context.ts`→`request-context.ts`,
`entry.workerd.ts`→`worker.ts`, inline `__internal`), which let me
**delete the
shared modules entirely** (the `apps/config`, `apps/logging/*`,
`apps/internal-router`, `apps/orpc` shims) instead of leaving them as
back-compat shims. Monorepo typecheck, lint (0 warnings), and tests are
green.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **High Risk**
> Removes the shared apps layer and rewrites the worker entry, request
context, and oRPC wiring across most of OS; also fixes a live
unauthenticated debug endpoint that exposed secrets—rotate affected
credentials after deploy.
> 
> **Overview**
> Refactors **`apps/os`** off the shared “apps framework” onto a
single-app golden path: **`app.ts` → `config.ts`**, **`AppContext` →
`RequestContext`** (TanStack Start’s registered request context), and
**`entry.workerd.ts` → `worker.ts`** with logic split into
**`debug-routes.ts`**, **`ingress/lookup.ts`**, and
**`project-stream-rpc.ts`**. oRPC routers and integrations now read
**Cloudflare bindings via `import { env } from "cloudflare:workers"`**
instead of optional fields on context, with trusted **DO stub helpers**
for lint compliance.
> 
> Fixes the phantom **`/projects/new`** route by moving project creation
to **`/new-project`**, bumps the TanStack pair, and adds
**`routes:check`** / **`generate-route-tree.ts`** so CI fails on a stale
**`routeTree.gen.ts`**.
> 
> **Security:** stops **`GET /api/__internal/debug`** from exposing
secrets — OS inlines a safe **`__internal`** router (static `{ runtime:
"workerd" }` on debug) and hardens the shared debug helper so other apps
don’t leak **`process.env`**.
> 
> Docs add the simplification decision log and expanded headless preview
smoke procedures; alchemy deploy points **`IterateApp`** at
**`./src/worker.ts`** and passes **`"os"`** to **`initAlchemy`** instead
of a manifest.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
ecbd6d7. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

<!-- CLOUDFLARE_PREVIEW -->
## Environment Config Lease
<!-- CLOUDFLARE_PREVIEW_STATE -->
<!--
{
  "apps": {
    "os": {
      "appDisplayName": "OS",
      "appSlug": "os",
      "status": "deployed",
      "updatedAt": "2026-06-10T07:35:52.639Z",
      "headSha": "ecbd6d7c21ea49d04a88d0962f6e7c38330b08ab",
      "message": null,
      "publicUrl": "https://os.iterate-preview-2.com",
"runUrl": "https://github.com/iterate/iterate/actions/runs/27260626648",
      "shortSha": "ecbd6d7"
    },
    "semaphore": {
      "appDisplayName": "Semaphore",
      "appSlug": "semaphore",
      "status": "deployed",
      "updatedAt": "2026-06-10T07:35:43.130Z",
      "headSha": "ecbd6d7c21ea49d04a88d0962f6e7c38330b08ab",
      "message": null,
      "publicUrl": "https://semaphore.iterate-preview-2.com",
"runUrl": "https://github.com/iterate/iterate/actions/runs/27260626648",
      "shortSha": "ecbd6d7"
    }
  },
  "environmentConfigLease": {
    "dopplerConfig": "preview_2",
    "leasedUntil": 1781080419272,
    "leaseId": "4779be3e-b0a6-4e72-851c-1efa4aae3514",
    "slug": "preview-2",
    "type": "environment-config-lease"
  }
}
-->
<!-- /CLOUDFLARE_PREVIEW_STATE -->
Lease: `preview-2`
Doppler config: `preview_2`
Type: `environment-config-lease`
Leased until: 2026-06-10T08:33:39.272Z

### OS
Status: deployed
Commit: `ecbd6d7`
Preview: https://os.iterate-preview-2.com
[Workflow
run](https://github.com/iterate/iterate/actions/runs/27260626648)
Updated: 2026-06-10T07:35:52.639Z

### Semaphore
Status: deployed
Commit: `ecbd6d7`
Preview: https://semaphore.iterate-preview-2.com
[Workflow
run](https://github.com/iterate/iterate/actions/runs/27260626648)
Updated: 2026-06-10T07:35:43.130Z
<!-- /CLOUDFLARE_PREVIEW -->

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Everything documentation related package: router-generator

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[_] escape for leading underscore in path is dropped when route has a pathless layout ancestor (virtual file routes)

1 participant