Skip to content

build: gate releases on attw + declaration-import check; manifest hygiene for agent-bundle, rsc-markdown-stream, create-agent-bundle (#566 §2) - #568

Merged
ScriptedAlchemy merged 9 commits into
mainfrom
build/pack-validation
Sep 5, 2026
Merged

build: gate releases on attw + declaration-import check; manifest hygiene for agent-bundle, rsc-markdown-stream, create-agent-bundle (#566 §2)#568
ScriptedAlchemy merged 9 commits into
mainfrom
build/pack-validation

Conversation

@ScriptedAlchemy

@ScriptedAlchemy ScriptedAlchemy commented Sep 5, 2026

Copy link
Copy Markdown
Owner

Wave 1 (S1 + S2) of the composite-plugin plan for #555 — the DECISION + SPEC and the implementation plan comment. Two commits; decisions D1, D5, D7 taken as final.

Commit 1 — S1: remove plugin from the public target model

  • config/normalize.ts no longer synthesizes the plugin lowering target; adapters/registry.ts no longer registers pluginAdapter; adapters/plugin.ts is deleted along with everything only it used (the Universal hook-wrapper codec, intersectCapabilityStates / unionCapabilityStates / mergeCapabilityEvidence / capabilityBooleanView, the lowersConfigExtensions adapter hook and Registry#lowersConfigExtension).
  • targets: ['plugin'] and --target plugin are rejected as an unknown target (AB4100); create-agent-bundle --target accepts only claude|codex|cursor|portable.
  • Target precedence and the default inclusion policy (portable) for real hosts are unchanged.

Commit 2 — S2: plan the selected projections into one composite root

  • build/build.ts stages one root at artifactRoot; only the selected hosts' planners run. New build/compose.ts merges byte-identical entries once, fails same-path/different-bytes with AB4103 (projections compared in host-name order, paths in path order), and refuses host-scoped components another selected host would discover conventionally with AB4105 (D5). adapters/composite-layout.ts owns the composite identity (claude+codex), sorted projections, hook-wrapper naming, and the folder-discovery shadow rule.
  • Codex and Cursor hook/MCP documents move beside their manifests (.codex-plugin/hooks.json, .codex-plugin/mcp.json, .cursor-plugin/hooks.json, .cursor-plugin/mcp.json); when another selected host claims their conventional path they emit an empty shield document so folder discovery cannot leak the other host's file. Claude keeps hooks/hooks.json + .mcp.json; portable keeps plugin.json + mcp.json.
  • A hook shared by several selected hosts compiles to hooks/<name>.<host>.mjs per host; a single-host hook keeps hooks/<name>.mjs. Native hooks are preserved for every selected host (the old composite path stripped them). Event-route wrappers bake the composite identity as artifactTarget (the MCP endpoint) and the invoking host as target.
  • Compiled MCP entries, scripts, routed CLI bins, and MCP apps compile once and are attributed to the composite identity; inspect --bundler reports distPath.root as <output>. INSTALL.md / install.mjs are composed once over the selection. agent-bundle.manifest.json records the selected projections and provenance only; agent-bundle.hooks.json indexes the selected hosts.
  • Capability and notice-delivery diagnostics, artifact validation (validate-artifact*.ts), and the pack inventory are computed over the selected projections at the root. AB4102 reports root-relative paths keyed per target.
  • Consumers moved to the composite root: install/doctor resolveBundleRoot (manifest directly under --from, no <from>/<host> lookup), dev epoch store, artifact/skill/script/hook-playground/MCP-session services, Workbench artifact inspector (one tree, root ., per selected host), eval harnesses (Claude/Codex plugin dir = root), rstest/browser.ts, examples (host-test, rsc-agent-runtime), and the tests.

Composite root layout (examples/host-test, targets: ['claude','codex','cursor','portable'])

artifact/
├── .agents/plugins/marketplace.json          # Codex marketplace
├── .claude-plugin/{plugin.json,marketplace.json}
├── .codex-plugin/{plugin.json,hooks.json,mcp.json}
├── .cursor-plugin/{plugin.json,marketplace.json,hooks.json,mcp.json}
├── .mcp.json                                 # Claude Code MCP document
├── plugin.json, mcp.json                     # portable (Agent Plugins) documents
├── hooks/hooks.json                          # Claude Code hook document
├── hooks/<hook>.mjs                          # hook selected for exactly one host
├── hooks/<hook>.{claude,codex,cursor}.mjs    # hook shared by several selected hosts
├── hooks/hooks-flight.mjs
├── mcp/mcp-<server>-<hash>.mjs (+ -flight)   # compiled once
├── bin/<plugin>.mjs, bin/<plugin>-flight.mjs # routed CLI
├── skills/, scripts/, assets/, mcp-apps/     # shared, emitted once
├── INSTALL.md, install.mjs                   # install surface (install.mjs: cursor|portable)
├── agent-bundle.manifest.json                # selected projections + provenance
└── agent-bundle.hooks.json                   # hook index over the selected hosts

Pinned by tests/build-compose.test.ts — "emits one root whose top-level entries are exactly the selected projections and shared surfaces (acceptance 1)" asserts the exhaustive sorted top-level listing (and of each dotfolder, hooks/, scripts/, skills/, commands/), and the Codex-only / portable-only / Cursor-only cases assert theirs.

Deleted files

  • packages/agent-bundle/src/adapters/plugin.ts
  • packages/agent-bundle/tests/plugin-bundle.test.ts
  • packages/agent-bundle/src/build/target-stages.ts → renamed build/compile-stages.ts (per-target staging removed; only the shared compile stages remain)

Acceptance tests (packages/agent-bundle/tests/build-compose.test.ts unless noted)

# Test
1 emits one root whose top-level entries are exactly the selected projections and shared surfaces; omits every unselected projection: a Codex-only root carries no Claude, Cursor, or portable files
3 tests/cli.test.ts--target plugin fails with AB4100; tests/config-validate.test.tstargets: ['plugin'] fails with AB4100
4 defaults to the portable projection when targets are omitted
5 emits byte-identical roots however the targets are ordered
6 compiles shared surfaces once and points every selected host document at the same file
7 keeps every selected host's native hook document and drops unselected hosts' hook files
8 records only the selected projections in the artifact manifest and hook index
AB4103 refuses one path planned with different bytes by two selected projections
AB4105 refuses a host-scoped component another selected host would discover conventionally (decision D5)

Diagnostics

  • AB4100 — reworded: plugin is an unknown target.
  • AB4103 (new, build error) — same path, different bytes across selected projections; deterministic ordering.
  • AB4105 (new, build error) — host-scoped component leaks into a conventionally discovered directory of another selected host.

Decisions beyond D1/D5/D7

  • Static relocation of Codex/Cursor documents. .codex-plugin/hooks.json|mcp.json and .cursor-plugin/hooks.json|mcp.json are fixed paths regardless of the selection, so document paths are constants (codexArtifactPaths, cursorArtifactPaths); only hook-wrapper naming is selection-aware.
  • Shield documents. A Codex/Cursor projection whose conventional hooks/hooks.json / mcp.json path is claimed by another selected host emits an empty document at its side path and points its manifest at it, so the host's folder-discovery fallback cannot load the other host's file.
  • Hook wrapper naming. Suffix only when the hook is shared by more than one selected host; single-host hooks keep the plain name so a Claude-only build is unchanged.
  • Sorted targets. The model, manifest, hook index, and inspect list targets sorted by name (acceptance 5); selectedTargetNames dedupes and sorts.
  • AB4102 keyed per target. Duplicate-output detection keys on <target>\0<generatedPath> internally and reports the root-relative path.
  • lowersConfigExtensions removed from the adapter model (only the composite adapter used it).
  • AB4104 / acceptance 9 not implemented (not in the brief): portable+codex / portable+cursor rely on AB4103 for byte conflicts and otherwise merge (S9).

Handed to S3/S4/S5

  • S3install/install.ts, install/doctor.ts: minimal change (manifest must sit directly under --from; the <from>/<host> fallback and its two tests are deleted). AB7001 rewording per the plan (D2), uninstall, dev services' relocation-awareness beyond "root = epoch", and eval harness Codex home layout stay with S3. dev/artifacts/projection-digest.ts hashes the whole root per selected host (one digest per host, identical bytes) — S3 may specialize.
  • S4build/pack-inventory.ts only follows the root-relative layout; npm package root = plugin root untouched.
  • S5install/surface.ts is composed once per selection (INSTALL.md for any built-in host, install.mjs for cursor|portable); per-host source-type rendering (AB4106) and distribution forms stay with S5.
  • S7 — nothing emits AGENTS.md after S1; the reserved-name entry in config/normalize.ts and the rootDocuments doc comment in adapters/types.ts are left for S7.

Remaining plugin literals (all non-target uses)

The audit (rg -n "'plugin'|\"plugin\"|/plugin\b" packages/agent-bundle/src packages/create-agent-bundle examples) finds no reference to the removed target: no target list, target === 'plugin', capability table, help text, or type union names it. Every remaining hit is one of:

  • plugin.json / .claude-plugin / .codex-plugin / .cursor-plugin manifest paths and schema $ids (adapters/{claude,codex,cursor,portable}.ts, build/pack-inventory.ts, install/{install,doctor,cursor-hooks-registration,cursor-marketplace,receipt}.ts, host-contracts/*-plugin-validation.ts, test/installed.ts, schemas/**, examples/rsc-agent-runtime/tests/**);
  • host CLI subcommands and flags — claude plugin …, codex plugin …, --plugin-dir (install/*.ts, eval/codex-plugins.ts, host-contracts/host-contract.ts, host-contracts/native-codex-contract.ts, build/artifact-diagnostics.ts, examples/*/scripts/*.mjs);
  • the per-host "plugin" capability section in adapters/capabilities/*.json (manifest features, not a target);
  • config.plugin / plugin-identity.ts, @rsbuild/plugin-react, and prose.

Four backtick-prose mentions of the old target (config/validate.ts, create-agent-bundle/src/{options,scaffold}.ts, create-agent-bundle/tests/options.test.ts) were rewritten.

Docs

docs/diagnostics.md (new "The composite plugin root" section: AB4100, AB4103, AB4105), docs/framework-mode.md, docs/entry-conventions.md, READMEs, and the website (en + zh) pages describing targets, output layout, install, hooks, and the CLI/config reference.

Self-review

SELF_REVIEW_PLACEHOLDER

…iene for agent-bundle, rsc-markdown-stream, create-agent-bundle

- lint:release runs attw --profile esm-only on all three packed tarballs and
  scripts/check-declaration-imports.mjs, which fails when a shipped .d.ts a
  consumer can reach imports a devDependency or an undeclared package
  (internal declarations warn; --strict escalates)
- expose ./package.json in exports on all three packages (create-agent-bundle
  gains an explicit exports map)
- agent-bundle: peer @agent-bundle/runtime >=0.0.0 <1 instead of *, drop the
  duplicate @modelcontextprotocol/server devDependency and the dead
  !dist/workbench/**/*.map files entry
- rsc-markdown-stream: declaration/value-export parity test for the
  hand-written index.d.ts
- keep per-file dts (API Extractor bundling measured and rejected)
@changeset-bot

changeset-bot Bot commented Sep 5, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: fabb05f

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

This PR includes changesets to release 3 packages
Name Type
agent-bundle Patch
rsc-markdown-stream Patch
create-agent-bundle Minor

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

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@pkg-pr-new

pkg-pr-new Bot commented Sep 5, 2026

Copy link
Copy Markdown
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle@568
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle/create-agent-bundle@568
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle/rsc-markdown-stream@568
npm i https://pkg.pr.new/ScriptedAlchemy/agent-bundle/@agent-bundle/runtime@568

commit: fabb05f

@ScriptedAlchemy
ScriptedAlchemy enabled auto-merge (squash) September 5, 2026 01:18
@ScriptedAlchemy
ScriptedAlchemy merged commit 8c8907e into main Sep 5, 2026
14 of 15 checks passed
@ScriptedAlchemy
ScriptedAlchemy deleted the build/pack-validation branch September 5, 2026 01:52
ScriptedAlchemy added a commit that referenced this pull request Sep 5, 2026
…e in lint:release

#568 added the gate for the other three publishable packages; the runtime
tarball this PR reshapes now goes through the same attw --profile esm-only
and scripts/check-declaration-imports.mjs run (0 errors, 0 warnings).
ScriptedAlchemy added a commit that referenced this pull request Sep 5, 2026
…1.x, expose ./package.json (#571)

* build(rsc-runtime): one module graph, drop @modelcontextprotocol/sdk 1.x, expose ./package.json

`@agent-bundle/runtime` imported `CallToolResult` from `@modelcontextprotocol/sdk`
1.x for two type positions, which kept the whole 1.x SDK (express 5, hono, jose,
cors, ajv, ...) in every consumer's install. Take the type from
`@modelcontextprotocol/server` 2.x (already a dependency) and publish the exact
result shape the lowerers emit as `McpCallToolResult` / `McpContentBlock`, whose
`_meta` and `structuredContent` are finite JSON objects, so the value stays
assignable to both SDK lines' `CallToolResult`.

Build every public entry in one Rslib lib instead of six bundle-mode libs
stitched with literal-request `output.externals`. Shared modules land in common
chunks once, so each error class is defined once in the dist and `instanceof`
holds across subpaths. The old externals missed `src/state/sqlite.ts`'s direct
`./contract.js` import, so today's tarball carries a second `AgentStateError`
(and three `AgentContractError` / `AgentRequestError`): a sqlite store's
`read({ revision: -1 })` rejection was not `instanceof` the class `./state`
exports. Entry graphs stay disjoint where they must: `node:sqlite` loads only
through `./state/sqlite`, the root and `./plugin` carry no kernel code.

Tests: `state-packaging` now walks the dist import graph per entry, asserts one
definition per error class, and runs a child-process probe that imports every
entry, checks `process.moduleLoadList` for `node:sqlite`, and compares errors
thrown by the sqlite and mount entries against `./state`'s class. A new packed
test installs the release tarball and repeats the probe through the `exports`
map. Both fail against the previous build.

Manifest: `./package.json` export; `@rspack/core` peer marked optional (no
runtime entry imports it; it is `react-server-dom-rspack`'s build-plugin peer).
`react`/`react-dom` stay exact and required: the Flight wire format is pinned to
the React build and `react-server-dom-rspack` loads both at import time.

* changeset: @agent-bundle/runtime hygiene (#571)

* fix(rsc-runtime): keep attachMcpStructuredContent generic; harden packaging graph tests

attachMcpStructuredContent is generic over its input so a CallToolResult
typed by either MCP SDK line round-trips instead of being narrowed to
McpCallToolResult. The dist-graph tests count class definitions by
occurrence, check sqlite confinement by entry reachability, exclude the
kernel error classes by definition file, assert every dist file is
reached by some entry, run the same static checks against the installed
tarball, and strip NODE_OPTIONS from probe children. docs/preview-packages
notes the @rspack/core peer is optional.

* docs(rsc-runtime): document McpCallToolResult; pin 1.x CallToolResult compatibility in the example's tests

The website MCP page (en/zh) and the package README name the lowered
result type and its assignability to both MCP SDK lines. The
rsc-agent-runtime example's lowering test annotates a lowered result and
the attachMcpStructuredContent round-trip as the 1.x SDK's CallToolResult,
so the example's typecheck fails if either narrows again.

* build: run attw and the declaration-import check on the packed runtime in lint:release

#568 added the gate for the other three publishable packages; the runtime
tarball this PR reshapes now goes through the same attw --profile esm-only
and scripts/check-declaration-imports.mjs run (0 errors, 0 warnings).
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