Skip to content

feat(test): type renderRoute ids, inputs, and results from the generated route registration - #456

Merged
ScriptedAlchemy merged 3 commits into
mainfrom
feat/typed-route-register
Sep 3, 2026
Merged

feat(test): type renderRoute ids, inputs, and results from the generated route registration#456
ScriptedAlchemy merged 3 commits into
mainfrom
feat/typed-route-register

Conversation

@ScriptedAlchemy

@ScriptedAlchemy ScriptedAlchemy commented Sep 3, 2026

Copy link
Copy Markdown
Owner

Summary

Lands the implementation candidate from the TanStack Router cross-reference lane: typed route ids, inputs, and results for the agent-bundle/test harness, through the generated .agent-bundle/routes.d.ts and a Register seam on @agent-bundle/runtime — TanStack Router's registration pattern (typed routes without per-route codegen: one declare module augmentation, inference everywhere else).

  • packages/agent-bundle/src/routes/typegen.ts: the generated declarations now emit AgentBundleRouteContracts ({ input, result } per route id) and register it as Register.routes in the same declare module '@agent-bundle/runtime' block that already declares provider keys (one block; omitted when the graph has neither routes nor providers).
  • packages/rsc-runtime/src/agent-request.ts: new empty Register interface plus RegisteredRoutes, RegisteredRouteContract, RegisteredRouteId, RegisteredRouteInput, RegisteredRouteResult, all degrading to string / unknown when nothing is registered. Exported from @agent-bundle/runtime.
  • Event routes register the harness contract, not the component signature: input is the { canonical, native } payload (the harness injects signal), result is undefined (event modules export no resultSchema). RouteInput/RouteResult keep their component-props meaning.
  • packages/agent-bundle/src/test/render.ts: renderRoute / renderRouteEvents infer Target from a string literal and check it with the ConstrainLiteral shape (Target & Constraint) | Constraint, so a typo is rejected naming the registered ids; input and result are typed from the registered contract. A value typed string, a module target, or an unregistered program keep the previous types. New exported types RouteTargetConstraint, RouteTargetInput, RouteTargetResult.
  • Docs: website/docs/{en,zh}/guide/development/testing.mdx, docs/framework-mode.md, packages/agent-bundle/README.md.
  • Changeset: .changeset/typed-route-register.md (@agent-bundle/runtime patch, agent-bundle patch — additive).

Not included: the lane's architecture matrix document (react-router-rsbuild / Next.js / TanStack vs agent-bundle) was never written before the worker halted; nothing of it existed to land.

Evidence

  • pnpm typecheck exit 0; pnpm lint exit 0 (0 errors, 0 warnings).
  • route-graph.test.ts (unit): 50 passed — typegen output pinned, including the single augmentation block and the Register['routes']RouteId equality in a real tsc program.
  • pnpm build exit 0 (publint passed); then route-register-typegen.test.ts + public-api.test.ts + provider-typegen.test.ts under the integration config with AGENT_BUNDLE_*_PREBUILT=1: 9 passed, 0 failed. The fixture now also compiles an event:tool/after route (AgentEventRouteProps, claude target): { canonical, native } accepted, result typed undefined, mistyped canonical rejected naming AgentEventCanonicalIdentity.
  • Head 3c8b7da99: pnpm test:unit 3026 passed; pnpm test:route-unit 44 passed; pnpm test:projection 145 passed; pnpm typecheck exit 0; pnpm lint 0 errors/0 warnings; pnpm docs:site:build exit 0. The new test type-checks a fixture against the built agent-bundle/test and @agent-bundle/runtime: registered program clean; wrong id rejected with '"tool:curator/find" | "tool:curator/status"'; wrong input and wrong result rejected; unregistered program (no generated file) accepts any id and unknown.
  • pnpm docs:site:build exit 0 (TypeDoc over the new exports, dead-link and language-parity checks pass).

Test plan

  • Unit + targeted integration above
  • Docs site build
  • CI on 3c8b7da99 (the earlier Release gates failure on 18a384a71 was the npm audit hang in audit-packed-release.mjs, also failing on main at 284141958; not this change)

Review status

Codex reviewed ab4051e31 and raised two P1 threads (event-route contract; changeset format); both fixed in 3c8b7da99 and answered on-thread. No re-review requested (maintainer process change 2026-09-03). Self-review: the generated augmentation references @agent-bundle/runtime for any graph with routes (previously only provider graphs did); a project that adds .agent-bundle/routes.d.ts to its tsconfig include without depending on @agent-bundle/runtime would see "invalid module name in augmentation" — the scaffold pins both packages and no example includes the file, so nothing in-tree is affected; documented as opt-in in the testing guide.

…ted route registration

