Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/dev-epoch-contract-matrix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"agent-bundle": minor
---

Gate live host and development-install epoch adoption on an opt-in, project-declared contract matrix (`dev.contracts`). Each published epoch runs the generated contract matrix through an epoch-pinned generated stdio session at the new `dev-epoch` proof level (`runDevEpochContractMatrix`); failing epochs stay inactive for live host MCP connections and `--install-host` installs while the last passing epoch keeps serving, and are reported on the `dev.contract.status` project event with `AB7210` (invalid declaration or fixture module) or `AB7211` (contract violations). `startDevServer().status()` and `/api/project/status` now carry a `hostAdoption` snapshot (`mode`, `adoptedEpochId`, latest `contracts` evaluation) that the Workbench Overview renders as **Host adoption**. A cold start whose initial build fails now seeds the restored last-good epoch through the same gate instead of leaving hosts without an epoch.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ The same config also owns the npm package build — no second bundler config, bi
- `build` — validate the project and write an artifact (plus the `bin`/`lib` package build when declared)
- `validate` — check project source, or a built artifact with `--artifact <dir>`
- `inspect` — show the normalized configuration and per-target plans; `--bundler` dumps the synthesized bundler configs (post-`tools`-hatch merge)
- `dev` — serve the local development workbench and rebuild the `dist/` package build when its inputs change
- `dev` — serve the local development workbench and rebuild the `dist/` package build when its inputs change; `--install-host <claude|codex|cursor>` installs a development variant whose stable `dev proxy` MCP command hot-swaps epochs behind the host's open connection and re-syncs hooks and Skills on every adopted rebuild (see [Framework mode › Live development into hosts](docs/framework-mode.md#live-development-into-hosts))
- `mcp list` / `mcp invoke` / `mcp run` — list, invoke, or run an artifact's MCP servers locally
- `hooks list` / `hooks simulate` — inspect and simulate generated hooks
- `eval` — run eval suites against a built artifact
Expand Down
13 changes: 13 additions & 0 deletions docs/architecture/rsc-runtime-workbench.md
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,19 @@ available for a selected run. Failed preparation retains the last good active
generation. Static MCP definitions and the broker survive independently of
generation-pinned invocations and binding authority.

