Skip to content

feat(test): projection-contract proof levels for the consumer harness (#103 stage 2) - #190

Merged
ScriptedAlchemy merged 1 commit into
mainfrom
wave4/103-projection-harness
Sep 1, 2026
Merged

feat(test): projection-contract proof levels for the consumer harness (#103 stage 2)#190
ScriptedAlchemy merged 1 commit into
mainfrom
wave4/103-projection-harness

Conversation

@ScriptedAlchemy

Copy link
Copy Markdown
Owner

Closes the stage-2 slice of #103: the projection-contract proof levels that
stage 1 (#151) explicitly deferred — event-stream matchers and the
transport/CLI/packed levels.

Proof levels shipped (labels are load-bearing)

Every helper stamps its level into the result provenance and prints it in
every failure, because a pass at one level is never a receipt for another.

level helpers what it proves what it does NOT prove
mcp-in-memory openInMemoryMcpServer, invokeMcpTool, readMcpResource, getMcpPrompt, listMcpSurface the real generated MCP server's protocol contract, driven by a real MCP client over the SDK's InMemoryTransport pair no process, no stdio framing, no packed artifact — not host evidence
cli-dispatch invokeCli, cliJson an argv vector resolved and run through the routed CLI's own shell (#102 stage 2), in-process not a spawned binary
packed-stdio openPackedMcpServer a built artifact's generated entry running as a separate OS process over real stdio the only process evidence here; one journey only

renderRouteEvents returns the ordered #140 render-event stream alongside the
final document, and expectEvents asserts over it. toContainSequence is
sequence-tolerant — an extra progress or replace frame is legal and cannot
turn a passing render red — while a missing frame, a reordering, or a regressed
ordinal fails. toHaveMonotonicSequence bakes in the #120 lesson directly.

Cost-rule compliance

  • Packed pool: no new build, no new pack. The one packed journey draws the
    run-level sharedPackedTarball (one pack per pool, cachedNpmInstallArguments
    per perf(test): serve packed-pool installs from the npm cache with --prefer-offline #109), installs once, builds one artifact, spawns one server, and iterates
    every per-route assertion inside that single client session.
  • New test:projection pool is in-process only: 22 tests, no build, no pack,
    no spawn. It is wired into pnpm test and pnpm check.
  • No per-PR packed journey beyond the single one.

Core-first: the shared server runtime

The in-memory level must exercise the artifact's own server, not a second copy
of it, so the generated MCP server's warm Flight host (#175), artifact epoch,
route registration, MCP progress projection, and event-runtime lifecycle (#180)
move out of the entry template into src/mcp-server-runtime.ts. The generated
entry aliases that module (like agent-bundle/mcp-entry) and now only supplies
data: routes, plugin identity, epoch, and the event binding. Generated-entry
behaviour is unchanged; tests/entry-shell.test.ts now asserts the delegation
and that nothing the runtime owns is re-templated.

Two defects this surfaced

  • Generated executables crashed on any JSX-authored route (separate patch
    changeset). Route entries were bundled without the React plugin, so Rslib
    lowered JSX to the classic React.createElement factory, which no generated
    entry or Flight worker has in scope: every documented .tsx route failed at
    run time with React is not defined. It survived because every build-level
    test authored routes with an explicit createElement import; the
    generated-route server test now authors its tool route as JSX.
  • Event routes rendered with the wrong props. The harness passed the raw
    invocation payload, so a route written against the public
    AgentEventRouteProps ({ canonical, native, signal }) received undefined
    for both. renderRoute now performs the same unwrapping the generated Flight
    worker does. expectDocument gains toContainContext for the context nodes
    an event route returns to its host.

Lane coordination

Scoped results

Changesets: minor (public agent-bundle/test surface grows) + patch (JSX
runtime fix).

…harness

#103 stage 2. `mcp-in-memory`, `cli-dispatch`, and `packed-stdio` join
`route-unit`, each labeled in its provenance and in every failure message.
The generated MCP server's warm host, route registration, and projection move
into a shared runtime module the generated entry aliases, so the in-memory
level exercises the artifact's own code.
@changeset-bot

changeset-bot Bot commented Sep 1, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: a111a57

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 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

@ScriptedAlchemy
ScriptedAlchemy merged commit 3ae7721 into main Sep 1, 2026
5 of 9 checks passed
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 1, 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-01T20:15:18.603497Z a111a57 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.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a111a573c4

ℹ️ 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".

Comment on lines +96 to +104
readonly cliCommands: readonly CompiledCliCommand[];
/** The absolute config path the compiler pass evaluated. */
readonly configPath?: string;
/** Diagnostics from the same pass, so a harness failure can name a compiler cause. */
readonly diagnostics: readonly Diagnostic[];
/** The route graph digest: project-relative route identity, equal on every machine. */
readonly digest: string;
/** Plugin name and version, as the generated MCP server reports them in `initialize`. */
readonly plugin: TestManifestPluginIdentity;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Bump the registry version for the expanded manifest

Adding required cliCommands and plugin fields changes the cross-package registry contract, but AGENT_TEST_REGISTRY_VERSION remains 1 in src/test/registry.ts. When the Rstest configuration and agent-bundle/test resolve to pre- and post-change package versions, compatible() therefore accepts the old manifest instead of reporting the intended version mismatch; invokeCli then crashes on manifest.cliCommands.length, while MCP helpers receive no plugin identity. Increment the registry version so mixed installations fail with the existing actionable compatibility error.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in #206 (merged as 7dbfacf): AGENT_TEST_REGISTRY_VERSION bumped to 2, so registries missing the required cliCommands/plugin fields fail the existing version gate with a recoverable AgentTestError before invokeCli or the MCP helpers read them. Regression test: version-1 manifest refusal in test-harness-manifest.test.ts.

Comment on lines +184 to +193
const root = manifest.projectRoot;
const result = await runtime.runAgentRequest({
capabilities: {
command: runtime.unavailable(),
filesystem: runtime.unavailable(),
network: runtime.unavailable(),
projectRoot: runtime.available({ root }, 'derived'),
},
host: runtime.unavailable('unsupported-surface'),
workspace: runtime.available({ root }, 'derived'),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use the generated CLI's actual working directory

When tests run from a directory other than the compiled project root, this bridge exposes manifest.projectRoot as both workspace and capabilities.projectRoot, while the generated executable explicitly derives both from process.cwd() in src/build/entry-shell.ts. Commands that inspect the request workspace can consequently pass through invokeCli but behave differently when users run the shipped binary elsewhere. Default this proof level to the real current working directory, or accept an explicit cwd override that mirrors process invocation.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in #206 (merged as 7dbfacf): invokeCli now derives the request workspace from process.cwd(), matching the generated binary (verified against the entry-shell generated code). Regression test in projection/cli-dispatch.test.ts.

Comment on lines +234 to +235
const call = await invokeMcpTool('summarize', { input: { title: 'Dune' } });
expect(call.result.structuredContent).toEqual({ chapters: 24 });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Read structured content from the documented return shape

The newly documented example cannot type-check because invokeMcpTool returns McpToolInvocation, whose structuredContent field is directly on call; there is no result property. Users copying the primary example for this new public helper receive Property 'result' does not exist, so the assertion should use call.structuredContent.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in #206 (merged as 7dbfacf): the README example now reads call.structuredContent, matching McpToolInvocation.

ScriptedAlchemy added a commit that referenced this pull request Sep 1, 2026
* feat(cli): compile src/cli/** routes into a routed CLI executable (#102 stage 2)

Conventional command routes now compile into one collision-checked command
graph on the route-graph IR: path nesting is identity, the static config
export supplies description/aliases/positionals/exit-code policy, and a
bounded documented zod grammar projects each route's inputSchema onto argv
with named AB4814 diagnostics for everything outside it. The graph feeds the
existing package-build pipeline as one generated Rslib executable (cli-entry
runtime shell aliased in, exactly like mcp-entry); commands run inside the
typed Agent request context and keep the one-JSON-line stdout contract.
New diagnostics AB4813 (command/alias/bin collisions), AB4814 (argv policy),
AB4815 (route contract), AB4816 (rendered commands gated until stage 3).

* feat(cli): render .tsx commands and scripts through the dispatcher (#102 stage 3)

Rendered CLI routes and conventional rendered scripts now execute through
the runtime dispatcher's public stream() against a sibling react-server
worker, with four output modes: in-place TTY progress, one final piped
Markdown document, --json canonical value, and --ndjson sequence-numbered
render events (CLI/script dialect, never MCP stdout). Plain .ts keeps
ordinary Node semantics. Lifts the AB4807/AB4816 stage gates (retired, not
reused), consciously flips the docs-contract pin to the narrowed runRscCli
compatibility claim with the routed-CLI replacement pins, and updates
framework-mode's power-tier reference. Proof: audiobook-curator's manual
CLI operation registry migrates to fifteen src/cli/ routes — fourteen plain
commands byte-parity, library-audit rendered.

* test(harness): pin the rendered flag on compiled CLI commands (#190 reconcile)
ScriptedAlchemy added a commit that referenced this pull request Sep 1, 2026
… can go green (#197)

* test: align hook expectations with sorted normalization (#180) and the example's semantic event route (#182)

Both features merged while main CI was already red, so their outdated
sibling tests were never caught: normalizeHooks now orders hooks by
stable id, the rsc-agent-runtime example declares one tool/after event
route instead of per-host prebuilt hooks, and the workbench therefore
exposes the simulatable Hooks capability page.

* fix(ci): keep route-unit proofs out of the example plain test pool

* fix(ci): defer to the mainline Skill IR digest canonicalization (#191)
ScriptedAlchemy added a commit that referenced this pull request Sep 1, 2026
…DME field (#206)

Post-merge review findings from #190: bump AGENT_TEST_REGISTRY_VERSION to 2
so registries missing the required cliCommands/plugin manifest fields fail
the existing version gate with a recoverable AgentTestError instead of
crashing inside invokeCli or the MCP helpers; derive the invokeCli request
workspace from process.cwd() to match the generated executable's semantics
instead of manifest.projectRoot; correct the README example to read
call.structuredContent (the McpToolInvocation field) rather than
call.result.
@ScriptedAlchemy
ScriptedAlchemy deleted the wave4/103-projection-harness branch September 3, 2026 05:29
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