✨ Run <Plan> with <TestAgent> in Markdown tests (#728) - #737
Conversation
) Plan authorship becomes a trusted host installation capability: the default agent, plus the operation that installs one Plan invocation's provider inside the ceiling. Everything else about that ceiling — the permission mode, the prompt-failure policy, the capability refusals and the session directory — stays where it was, identical for every provider, so a second implementation cannot bring a weaker one with it. The production ACPX adapter is one concrete provider of that capability, built from the Agent stack a run settled, unchanged. A nested `<Execution host="run">` that declares a canonical `<TestAgent>` is the second: it installs the controlled provider it was already given, again, for the Plan invocation that asks, and gets an authorship root of its own that goes when the child settles. The failure this corrects was assembly order. The outer `xmd test` invocation built one Plan declaration closed over the absence of a stack and handed it to every child, so by the time a child's provider existed the declaration could no longer use it. Children now build their own declaration from the ceiling they settled; a child that settled none resolves the same protected bytes and is refused before a directory, a provider, a turn or a review exists. Claude-Session: https://claude.ai/code/session_01TNJwcFmnt3kYSn9gGsx9u7
… have one (#728) Two host cases for the boundaries the capability draws. A repository `TestAgent.md` ends the scan, so what an `<Execution>` prefix holds is an ordinary component invocation rather than a declaration this host recognizes. A child whose `<Plan>` found a ceiling anyway would mean the capability came from the name rather than from the definition ordinary resolution selected, so the child is held to the same refusal an unconfigured one gets. A direct `xmd test` root does not resolve `<Plan>` at all: the run profile's vocabulary is installed for a run, and the testing profile declares the Component to the production run children it launches and to nothing else. Either refusal would satisfy "a test root cannot write a Plan", so the case pins which one is delivered — a later change that gave the test root the declaration, and therefore a ceiling to be refused at, is one somebody has to make deliberately. Claude-Session: https://claude.ai/code/session_01TNJwcFmnt3kYSn9gGsx9u7
The declared `<Plan>` section of the executable MDX specification, the authorship-profile entry in the architecture inventory, and the deterministic Agent configuration section of the testing specification each described one way to establish the Plan ceiling, because there was one. They now describe three surfaces and keep them distinct: a production run states the ACPX ceiling from the Agent stack it settled; a nested run child declaring a canonical `<TestAgent>` states the deterministic one from the controlled provider that declaration produced; everything else states none and carries the sentence a person reads instead. What the ceiling does once stated is the same for both — the permission mode, the prompt-failure policy, the capability refusals and the empty session directory are installed in one place rather than restated by each provider, so a second implementation cannot bring a weaker ceiling with it. `specs/test-agent-spec.md` is deliberately untouched: what it says about session matching is the open question this story is blocked on, and writing it now would mean rewriting it after. Claude-Session: https://claude.ai/code/session_01TNJwcFmnt3kYSn9gGsx9u7
`<Plan>` derives the session it opens from the expansion that asked, so a checked-in test has no name to write: the conversation is `xmd-plan:` followed by a digest that moves when the document moves. A scenario maps one exact agent and session, and an omitted session maps the unnamed default rather than acting as a wildcard — so nothing could address the Plan's turn, and the story's successful path was unreachable. `anySession` is an explicit opt-in for exactly that: a scenario that answers for any of its agent's sessions no exact mapping claims. An exact mapping always wins, so every guarantee about named sessions still holds wherever one is written; an agent declares at most one; and writing both `session` and `anySession` on one scenario is refused where it is written rather than when a prompt arrives. Chosen because it is additive. It invalidates no existing declaration and changes neither production Plan session identity nor what an omitted `session` means — the two alternatives both edit a settled guarantee. PMT1 and PMT2 now run as checked-in Markdown rows: a document captures its approved Plan through a scripted agent and an authored approval, and the file the approved source names is absent afterwards. That negative control was verified by planting the file, which fails the row. Claude-Session: https://claude.ai/code/session_01TNJwcFmnt3kYSn9gGsx9u7
The Plan ceiling installs its review provider inside the invocation, which is nearer than anything installed around the child. A configured child therefore got the browser form in front of its own `<Answers>` matchers, and the review waited for a person no test can supply: the run printed a form URL and hung until the harness abandoned it. Who answers now travels with the child's declaration request. An ordinary run states the browser form, because that is how a person reviews a Plan. A configured child states nothing and lets what it already has answer — the matcher provider when the test declared one, and the form otherwise, which is what an unconfigured child had all along. PMT4 and PMT5 follow: two sibling children, one approving and one stopping, each writing a Plan under a root of its own, with neither root surviving and neither reaching the tree a production `xmd plan` owns. Both endings are in one case because cleanup that only ran on the happy path would satisfy a case that checked one of them. Claude-Session: https://claude.ai/code/session_01TNJwcFmnt3kYSn9gGsx9u7
…728) `installChildTestAgent()` now answers with the components *and* the operation that installs a Plan provider, so the whole value is no longer what `installAgentComponents()` takes. Two call sites in the package's own tests still passed it whole, which left `deno task check` red. Only the calls move; both cases assert exactly what they asserted before. Claude-Session: https://claude.ai/code/session_01TNJwcFmnt3kYSn9gGsx9u7
| // absence of one before that child existed. Each caller supplies the ceiling | ||
| // it settled, the authorship root it owns and the scope its host acts run in; | ||
| // everything else about the Component is this entrypoint's and identical for | ||
| // all of them. |
There was a problem hiding this comment.
Redundant comment — restates what the code does.
| // all of them. |
| : { authorshipRoot: mode.planAuthorshipRoot }), | ||
| // Captured before the document exists, so the two acts that are this | ||
| // host's — putting this build's adapter on disk, and opening the review | ||
| // form — run outside the ceiling the Component installs around itself. |
There was a problem hiding this comment.
Redundant comment — restates what the code does.
| // form — run outside the ceiling the Component installs around itself. |
| : { observeAuthorship: request.observeAuthorship }), | ||
| installElicitation: request.installElicitation, | ||
| // Rendered when a `<Plan>` first asks, not before: an ordinary run that | ||
| // writes none never builds a catalog it has no reader for. |
There was a problem hiding this comment.
Redundant comment — restates what the code does.
| // writes none never builds a catalog it has no reader for. |
| ceiling: planAuthorshipCeiling(mode.agent), | ||
| host: yield* useScope(), | ||
| // This command's own root: the browser form is how a person reviews a Plan | ||
| // written by an ordinary run. |
There was a problem hiding this comment.
Redundant comment — restates what the code does.
| // written by an ordinary run. |
| // ceiling the Component installs refuses to everything inside it. | ||
| const host = yield* useScope(); | ||
| // The one ceiling this invocation can establish, settled before the | ||
| // declaration exists so nothing the document does can reach or replace it. |
There was a problem hiding this comment.
Redundant comment — restates what the code does.
| // declaration exists so nothing the document does can reach or replace it. |
| if (root === undefined || defaultAgent === undefined) { | ||
| // Not reachable from `installChildTestAgent`, which states both. A child | ||
| // that somehow reached here has no provider to put under the ceiling, and | ||
| // saying so is the honest answer rather than establishing one anyway. |
There was a problem hiding this comment.
Redundant comment — restates what the code does.
| // saying so is the honest answer rather than establishing one anyway. |
| const installations: ExecutionInstallation[] = []; | ||
| // What this child can establish for a `<Plan>` written inside it. A child | ||
| // nobody configured establishes nothing, which is the refusal `<Plan>` has | ||
| // always given where no coding-agent ceiling exists. |
There was a problem hiding this comment.
Redundant comment — restates what the code does.
| // always given where no coding-agent ceiling exists. |
| // Created out here, outside the ceiling that refuses a directory to | ||
| // everything inside it, and owned by this child alone: the Plan invocation | ||
| // still makes and proves its own empty session directory underneath it, and | ||
| // the whole tree goes when this child settles however it settles. |
There was a problem hiding this comment.
Redundant comment — restates what the code does.
| // the whole tree goes when this child settles however it settles. |
| installations.push({ declarations: [settings.plan] }); | ||
| // could not resolve `<Plan>` would be a different one. Built here, from what | ||
| // this child settled above, rather than taken from a declaration the | ||
| // entrypoint built before this child's configuration had been read. |
There was a problem hiding this comment.
Redundant comment — restates what the code does.
| // entrypoint built before this child's configuration had been read. |
| // configuration has been read: a configured `<TestAgent>` child gets the | ||
| // controlled one, and every other child gets none and is refused at the | ||
| // ceiling — which is what a host with no coding agent should say, rather | ||
| // than that the component does not exist. |
There was a problem hiding this comment.
Redundant comment — restates what the code does.
| // than that the component does not exist. |
PR #737: ✨ Run
|
| Symbol | Declared at | Refs in diff | Why flagged |
|---|---|---|---|
AuthorshipFrame |
packages/cli/src/authorship-profile.ts:271 |
1 | referenced ≤1× within the added diff (pre-existing usages not counted) |
Oxlint structural signals:
no-unused-vars×7: packages/cli/src/authorship-profile.ts, packages/test-agent/src/components.ts, packages/cli/src/cli.tsno-empty-function×3: packages/cli/src/testing-host.ts, packages/test-agent/src/components.ts, packages/cli/src/cli.ts
Slop
-
packages/cli/src/testing-host.ts:284—// always given where no Agent context exists. -
packages/cli/tests/support/run-markdown-tier.ts:94—// not exist. -
packages/test-agent/src/child-configuration.ts:313—// describe an arrangement other than the one installed. -
packages/test-agent/src/child-configuration.ts:327—// what it actually is rather than as what was asked for. -
packages/test-agent/src/provider.ts:167—// a provider built from anything else would be reported by nothing. -
packages/test-agent/src/provider.ts:174—// advertises the same names for each. -
packages/test-agent/src/provider.ts:177—// except the one name reserved for the client-allocated contract. -
packages/test-agent/src/provider.ts:196—// route already sees what this one established.
Oxlint slop signals:
no-console×3: packages/cli/src/cli.ts
Static Analysis
Oxlint: 38 diagnostics across 9 files (9 rules)
Density: 0.028 violations/added-line
unbound-method (13): packages/cli/tests/support/plan-harness.ts, packages/cli/tests/support/run-markdown-tier.ts, packages/cli/src/plan-component.ts (+3)
no-unused-vars (7): packages/cli/src/authorship-profile.ts, packages/test-agent/src/components.ts, packages/cli/src/cli.ts
no-unsafe-type-assertion (4): packages/cli/src/plan.ts, packages/cli/src/plan-component.ts, packages/cli/src/cli.ts
no-empty-function (3): packages/cli/src/testing-host.ts, packages/test-agent/src/components.ts, packages/cli/src/cli.ts
no-console (3): packages/cli/src/cli.ts
no-base-to-string (3): packages/cli/src/plan-component.ts
consistent-function-scoping (2): packages/cli/src/authorship-profile.ts, packages/test-agent/src/provider.ts
no-floating-promises (2): packages/test-agent/src/components.ts, packages/cli/src/cli.ts
consistent-return (1): packages/cli/src/cli.ts
Correctness
No extraneous code patterns detected.
…as policy (#728) Availability and restrictions were one word doing two jobs. They are now two. Whether a host can give a Plan an Agent is `Result<PlanAuthorship>`: `Ok` with the capability, or `Err` with the sentence a person reads. The bespoke `{ established, authorship | refusal }` protocol is gone, and provider installation remains impossible on `Err` because there is no capability to call. What a Plan then runs under stays where it was — `installAuthorshipFrame()` owns the fixed policy, identical whoever supplies the Agent. The observation a trusted host receives is now read from what the adapter assembled and the frame installed, after the last install, rather than from the policy either was handed. A report taken from the input agreed with the policy however the adapter assembled its dependencies, so it could not tell an assembly that honored the policy from one that dropped it: both adapters now build their provider dependencies once and describe that object, the provider is the name that actually routes a turn, and the prompt-failure policy is reported by the call that installs it. Changing the system instruction, the permission mode, the prompt-failure policy, the MCP servers, the native tools or the provider fails PMT4 — verified by changing the assembled tool set, which fails it. The diagnostics are the approved sentences, and the negative assertions read them whole rather than by fragment, at the same moment they were read before. `TEST_AGENT_PROVIDER` is unexported again; Plan routing stays private. Claude-Session: https://claude.ai/code/session_01TNJwcFmnt3kYSn9gGsx9u7
…eiling words (#728) The observation was built beside the installation rather than being it. The controlled adapter registered its provider and invocation settings and then constructed a separate record from the requested policy; the frame installed prompt-failure middleware and then reported the literal "fail". Either could stay green while the effective installation moved. There is now one assembly — the provider identity, the dependencies, and the invocation options — and it is what registers the provider, what installs the invocation, and what a trusted host receives. The dependencies it carries are the partition's own account of what its provider holds, so a provider built from anything but the assembled Plan dependencies reports as what it actually is. Nothing is reconstructed afterward. The prompt-failure rule is proven by a turn instead of a value: a scripted turn emits part of a candidate and then fails, and authorship ends before that partial can be checked or reviewed. A value saying the policy is installed would say so however the middleware behaved. Four mutations confirm the evidence — disconnecting the provider from its assembled dependencies, changing the effective permission mode, changing the registered identity, and bypassing the prompt-failure handler — each failing its case, all restored. The cancellation case keeps its `halt()` and its root-cleanup assertion, and its comment no longer claims a Prompt is in flight: the observer runs once the frame is installed and before the Component's content starts. Plan-specific "ceiling" is gone from the words this story introduced — `planConfiguration` for the assembled dependencies, Agent context for whether a host supplies one, authorship policy or frame for the fixed restrictions. Established uses elsewhere are untouched. Claude-Session: https://claude.ai/code/session_01TNJwcFmnt3kYSn9gGsx9u7
| const installations: ExecutionInstallation[] = []; | ||
| // What this child can establish for a `<Plan>` written inside it. A child | ||
| // nobody configured establishes nothing, which is the refusal `<Plan>` has | ||
| // always given where no Agent context exists. |
There was a problem hiding this comment.
Redundant comment — restates what the code does.
| // always given where no Agent context exists. |
| // has been read: a configured `<TestAgent>` child gets the controlled one, | ||
| // and every other child gets none and is refused — which is what a host | ||
| // with no coding agent should say, rather than that the component does | ||
| // not exist. |
There was a problem hiding this comment.
Redundant comment — restates what the code does.
| // not exist. |
| } | ||
| // One assembly, used for every installation and handed back as the | ||
| // observation. Nothing is reconstructed afterward, so a report cannot | ||
| // describe an arrangement other than the one installed. |
There was a problem hiding this comment.
Redundant comment — restates what the code does.
| // describe an arrangement other than the one installed. |
| provider: TEST_AGENT_PROVIDER, | ||
| // The partition's own account of what its provider holds, so a provider | ||
| // built from anything but the assembled Plan dependencies reports as | ||
| // what it actually is rather than as what was asked for. |
There was a problem hiding this comment.
Redundant comment — restates what the code does.
| // what it actually is rather than as what was asked for. |
| return yield* useAcpxProvider( | ||
| // Assembled once and then both used and returned. The provider is created | ||
| // from this exact object, so a trusted host reporting it reports what runs — | ||
| // a provider built from anything else would be reported by nothing. |
There was a problem hiding this comment.
Redundant comment — restates what the code does.
| // a provider built from anything else would be reported by nothing. |
| advertiseNativeLaunch: options.agents, | ||
| // Both gates, stated separately, because they are separate capabilities. | ||
| // This partition proves them the same way — deterministically — so it | ||
| // advertises the same names for each. |
There was a problem hiding this comment.
Redundant comment — restates what the code does.
| // advertises the same names for each. |
| // advertises the same names for each. | ||
| advertiseClientNativeAttachment: options.agents, | ||
| // Every agent this partition serves gets the provider-returned adapter, | ||
| // except the one name reserved for the client-allocated contract. |
There was a problem hiding this comment.
Redundant comment — restates what the code does.
| // except the one name reserved for the client-allocated contract. |
| try { | ||
| const value = yield* op(); | ||
| // Reported before the slot advances, so the next operation to pin a | ||
| // route already sees what this one established. |
There was a problem hiding this comment.
Redundant comment — restates what the code does.
| // route already sees what this one established. |
Why
A Markdown test could not run a document that writes
<Plan>.<Execution host="run">already accepted<TestAgent>and<Answers>, and the childalready received the controlled provider — but its
<Plan>declaration had beenbuilt by the outer
xmd testinvocation before that child's configuration wasread, so it closed over the absence of an Agent stack and
<PlanAuthorship>refused for want of an Agent context.
The result was that the author-facing Plan workflow had no checked-in Markdown
evidence at all. TypeScript could drive it through a private harness; a document
could not.
Closes #728.
What changes
Before:
The child was refused:
No Agent context was found. No Plan was returned..After, the same child writes a Plan with a scripted agent and an authored
approval, reaching no live coding agent and no browser:
An
xmd testroot and an unconfigured child are unchanged: neither gains Planauthorship authority.
How it works
Plan authorship becomes a trusted host installation capability: the default
agent, plus the operation that installs one invocation's provider under the
authorship policy. The production ACPX adapter is one implementation of it; a configured
<TestAgent>child is the second. The fixed policy — the deny-all permissionmode, the prompt-failure policy, the document-capability refusals, the empty
session directory — stays in
installAuthorshipFrame(), identical for both, soa second provider cannot bring a weaker one with it.
TestingHostSettings.plan(a prebuilt declaration) becomesplanDeclaration(request), a factory each child calls after reading its ownconfiguration. That is the fix.
Review guide
Start with:
packages/cli/src/authorship-profile.ts—PlanAuthorship,Result<PlanAuthorship>andplanAgentContext().Then review:
packages/cli/src/testing-host.ts—ChildPlanDeclaration,controlledAgentContext()anduseChildAuthorshipRoot().packages/cli/src/plan-component.tsandpackages/cli/src/cli.ts— theassembly's
authorship policy, and the declaration factory.packages/test-agent/src/child-configuration.ts—ChildTestAgentInstallationand
installPlanProvider(), and how the authored session name reaches it.packages/cli/tests/document-suites/plan/— the author-facing rows.Look carefully at: the installed-provider assembly. One value registers the
provider, installs the invocation options and is handed to the trusted host as
its observation, so a report cannot describe an arrangement other than the one
installed. The prompt-failure rule is proven by a turn that fails after emitting
part of a candidate, not by a value saying the policy is installed.
And at: who answers a configured child's Plan review. The frame
installs its review provider inside the invocation, nearer than anything around
the child, so installing the browser form there put it in front of the child's
own
<Answers>and the review waited for a person no test can supply. Whoanswers now travels with the child's declaration request.
What must stay true
recognition against the definition ordinary resolution selected, and checked
by PMT6, where a repository
TestAgent.mdconfigures nothing.planAgentContext()being the same ACPX construction it was, and checked by the whole preserved
plan-component.test.tstier.PMT7.
registered for removal before it is created, and checked by PMT5 across an
approved, a stopped and a cancelled ending.
document wrote travels to the authorship policy, so a test names the conversation
without production's derived placement changing.
How to verify it
browser, and fails if the child has no Agent context.
planned.txt, which fails the row — the absence is a real negative control.rather than told the component does not exist.
the provider, installs the invocation options and is what the trusted host
observes, so the system instruction, the deny-all invocation permission mode,
the strict native-permission handling, the empty MCP-server and native-tool
sets and the controlled-provider identity are read from what is installed
rather than from what was requested. It also proves the failed-turn behavior:
a scripted turn that emits part of a candidate and then fails ends authorship
before that partial can be checked or reviewed.
sibling children each write under a root of their own, neither root survives
an approved, a stopped or a cancelled ending, and the tree a production
xmd planowns gains nothing.TestAgentsupplies no Agent context.deno task test \ packages/cli/tests/document-suites/plan/plan-markdown.test.ts \ packages/cli/tests/testing-execution-host.test.ts \ packages/cli/tests/plan-component.test.tsok | 4 passed (37 steps) | 0 failed(50s).deno task test \ packages/test-agent/tests/components.test.ts \ packages/test-agent/tests/smoke.test.tsok | 3 passed (19 steps) | 0 failed(1m4s).deno task checkanddeno task lintare clean.Scope
Included
<Plan>declaration and a child-owned authorship root.the conversation
<Plan>opens.Intentionally unchanged
<Plan>'s current value behavior.#722owns text emission and capture, andis open until this story lands.
xmd testroot, which does not resolve<Plan>at all.New abstractions
PlanAuthorship, answered asResult<PlanAuthorship>, exists because "who may give a Plan an Agent" had been read off anAgentStackat a moment before achild's configuration existed. Consumers:
xmd run,xmd plan, and thetesting host.
ChildTestAgentInstallationexists because a configured child needs both theordinary Agent components and a way to install a provider under the authorship
policy, and those are installed at different moments in different scopes.
New dependencies
None.
Notes for the reviewer
The
xmd testroot's behavior is pinned, not changed.cli.tswithholds thedeclaration there, while a comment a hundred lines earlier says the opposite.
PMT7 pins what is delivered so the contradiction cannot be resolved silently; the
prose is left alone as out of scope.
Two existing test call sites moved, and nothing they assert did.
installChildTestAgent()now answers with the components and the operationthat installs a Plan provider, so the whole value is no longer what
installAgentComponents()takes.packages/test-agent/tests/{components,smoke}.test.tsstill passed it whole, which left
deno task checkred.#722is open. It resumes only after#728is delivered, and this PR targetsthe current value-mode
<Plan>accordingly.