Host-facing adoption is a further, separately gated axis (#179 / #218 stage 4).
`EpochAdoptionPolicy` sits between `artifact.available` and the two surfaces a
real host holds: `HostMcpRoutes` (the stable `/mcp/host/<server>` endpoint the
`dev proxy` stdio process forwards) and `DevHostInstallManager` (the
`--install-host` generations). Without `dev.contracts` it forwards every
published epoch; with it, `runDevEpochContracts` opens an epoch-pinned generated
stdio session through `McpSessionService`, runs the shared contract matrix at
the `dev-epoch` proof level, publishes `dev.contract.status`, and adopts only a
passing epoch. Workbench playground sessions stay epoch-pinned on their own and
are never gated. `ProjectStatus.hostAdoption` exposes the adopted epoch and the
latest evaluation, and the Overview renders it as **Host adoption** beside the
published build, so a rejected epoch is visible rather than silently skipped.

The RSC result tree is not the MCP App document. A current preview moves through
`McpAppPreview`, `SecureAppRenderer`, the official App renderer, the
generation-bound bridge, and the runtime client-surface proxy to an opaque-origin
Expand Down
24 changes: 24 additions & 0 deletions docs/diagnostics.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,15 @@ gate a build, a validation, or a dev rebuild.
| `AB60xx` | Built-artifact validation, including schema documents and referenced files (`AB6025`: a manifest-declared `logo` path is missing from the artifact or escapes the deploy tree; `AB6034`: emitted Skill Markdown has no instruction body). |
| `AB700x` | Host installation: bundle identity, host availability, scope, command failure, and collision checks. |
| `AB7010`–`AB7013` | npm prepack inventory, artifact freshness, package bin targets, and release-version agreement. |
| `AB7200`–`AB7202`, `AB7210`–`AB7211` | Development rebuilds and live host surfaces: rebuild admission and phase failures, development host install sync, and the dev-epoch contract gate (see below). |
| `AB7xxx` | Project preparation and development rebuilds. |
| `AB7300`–`AB7320` | Read-only install Doctor: host probes, installed inventory, bundle comparison and registration proof, runtime endpoint health and identity, durable-state inventory, and static bytes-at-rest validation. |
| `AB8200`–`AB8209` | Workbench development runtime routes (`/api/runtime/**`): `AB8200` development runtime provider configuration, load, or lifecycle failure, `AB8201` runtime/session/run not available, `AB8202` invalid route path, `AB8203` invalid request shape, `AB8204` stale runtime generation or MCP session revision (409), `AB8205` runtime request could not be completed, `AB8206` Workbench runtime client failure, `AB8207` Agent Document decoding needs the optional `@agent-bundle/runtime` peer (503), `AB8208` stored Flight could not be decoded as an Agent Document (409), `AB8209` decoded Agent Document over the 16 MiB budget (413) or an invalid document response. |
| `AB8210`–`AB8214` | Workbench semantic lifecycle replay routes (`/api/lifecycles`, `/api/lifecycles/replays`): `AB8210` invalid path, `AB8211` malformed replay request or native envelope (400, carries the shared validator message), `AB8212` replay unavailable or could not be completed, `AB8213` stale manifest binding (409; the page repairs it with refresh → explicit re-run), `AB8214` replay over the 16 MiB budget (413). |
| `AB8215`–`AB8218` | Workbench read-only host discovery route. |
| `AB8219`–`AB8223` | Workbench live MCP probe route (user-initiated, read-only initialize + tools/list): `AB8219` invalid path, `AB8220` invalid request/method, `AB8221` probe target not found, `AB8222` response over the 16 MiB budget, `AB8223` probe unavailable. |
| `AB8233`–`AB8235` | Workbench browser-side strict decoders rejecting a dev-server response: `AB8233` lifecycle replay, `AB8234` host discovery, `AB8235` MCP probe report. |
| `AB8024`–`AB8025` | Live host MCP proxy: epoch drift behind a host connection and dev-server unavailability (see below). |
| `AB8xxx` | Development server configuration. |
| `AB9xxx` | Eval selection, harnesses, and persisted runs. |

Expand Down Expand Up @@ -454,6 +456,28 @@ host CLI, repair a bundle, or perform a live protocol exchange.
| `AB7319` | error | A host tree resolved from `doctor --from` violates its pinned document schemas or process-free loader rules. The message retains the originating build-validator code and detail. | Rebuild that host bundle from valid source bytes, then rerun Doctor. |
| `AB7320` | error / info | Error when a `.cursor-plugin/plugin.json` install violates Cursor's pinned document schemas or token-location rules, or when any local plugin contains a symlink that escapes `~/.cursor/plugins/local`; the inventory entry is reported as `corrupt`. Info when a `.claude-plugin/plugin.json` or root `plugin.json` install has no Cursor-side pinned static document contract; the loader-recognized entry remains `installed`. | Reinstall an invalid Cursor plugin or repair an escaping symlink. For other manifest flavors, use that ecosystem's validator when static document proof is required. |

## Live development into hosts (`AB7200`–`AB7202`, `AB7210`–`AB7211`, `AB8024`–`AB8025`)

`agent-bundle dev` keeps a host's one stdio MCP process connected while it
swaps the generated plugin behind it (`dev proxy`), re-syncs opted-in
development installs (`--install-host`) on every adopted epoch, and — when a
project declares `dev.contracts` — gates host-facing adoption on the
development contract matrix. Every failure on that path is a structured
diagnostic; none of them silently changes what a host serves. A failing gate
is not a build failure: the epoch publishes to the Workbench playground, and
the Overview page's **Host adoption** section names both the published and the
host-facing build together with the failed checks.

| Code | Severity | Trigger | Recovery |
| --- | --- | --- | --- |
| `AB7200` | error | A development rebuild could not be admitted: the coordinator is closed, closing, or not yet started. | Restart `agent-bundle dev`; no epoch changed. |
| `AB7201` | error | The prepare, lint, or artifact phase of a development rebuild threw instead of reporting diagnostics. The message names the phase and the underlying error. | Fix the named failure and save again; the last-good epoch stays active. |
| `AB7202` | error | Publishing a new epoch into an installed development host (`claude`, `codex`, or `cursor`) failed. Pointers were rolled back to the previous generation and the failure was published on `dev.host.sync`. | Repair the host cache path or permissions named in the message; the next successful epoch re-syncs. |
| `AB7210` | error | `dev.contracts` is malformed, its `fixtures` module escapes the project root, cannot be loaded, or default-exports something other than route-id keyed `ContractRouteFixture` objects. Reported on `dev.contract.status` for the affected epoch; compilation is unaffected. | Correct `dev.contracts` or the fixture module and rebuild; host surfaces keep the last passing epoch meanwhile. |
| `AB7211` | error | The development contract matrix failed or could not complete for a published epoch. The message carries the aggregated `contract-violation` detail; `dev.contract.status` lists the failed check names grouped by route. That epoch is never adopted by live host connections or development installs. | Fix the failing route or fixture and rebuild; a passing epoch is adopted normally. |
| `AB8024` | error (MCP) | The epoch a live host connection was serving vanished from the epoch store mid-session. The connection is invalidated and the typed MCP error carries `{ code, epochId }`. | Reconnect from the host; the proxy binds to the currently adopted epoch. |
| `AB8025` | error (MCP) | `agent-bundle dev proxy` found no running development server for the project (cold start or shutdown), so the host-facing connection fails closed rather than serving stale bytes. | Start `agent-bundle dev` for that project root; installed hooks and Skills remain in place. |

## Development package build (`AB7103`)

`agent-bundle dev` rebuilds the framework-owned package build (`dist/` bin
Expand Down
35 changes: 35 additions & 0 deletions docs/framework-mode.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,41 @@ release-identity config rejects absolute paths. The per-invocation CLI
subject to the same project-root containment check; absolute and external
output roots are unsupported.

## Live development into hosts

`agent-bundle dev` is the webpack-HMR analog for plugins that are installed
and in use in a real host. Three pieces make a rebuild reach the host without
the host ever seeing a disconnect:

1. **A stable host-facing proxy.** `agent-bundle dev proxy --root <project>
--server <name> [--target <host>]` is the thin stdio process a host spawns
and holds. It forwards the developed plugin's MCP surface from the dev
server's `/mcp/host/<serverName>` endpoint. On every adopted epoch the dev
server opens and primes a session on the new generated server, promotes it
behind the same connection, emits `notifications/tools/list_changed` (and
the resources/prompts equivalents the catalog advertises), lets in-flight
calls finish against the epoch they started on, then drains the old
session. A failed build changes nothing; a vanished epoch or stopped dev
server fails closed (`AB8024` / `AB8025`).
2. **Installed-host re-sync.** `agent-bundle dev --install-host <claude|codex|cursor>`
installs a marked development variant through the ordinary installer once,
pointing the host's MCP document at the proxy, then re-syncs hooks, Skills,
and MCP Apps into the host's own layout on every adopted epoch with atomic
generation swaps and rollback (`AB7202`). Hooks are spawned per event, so
they pick up the new epoch on their next invocation.
3. **A contract gate on adoption.** Declaring `dev.contracts` in
`agent-bundle.config.ts` runs the generated contract matrix against each
published epoch through an epoch-pinned generated stdio session before any
host-facing surface adopts it. A failing epoch stays inactive for hosts,
is reported on the `dev.contract.status` project event (`AB7210` for an
invalid declaration, `AB7211` for violations), and appears in the
Workbench Overview's **Host adoption** section beside the published
build. Playground sessions stay independently epoch-pinned.

The package README's [Developer workbench](../packages/agent-bundle/README.md#developer-workbench)
section carries the exact commands, install layouts, and event payloads;
[Diagnostics](diagnostics.md) lists every code on this path.

## Distribution

`agent-bundle build` makes each target directory independently distributable.
Expand Down
40 changes: 40 additions & 0 deletions packages/agent-bundle/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,46 @@ epoch automatically. Use **Restart MCP session** to respawn that generated serve
epoch; open a new session to use a newly published epoch. Compatible MCP Apps preview through the
same bound session.

### Development contract matrix

Projects can opt host-facing rebuild adoption into the generated contract matrix by pointing
`dev.contracts.fixtures` at a project-local module:

```ts
// agent-bundle.config.ts
export default {
dev: {
contracts: {
fixtures: './contract-fixtures.ts',
server: 'tools', // optional when the project has exactly one MCP server
},
},
};
```

The module default-exports the same `Record<routeId, ContractRouteFixture>` consumed by
`runContractMatrix`. Agent Bundle reloads and validates it for every prepared epoch. An invalid
module does not fail compilation: it fails that epoch's contract run with an `AB7210` diagnostic
instead. Omitting `dev.contracts` leaves the matrix off and preserves direct `artifact.available`
adoption.

For an enabled project, each published epoch is exercised through an already-open, epoch-pinned
generated stdio session at the `dev-epoch` proof level. Passing epochs atomically replace the server
behind existing live host MCP connections and refresh opted-in development host installs. Failing or
timed-out epochs remain inactive on those host-facing surfaces (`AB7211`), leaving the last passing
epoch connected and installed. On a cold start whose initial build fails, the last-good epoch the
epoch store restored is seeded through the same gate before hosts serve it; when the project no
longer prepares at all, the `dev.contracts` declaration cannot be read and the restored epoch is
adopted directly, exactly as an undeclared project would be.

The Workbench project stream emits `dev.contract.status`, and `status()` (and `/api/project/status`)
carries a `hostAdoption` snapshot — `mode` (`gated` or `direct`), the `adoptedEpochId` hosts serve,
and the latest `contracts` evaluation. The Overview page renders it as **Host adoption**: a failed
gate names the published build, the build hosts kept, and the failed check names grouped by route,
and folds the gate diagnostics into the Diagnostics table; the Logs page carries the same records.
A later passing rebuild is adopted normally. Workbench playground sessions remain independently
epoch-pinned and are not gated by this matrix.

### Live host MCP proxy

During development, a host can keep one stdio MCP process connected while `agent-bundle dev`
Expand Down
1 change: 1 addition & 0 deletions packages/agent-bundle/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ export { startDevServer } from './dev/workbench-server.ts';
export type { DevServerSession, StartDevServerOptions } from './dev/workbench-server.ts';
export type {
AgentBundleDevConfig,
AgentBundleDevContractsConfig,
AgentBundleDevRuntimeConfig,
} from './core/types.ts';
export type {
Expand Down
Loading
Loading