The compiler's .agent-bundle/routes.d.ts now registers an
AgentBundleRouteContracts map ({ input, result } per route id) on
@agent-bundle/runtime's new Register interface, in the same
declare-module block that already declares provider keys. agent-bundle/test's
renderRoute and renderRouteEvents infer the route id, input, and result from
that registration (TanStack Router's Register / ConstrainLiteral pattern);
without the generated file in the program they keep the previous types
(any string id, unknown input and result).

Adds RegisteredRoutes / RegisteredRouteContract / RegisteredRouteId /
RegisteredRouteInput / RegisteredRouteResult to @agent-bundle/runtime and
RouteTargetConstraint / RouteTargetInput / RouteTargetResult to
agent-bundle/test; a route-register-typegen integration test proves the
registered, mistyped, and unregistered programs; en/zh testing docs,
framework-mode.md, and the package README describe the seam.

Started by the TanStack cross-reference lane; landed from its parked work.
@changeset-bot

changeset-bot Bot commented Sep 3, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 3c8b7da

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@agent-bundle/runtime Patch
agent-bundle Patch

Not sure what this means? Click here to learn what changesets are.

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

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 3, 2026

Copy link
Copy Markdown

Codex Review Summary

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

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-03T22:12:15.014058Z ab4051e PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

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

@pkg-pr-new

pkg-pr-new Bot commented Sep 3, 2026

Copy link
Copy Markdown
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle@456
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle/create-agent-bundle@456
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle/@agent-bundle/runtime@456

commit: 3c8b7da

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ab4051e31f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

'export type RouteResult<Id extends RouteId> = ContractResult<AgentBundleRoutes[Id]>;',
'/** The registered contract map: one `{ input, result }` per route id, for `@agent-bundle/runtime`\'s `Register`. */',
'export type AgentBundleRouteContracts = {',
' readonly [Id in RouteId]: Readonly<{ input: RouteInput<Id>; result: RouteResult<Id> }>;',

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Register the event harness payload instead of component props

For a typed event component using AgentEventRouteProps, this maps the route input to { canonical, native, signal } and the result to the component's React return value. However, renderRoute expects options.input to contain only { canonical, native }, injects its own signal in componentProps, and exposes no result because event modules have no resultSchema. Once the generated declaration is included, valid event-route calls therefore fail for a missing signal, while the result type describes a value the harness never returns; event registrations need a harness-specific payload/result contract rather than the component signature.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 3c8b7da. The registered map (AgentBundleRouteContracts) is now built from two harness-specific helpers rather than the component-props RouteInput/RouteResult: HarnessInput yields the schema output for schema routes and Omit<Props, 'signal'> — i.e. { canonical, native } — for event routes, since the harness injects signal itself; HarnessResult yields the resultSchema output for schema routes and undefined for event routes, which export none. RouteInput/RouteResult keep their existing meaning (what the component receives). Tests: route-graph.test.ts pins the generated helpers and asserts Register['routes']['event:workspace/open'] equals { input: Omit<Props, 'signal'>; result: undefined } in a real tsc program; route-register-typegen.test.ts now compiles an event:tool/after route typed with AgentEventRouteProps against the built packages — a { canonical, native } call is accepted, result is undefined, and a mistyped canonical is rejected with AgentEventCanonicalIdentity named. Docs (en/zh testing guide, framework-mode, README) and the Register doc comment state the event-route contract.

Comment thread .changeset/typed-route-register.md Outdated
"agent-bundle": patch
---

Type `renderRoute` and `renderRouteEvents` (from `agent-bundle/test`) against the project's own routes: the generated `.agent-bundle/routes.d.ts` now registers an `AgentBundleRouteContracts` map (`{ input, result }` per route id) on `@agent-bundle/runtime`'s new `Register` interface, so once that file is in the project's TypeScript program a string-literal route id is checked against the compiled ids, `input` is typed from the route's `inputSchema`, and `result` from its `resultSchema`. `@agent-bundle/runtime` exports `Register`, `RegisteredRoutes`, `RegisteredRouteContract`, `RegisteredRouteId`, `RegisteredRouteInput`, and `RegisteredRouteResult`; `agent-bundle/test` exports `RouteTargetConstraint`, `RouteTargetInput`, and `RouteTargetResult`. Nothing is required: a value typed `string`, a module target, or a program without the generated file sees the previous types (any id, `unknown` input and result).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Rewrite the changeset summary in the required format

This changeset is a long implementation description and ends without the required (#PR) suffix. Rewrite it as a concise, user-facing imperative summary naming the affected exports and ending with the PR number, as required by the repository changeset convention.

AGENTS.md reference: AGENTS.md:L104-L105

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rewritten in 3c8b7da: imperative, names the affected entry points and every new export (Register, RegisteredRoutes, RegisteredRouteContract, RegisteredRouteId, RegisteredRouteInput, RegisteredRouteResult; RouteTargetConstraint, RouteTargetInput, RouteTargetResult), states the degraded behaviour without the generated file, and ends with (#456).

…nical, native } input, no result; tighten the changeset
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant