Skip to content

Generated plugin contract matrix: catch source→artifact→dev→installed-host drift before release #218

Description

@ScriptedAlchemy

Context

This is a concrete acceptance-test child of #103 (generated consumer test harness), #133 (emitted-artifact conformance), #105 (Workbench lifecycle replay), #179 (stable dev-host proxy/HMR), and #181 (framework-owned host installs).

Developing https://github.com/ScriptedAlchemy/cargo-conductor exposed failures that all passed project unit tests and agent-bundle build, but broke only in the live plugin lifecycle:

  1. MCP result-schema drift: the daemon added execArgv, then errorCount / warningCount / diagnostics; generated/runtime response validation rejected otherwise-valid conductor_status and conductor_result payloads with unrecognized_keys. Agents institutionalized a CLI+jq workaround and reran cargo commands because they stopped trusting MCP.
  2. Status/result lifecycle gap: status rows intentionally omitted output tails, while result fetching skipped non-terminal rows. Running tickets therefore showed “no output until the run finishes” despite the daemon already streaming output. Source-level tests did not exercise an open MCP/UI consumer across queued→running→output→terminal transitions.
  3. Source/artifact/install drift: rebuilding source did not prove that the installed Cursor plugin, spawned stdio MCP process, dashboard resource, or daemon was the new version. The Workbench tested its own MCP control surface, not the developed plugin surface; stale processes/artifacts looked healthy.
  4. Target layout drift: Cursor local-plugin layout needed a hand-maintained install script and previously emitted artifacts did not match host discovery semantics (Cursor target artifacts don't match how Cursor actually loads local plugins (manifest location, hooks doc collision, root tokens) #126). Build success was not install success.
  5. Forward/backward compatibility: a new daemon with an older widget/client, and a new widget/client with an older daemon, were not tested as an explicit matrix.

Requested generated contract matrix

agent-bundle test (or the #103 Rstest integration) should generate and run the same contract at five boundaries:

1. Source route

  • invoke every operation with generated valid inputs;
  • validate the operation result against its declared result schema;
  • generate negative inputs from schema boundaries;
  • assert cancellation/timeout behavior.

2. Packed artifact

  • build once, delete or make source unavailable, spawn the exact generated MCP executable from the artifact;
  • initialize over the real transport, list tools/resources, call every operation, fetch every bound App resource;
  • assert stdout protocol purity and clean shutdown.

3. Stateful lifecycle replay

For each operation with a stateful fixture, keep one client connection open and replay:

unknown → queued → running → first progress/output → repeated progress → terminal

At every phase:

  • validate structuredContent and rendered/Agent Document output;
  • validate additive fields are accepted according to the declared compatibility policy;
  • assert live progress is visible before terminal settlement;
  • assert reconnect/restart retains durable state where declared.

The fixture API should let projects supply a deterministic transition driver; the framework owns transport, schema, render, and lifecycle assertions.

4. Version-skew matrix

Run at least:

  • current server + previous generated client/widget contract;
  • previous server fixture + current generated client/widget;
  • unknown additive response fields;
  • absent new optional fields;
  • every declared status/discriminant value.

Schema strictness should be explicit per boundary: user inputs may be closed; daemon/server outputs intended for rolling upgrades must prove their forward-compat policy. Generated compile-time satisfies checks are not enough — the exact serialized payload must round-trip through the exact runtime validator.

5. Installed-host smoke / doctor

Using the same framework-generated install plan from #181:

  • stage into a clean temporary host root;
  • run host discovery exactly as the adapter does;
  • verify manifests, component paths, MCP command, resources, hook commands, and version digests;
  • spawn the configured MCP command and run the lifecycle replay;
  • report source/artifact/installed/running versions separately so stale-but-healthy processes cannot pass.

Where a real host CLI is available, add an opt-in native lane; otherwise the adapter simulator is the required deterministic baseline.

Dev-server requirement

The Workbench/dev server should run this matrix against the developed plugin’s routes and resources, not only its own control tools. A rebuild should automatically rerun affected contracts and preserve last-good behavior:

Acceptance proof using cargo-conductor

A fixture plugin should include:

  • an operation whose response adds optional execArgv, diagnostics, and live-tail fields over successive fixture versions;
  • an App that opens a running ticket and must render output before terminal completion;
  • a durable record that survives a server restart;
  • Cursor/Claude/Codex target artifacts staged into clean roots.

The generated matrix must fail if:

  • any additive output field triggers unrecognized_keys;
  • a running App says output is unavailable when the server emitted progress;
  • source, built artifact, installed artifact, and running process versions disagree;
  • a host adapter cannot discover or spawn its emitted target layout.

Why this belongs in Agent Bundle

Projects can unit-test their business logic, but only Agent Bundle owns all five transformations: route contract → generated runtime → packed artifact → host projection/install → live protocol/render lifecycle. Requiring each plugin to rebuild this harness guarantees inconsistent coverage and repeats the exact drift the meta-framework is meant to eliminate.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions