Skip to content

fix(events): put every generated hook execution on the Flight render path - #292

Merged
ScriptedAlchemy merged 2 commits into
mainfrom
wave/flight-hooks
Sep 2, 2026
Merged

fix(events): put every generated hook execution on the Flight render path#292
ScriptedAlchemy merged 2 commits into
mainfrom
wave/flight-hooks

Conversation

@ScriptedAlchemy

Copy link
Copy Markdown
Owner

Summary

Audit + close-out for the USER directive "ensure flight loader used for all the hooks etc like we discussed and planned and mentioned in the git issues", against the #107 Revision 3 architecture (MCP tools and host hooks are internal Flight render requests against the warm runtime; Flight stays internal; standalone is an explicit degraded mode, never a different renderer).

Two hook execution paths violated the plan; both are fixed here:

  1. Standalone event routes now render through the real Flight renderer. runtime: 'standalone' / fallback: 'standalone' wrappers previously evaluated route components with a manual element walker (resolveServerNode) — the exact sync-element-walker pattern Build a real RSC Agent Document renderer with streaming semantics #96 retired; it could not execute Suspense/async React semantics. The bundle now emits one shared hooks/hooks-flight.mjs react-server worker per target, and the wrapper dispatches the same kind: 'event' render request the warm runtime uses (createAgentRenderDispatcher → Flight render → AgentDocumentprojectEventDocument), with the same request-identity axes (host/session/workspace) the warm path derives. The manual walker is deleted. Native hook envelopes are unchanged.

  2. Composite plugin artifacts can now execute shared event routes at all. The wrapper's host detection had no Cursor branch, and startEventRuntime ignored the IPC request's target, projecting with the literal 'plugin' — which projectEventDocument rejects by design. The Cursor wrapper variant now bakes target = "cursor" at codegen; the shared Claude/Codex wrapper keeps evidence-based detection (PLUGIN_ROOT is pinned Codex capability data); the event runtime validates request.target against the artifact's allowed hosts (fail-closed transport error otherwise) and uses the concrete host for props, request host identity, and projection. Endpoint identity derivation is unchanged.

Audit result (path → Flight or bypass)

Execution path Surface Ground truth before this PR Verdict
Generated MCP tools/resources/prompts all 5 adapters Flight via warm worker (renderGeneratedRoute → dispatcher → createFlightWorkerHost) conformant (#147/#175)
Shared event routes, 6 families claude, codex, cursor Flight via thin client → requestEventRuntime IPC → warm runtime conformant (#180/#182)
Shared event routes composite plugin Flight attempted, failed at projection ('plugin' target; no Cursor detection) fixed here
Standalone/fallback event routes claude/codex/cursor/plugin Bypass — manual element walker, no Flight fixed here
workspace/open all hosts honestly unavailable everywhere (#221) conformant
Event routes portable no hooks capability conformant
Legacy config-declared hooks / prebuilt handlers / authored nativeHooks claude/codex/cursor/plugin direct wrappers / opaque commands sanctioned escape hatches (#107 §1-#97: config hooks "remain an escape hatch")
Rendered CLI (.tsx), routes.mcpCommands, rendered scripts package executables Flight via local per-invocation react-server worker conformant (#107 §1-#102 s3: render requests "typically in-process")
Plain CLI/scripts (.ts) package executables plain Node by convention conformant (".tsx renders; .ts is plain Node" is plan text)
runRscCli / createRscMcpServer sync lowerers hand-written operations model direct execute + sync lowering documented compatibility surface (#96 landing comment)
Workbench hooks page / MCP page / lifecycle replay dev server executes real emitted wrappers / real MCP sessions / real codecs + real Flight render conformant
No path fabricates success when a runtime is unavailable verified conformant

Positive pins added (regressions now fail red)

  • Composite plugin end-to-end: one warm runtime serves Claude, Codex, and Cursor envelopes across tool/after and session/start — same processLifetime.instanceId observed across four consecutive events, per-host exact native output envelopes, and a disallowed target rejected as a transport failure.
  • Standalone routes: a Suspense + async Server Component route renders correctly (impossible on the retired walker); wrapper-shape pins assert no route-module import, the hooks-flight.mjs reference, and dispatcher usage.
  • Projection semantics moved to the route-unit real-renderer harness (tests/route-unit/event-project.test.ts) instead of the walker.

Parity notes

  • Native encode/decode contracts unchanged: lifecycle-replay suite (feat(dev): semantic lifecycle replay (#105 stage 3) #279 machinery) and the packed stdio/projection suites pass; examples/rsc-agent-runtime's standalone tool/after route passes unchanged.
  • Standalone cold start rises from ~38 ms to ~389 ms median (local worker spawn + react-server graph) — well inside the 5 s default host deadline; no budget test loosened. Shared-runtime hook cold start is unaffected (~38 ms).

Test plan

  • pnpm typecheck
  • pnpm lint
  • pnpm test (unit + route-unit + projection + integration)
  • pnpm test:packed (packed journey pool)

Pre-existing failure on main (not from this PR)

packages/workbench/tests/examples-real.e2e.test.ts:635 fails on clean origin/main (verified in a pristine worktree at 9c767c98b) exactly as it does on this branch: the audiobook Workbench "CLI commands" table renders 30 rows where the pin expects 15 (an exact doubling). This lane's diff does not touch CLI command projection or the Workbench; the drift comes from one of today's installer/bin landings. Reported for a fixer lane; not addressed here.

…rker

Standalone and fallback event-route wrappers no longer evaluate route
components with a manual element walker: the bundle emits one shared
hooks-flight.mjs react-server worker per target, and the wrapper
dispatches the same kind:'event' render request the warm runtime uses,
then projects the resulting Agent Document into the unchanged native
hook envelope.
…event routes

The composite plugin artifact's event runtime validated and projected
with the literal 'plugin' target, which projectEventDocument rejects,
and the shared wrapper's host detection had no Cursor branch. The
Cursor wrapper variant now bakes its concrete target, the shared
runtime validates request.target against the artifact's allowed hosts,
and props, host identity, and projection all use the concrete host.
@changeset-bot

changeset-bot Bot commented Sep 2, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 0ca8e11

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

@ScriptedAlchemy
ScriptedAlchemy merged commit c509384 into main Sep 2, 2026
4 checks passed
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 2, 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-02T06:36:01.364777Z 0ca8e11 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.

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