feat: prebuilt payload adapter mode (RFC #50 Phase 3) - #71
Conversation
The `payload` config block declares already-built directory trees that
`agent-bundle build` packages byte-for-byte at stable paths, and
`{ prebuilt: ... }` markers point MCP server entries and hook handlers at
files inside those payloads. Prebuilt entries skip compilation but flow
through the same adapter lowering as compiled entries: path-token expansion
in every target MCP document, the injected env anchor, generated
hooks/hooks.json commands (with shell-safe prebuilt hook args), MCP
artifact-reference validation, and manifest provenance (new `prebuilt` file
kind; payload files hash into project.sourceInputs). New diagnostics:
AB4740-AB4746 at validate (missing payloads warn), AB4747-AB4749 as
build-time refusals, AB4750 staleness nudge.
examples/rsc-agent-runtime migrates in the same change: it deletes
scripts/package-hosts.mjs and packaging/, declares its Rsbuild output trees
as payloads with prebuilt MCP/hook entries, and packages hosts with
`agent-bundle build --json --output dist/plugins`. Its Rsbuild config pins
the runtime flavor (`mode: 'production'`) that was previously implied by
the epoch build's in-process NODE_ENV side effect.
🦋 Changeset detectedLatest commit: ced8257 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
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 |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
…-adapters # Conflicts: # docs/diagnostics.md # packages/agent-bundle/src/config/discover.ts # packages/agent-bundle/src/config/validate.ts
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e180e17546
ℹ️ 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".
commit: |
Wholesale invalidations (registry replay gap, runtime shutdown, foreground replacement) and server-announced revocations now record the superseded session revision before revoking bindings, and runtime previews degrade to fallback instead of re-creating authority from superseded run evidence. Also lands review follow-ups: prebuilt hook commands are exempt from the AB6018 wrapper-index coherence check, validate-command diagnostics retain warnings and infos, and runtime response debug logging is removed.
…s, e2e prebuilt entry Prebuilt hook commands are exempt from the AB6018 wrapper-index coherence check, validate-command diagnostics retain warnings and infos (AB4750 stays visible), runtime response debug logging is removed, and the overview e2e transport probe targets the prebuilt stdio entry shape.
…dation A dev artifact epoch flip (for example a consumer compiler rewriting prebuilt payload outputs beside the dev host) put the capability gate back into its loading state, unmounting the whole workbench UI and any live runtime preview with it. The remounted preview then re-created its session binding from stale evidence, which surfaced as duplicate POST /api/runtime/apps creates and teardown hangs in the overview e2e suite. Keep the last known catalog rendered while the new epoch's capabilities load (or fail), so revalidation is invisible to mounted routes. This supersedes the session-superseded revision guard, which is reverted here: with the UI stable across epochs, the preview never remounts from stale evidence in the first place.
An argument-less prebuilt hook command parses exactly like a compiler wrapper command; the coherence check recognizes it by payload location. Pin that a build with such a hook validates and revalidates without AB6018.
… narration Reuse the exported isInside and isRecord helpers instead of the two open-coded reimplementations in discover.ts and normalize.ts, drop the type assertions the isPrebuiltEntryInput guard already narrows away, collapse the duplicated empty-payload branches in discoverPayloads, and extract the payload targets check so validatePayload stays shallow. Write the two capability-state updates in main.tsx with the conditional spread the rest of the codebase uses, rather than duplicating the object literal per branch. Comment edits keep one authoritative statement per rationale and drop narration about what the branch changed.
* refactor: simplify prebuilt-payload internals (post-#71 follow-up) Deferred /simplify pass on PR #71's merged diff, applying the reviewer findings that survived aggregation: - Skip the AB4750 payload-freshness mtime walk unless the operator ran `validate` — every dev/inspect/build preparation was paying a blocking full-project stat sweep whose diagnostics were then discarded. - Extract payloadDeclarationEntry/payloadDeclarationSource in discover.ts and consume them from configuredPayloadRoots, discoverPayloads, normalizePayloads, declaredPayloads, and validatePayload, replacing five open-coded copies with three divergent malformed-input policies. - Extract owningPayload and share the innermost-payload ownership rule between prebuiltArtifactPath and validatePrebuiltReference, which had drifted to inside-or-equal semantics in validate.ts. - Make PreparedProject.snapshotSource required so the artifact-service fallback that silently dropped payload roots (recreating the drift bug the closure exists to prevent) is unrepresentable. - Probe prebuilt references against one Set of artifact paths, share the AB4748 emitter, reuse fast-glob stats over per-file stat calls, trust the declared hook-args type instead of silently filtering, and derive MCP-app prebuilt-ness from normalized provenance instead of raw config. - Migrate prebuilt-payload.test.ts onto the shared project fixture helpers used by the sibling suites. * chore: add patch changeset for the prebuilt-payload simplification * chore: retrigger CI (previous run wedged in queue)
Summary
Implements the deferred "prebuilt payload" adapter mode — RFC #50 Phase 3, the examples-phase2 plan's Q9 — and migrates
examples/rsc-agent-runtimeoff its hand-rolled host packaging in the same PR as proof.Config surface
payloadblock —payload: { runtime: './dist/runtime', app: { source: './dist/app', targets: [...] } }. Each key names an artifact-root destination directory; the whole source tree is packaged byte-for-byte into every selected target.{ prebuilt: ... }markers onmcp.servers.<name>.entryand hookhandlerpoint the generated host manifests at files inside a declared payload. Hooks additionally accept shell-safeargs(e.g.['--host', 'claude']) — prebuilt handlers only.import.meta.url(the reason this example compiles withsplitChunks: false) — and hosts, manuals, and tests pin the entry paths. Integrity stays content-addressed anyway: every payload file lands in the artifact manifest with its SHA-256 under the newprebuiltfile kind, and payload files hash intoproject.sourceInputs, so the project revision changes whenever payload bytes change.${CLAUDE_PLUGIN_ROOT}/runtime/mcp/stdio.js, Codex./runtime/mcp/stdio.js+cwd: "./",${PLUGIN_ROOT}/…), theAGENT_BUNDLE_PLUGIN_ROOTenv anchor is injected as usual, and the existing MCP artifact-reference validator confirms the referenced payload file is present and manifested. Prebuilt hooks emitnode "<root>/<payload path>" <args…>into the generatedhooks/hooks.jsonand are packaged like native hooks (no compiled wrapper, not in the simulatable hook index).kind: 'prebuilt'; manifest/ArtifactOutputKindgain theprebuiltfile kind; prebuilt files are exempt from generated-content validation (bundled-ESM graph, strict generated JSON) but remain hash-locked to the manifest, and the AB6014 ownership check admits them by manifest kind.docs/diagnostics.md):AB4740–AB4742config errors,AB4743/AB4745warnings for not-yet-built payloads/entries (surfaced throughvalidate; dev flows keep working from a clean checkout),AB4744/AB4746reference/args errors,AB4747–AB4749build-time refusals (empty payload, absent prebuilt entry, payload/output overlap),AB4750staleness info nudge.Example migration (the consumer proof)
scripts/package-hosts.mjs(75 lines) andpackaging/(7 hand-rolled host manifests, 88 lines).agent-bundle.config.tsis now the single declaration: payload trees, a prebuilt MCP entry, and one prebuilt hook per host (--host claude/--host codex) replacing the dual dev-config + native-hooks.json declaration;marketplace: truekeeps the Codex marketplace document."build": "rsbuild build --mode production && agent-bundle build --json --output dist/plugins"— the demo's multi-environment RSC compilation stays untouched as the subject of the example.rsbuild.config.tspins the runtime flavor (mode: 'production'): previously the dev artifact epoch's bundler runs setNODE_ENV=productionin-process before the provider session compiled, silently pinning production React; with nothing left to compile the flavor would float with ambientNODE_ENV(development React's Flight debug frames overflow the workbench's 4 KB preview cap).Test-pin updates
docs-contract.test.ts— build-script pin updated in the same commit with rationale.host-artifacts.test.ts— repackaging step is nowagent-bundle build; manifests pin the config-owned identity (rsc-agent-runtime-demo/1.0.0), thetimelineserver key, canonical capability matchers (^(?:Write|Edit)$,^(?:apply_patch|Edit|Write)$), the token-free-Codex guarantee restated as "no${…}host tokens" (the env anchor is a plain variable name), and no emptyskills/directory. Stable entry paths and the exact hook command shape (node "${ROOT}/runtime/hook/index.js" --host <host>) are unchanged.dev-provider.integration.test.ts— one prebuilt hook per host instead of one dual-target hook; the dev-epoch test provisions the payload it now packages.runtime-playground.e2e.test.ts+ fixture — the fixture provisions the example's Rsbuild payload; Hooks/Playground nav pages are hidden for this example (prebuilt hooks are packaged like native hooks).Gate results
pnpm build,pnpm typecheck,pnpm lint— clean.pnpm test:unit— 1760 passed (includes newprebuilt-payload.test.tscovering adapter lowering across claude/codex/portable, manifest provenance, artifact revalidation from disk, validation diagnostics, and the three build refusals).examples:check— all examples green, including the migrated demo (176 tests: 170 passed, 6 platform skips).examples-contract.test.ts,test:packed(22/22, manifest kind union extended),check:runtime-topology(doc regenerated for the deleted script),eval:spot— green.main(5/8 vs 1/8) under load, so that instability is environmental, not introduced here.Refs #50 (Phase 3, item: adapter prebuilt-payload mode / examples-phase2 Q9).