Skip to content

feat(routes): #93 route-compiler substrate — discovery, route-graph IR, mode diagnostics, inspect focus (PR-1) - #116

Closed
ScriptedAlchemy wants to merge 1 commit into
mainfrom
wave1/93-route-substrate
Closed

feat(routes): #93 route-compiler substrate — discovery, route-graph IR, mode diagnostics, inspect focus (PR-1)#116
ScriptedAlchemy wants to merge 1 commit into
mainfrom
wave1/93-route-substrate

Conversation

@ScriptedAlchemy

Copy link
Copy Markdown
Owner

Summary

First of two substrate PRs for the route compiler (Wave 1, Lane B of #107). Part of #93 — substrate only, the issue stays open.

  • Route discovery under the conventional roots (src/mcp/<server>/{tools,resources,prompts,apps}/*, src/events/**, src/providers/*, src/cli/*, src/scripts/*) rides the existing deterministic machinery: sorted globbing, project ignore rules, safe path segments, provenance on every route. No parallel scanner.
  • Immutable route-graph IR (packages/agent-bundle/src/routes/): deep-frozen AgentRouteGraph of CompiledAgentRoute { kind, id, source, serverId?, config, provenance }. Paths supply kind/server/identity; config stays empty until PR-2's static extractor; the route-kind union is switched exhaustively with a never default.
  • New AB480x diagnostic family (documented in docs/diagnostics.md): AB4800 route directory vs src/mcp/<server>.ts entry, AB4801 route-mode server also declared in mcp.servers, AB4802 src/cli/ routes vs src/cli.ts, AB4803 duplicate route ids, AB4804 unsafe route names. All hard errors — a server (and the package CLI) is in exactly one mode; the compiler never silently chooses (issue rule 10).
  • inspect --routes focus through the existing focus/DTO pattern (additive to InspectOptions/ReadyInspectResult.selected).
  • Config always wins: modules referenced by explicit scripts/hooks/bin/lib/mcp configuration are claimed by that declaration and never become routes. This keeps examples/hooks-and-scripts and examples/mcp-app (both have src/scripts/* files, all config-claimed) building unchanged — verified; no other example matches a conventional root.
  • Consumer-invisible this wave (G8): no public authoring docs, no rsc-runtime changes, no generated server entries, no route-module execution, defineOperation and all existing conventions untouched, docs/framework-mode.md untouched. Driven purely by internal fixtures (tests/routes.test.ts, 9 tests).
  • Docs rider (Lane D): supersession note on the 2026-08-26 parity spec's filesystem-router non-goal pointing at Compile filesystem routes into the Agent Bundle meta-framework #93; status note on the 2026-08-25 capability-aware Workbench spec pointing at Add an interactive Agent Workbench with local host discovery and lifecycle replay #105.
  • Changeset: patch for agent-bundle.

Test plan

  • tsc --noEmit, rslint, full test:unit (1790 passed)
  • Targeted integration: cli.test.ts, examples-contract.test.ts green against the built package
  • Full pnpm check:local-ci green before merge

…able route-graph IR, mode diagnostics, inspect focus

Discover route modules under the conventional roots (src/mcp/<server>/
{tools,resources,prompts,apps}/, src/events/, src/providers/, src/cli/,
src/scripts/) through the existing sorted, ignore-aware, safe-path
discovery machinery, and compile them into a deep-frozen AgentRouteGraph
whose paths supply kind, owning server, and identity. New AB480x error
family: route-directory vs entry-file mode conflicts (AB4800 server file,
AB4801 declared server, AB4802 src/cli.ts), duplicate route ids (AB4803),
and unsafe route names (AB4804) — a server is in exactly one mode and the
compiler never silently chooses. Modules explicit configuration claims
(scripts, hooks, bin, lib, mcp entries) are never routes, keeping shipped
example layouts valid. inspect gains a --routes focus listing the graph.
The graph is consumer-invisible this wave: config stays empty until the
static extractor lands, and nothing generates entries from it.

Docs rider: supersession note on the 2026-08-26 filesystem-router
non-goal pointing at #93; status note on the 2026-08-25 capability-aware
Workbench spec pointing at #105.

Part of #93
@changeset-bot

changeset-bot Bot commented Sep 1, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: dc8af7e

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

This PR includes changesets to release 1 package
Name Type
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 1, 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-01T01:11:53.659328Z dc8af7e 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.

@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: dc8af7e243

ℹ️ 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".

}
}
if (routes.some((route) => route.kind === 'cli')) {
const cliEntry = conventionalCliEntrySource(projectRoot);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Respect explicit bin configuration before reporting AB4802

When a project has routed commands, retains src/cli.ts, and explicitly points bin elsewhere, this unconditionally treats the shadowed file as the active conventional package entry and raises hard error AB4802. Existing packageConventionShadowNudges() explicitly defines that file as shadowed in this configuration and only emits AB4731, so otherwise valid validation/build commands are now blocked until an unused file is removed. Check whether the convention is active under config.bin before reporting this mode conflict.

Useful? React with 👍 / 👎.

Comment on lines +151 to +155
const matches = await fastGlob([...routeModulePatterns], {
absolute: true,
cwd: projectRoot,
followSymbolicLinks: false,
onlyFiles: true,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Include dotfiles in unsafe route-name validation

Because fast-glob defaults dot to false, inputs such as src/scripts/.release.ts or src/events/.internal/saved.ts never reach the isSafePathSegment check and are silently omitted instead of producing the documented hard AB4804 diagnostic. The only documented private convention here is _-prefixed segments, so hidden modules under a recognized route root should be scanned and then rejected as unsafe.

Useful? React with 👍 / 👎.

@ScriptedAlchemy

Copy link
Copy Markdown
Owner Author

Duplicate of Wave 1 Lane B PR-1. Keeper is #119 (graph.ts + inspect.ts + route-graph tests). This branch used routes/discover.ts instead of the agreed compiler.

ScriptedAlchemy added a commit that referenced this pull request Sep 1, 2026
…ared route graph to inspect

Reconciliation of the two Lane B PR-1 implementations (#116 closed as
duplicate, #119 the keeper):

- Config always wins: modules referenced by explicit scripts/hooks/bin/
  lib/mcp configuration are claimed by that declaration and never become
  routes. This keeps examples/hooks-and-scripts and examples/mcp-app —
  both with src/scripts/* files, all config-claimed — route-free and
  building unchanged (proved by pnpm examples:check).
- Codex P2 fix: the inspect routes focus no longer reloads the config and
  recompiles the graph (a non-idempotent config factory could diverge from
  the validated model, and second-pass diagnostics were mishandled).
  PreparedProject now carries the graph compiled during discovery, and the
  focus serves it directly; route-free projects serve the shared frozen
  emptyCompiledRouteGraph.
- Test coverage ported from #116: the claim rule (examples-shaped
  fixture), the declared entry/command/url server-conflict branch of
  AB4800, and the route-free inspect focus.

Part of #93
ScriptedAlchemy added a commit that referenced this pull request Sep 1, 2026
* feat: compile route-graph IR and inspect focus (#93)

Compile the conventional route tree (src/mcp/<server>/{tools,resources,
prompts,apps}, src/events, src/providers, src/cli, src/scripts) into an
immutable, digest-stable route-graph IR with hard collision errors
(AB4800-AB4804) and explicit routes.servers/<id> + routes.cli mode
overrides. discoverProject attaches the graph only when non-empty,
validateSource surfaces its diagnostics, and inspect gains a routes
focus (--routes) that dumps the graph bundler-style.

Towards #93.

Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>

* docs: mark filesystem-router non-goal superseded by #93 and add Workbench spec status note (#105, #107)

Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>

* feat(routes): port the config-claim rule from #116 and serve the prepared route graph to inspect

Reconciliation of the two Lane B PR-1 implementations (#116 closed as
duplicate, #119 the keeper):

- Config always wins: modules referenced by explicit scripts/hooks/bin/
  lib/mcp configuration are claimed by that declaration and never become
  routes. This keeps examples/hooks-and-scripts and examples/mcp-app —
  both with src/scripts/* files, all config-claimed — route-free and
  building unchanged (proved by pnpm examples:check).
- Codex P2 fix: the inspect routes focus no longer reloads the config and
  recompiles the graph (a non-idempotent config factory could diverge from
  the validated model, and second-pass diagnostics were mishandled).
  PreparedProject now carries the graph compiled during discovery, and the
  focus serves it directly; route-free projects serve the shared frozen
  emptyCompiledRouteGraph.
- Test coverage ported from #116: the claim rule (examples-shaped
  fixture), the declared entry/command/url server-conflict branch of
  AB4800, and the route-free inspect focus.

Part of #93

* fix(inspect): reuse prepared route graph for --routes focus

Carry discovery's graph on the invalid-source prepare path and pin
inspect --routes to a single config-factory evaluation.

* refactor(routes): use the shared cycle-safe deepFreeze and fold the duplicate api re-export

Deslop audit items 1 and 4: graph.ts drops its local deepFreeze copy for
the shared core/freeze.ts implementation (cycle-free import; behavior
equivalent for the fresh plain-object IR), and api.ts folds the two
consecutive routes/graph re-export statements into one.

Part of #93

* fix: contain hostile config throws in route discovery and resolve ignore types for consumers

Route-graph compilation reads config sections (scripts, hooks, bin, lib, mcp)
whose getters are untrusted until validation runs; a throwing getter must
degrade to an absent graph so validation still reports AB7001 instead of the
load-failure path reporting AB7000. The public-api consumer fixture also gains
an ignore symlink because routes/graph.d.ts now pulls config/ignore.d.ts into
the root declaration graph (ignore is a runtime dependency, matching the ajv
precedent).

* fix(routes): fail closed on hostile routes getters

Keep discovery tolerant of hostile config-claim getters while forcing hostile routes access through source validation, so inspect reports AB7001 instead of silently returning ready.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Zack Jackson <ScriptedAlchemy@users.noreply.github.com>
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