Document adversarial implementation workflow WIP - #181
Conversation
PR #181: Document adversarial implementation workflow WIP26 files, +5042 / -0 Scope🔴 PR has 5042 lines changed. Split into focused PRs. 🟡 5042 lines changed. PRs under 400 receive more thorough review. 🟡 26 files changed. Are all changes related? Structural✅ No structural bloat detected. Slop✅ Slop indicators look low. Static Analysis✅ Oxlint found no issues. CorrectnessNo extraneous code patterns detected. |
End-goal implementation audit — 2026-08-02Reviewed PR head The target remains coherent: the user owns material decisions; planner and implementor converge through evidence; workflow state and environmental effects are explicit; agents do not own Git, GitHub, or workspace effects; and every live resource is scope-owned. Settled contracts to fold into the target documentsThese are documentation updates, not missing product features.
Shipped foundation available to the end goal
The document-level logic in Missing feature checklist for the complete workflowWorkflow identity, artifacts, and resumption
Stage execution and user continuation
Workspace ownership
Enforceable sandbox and permissions
Deterministic Git and GitHub effects
End-to-end workflow contract
Not blockers for this workflow
This checklist should shrink as features land. A settled contract updates the target documents and moves to the shipped foundation; only capabilities absent from the runtime remain unchecked. |
Current delivery mapThe root document is the workflow. A durable workflow run may continue through multiple document executions. Each issue remains an independently reviewable stop point. Living target and shipped document logic
Workflow identity and environment foundations
#199 and #227 can proceed independently. #218 is designed and stacks on PR #341 until #289 merges. Persistence and workspace composition
Workspace state is retained across completion, failure, cancellation, and interruption; scope teardown releases only ephemeral provider attachments. Point-in-time snapshots, rollback, and training export are not prerequisites for normal resumption. Deterministic Git and GitHub effects
Authority, composition, and certification
Dependency spine#298 is superseded by #291, which owns durable lookup, Implementors validate authorized slices. Taras settles #227, #290 exhaustion, and #302's term and installation architecture before those stories are delegated. #218's product decisions are recorded in the issue; its implementor validates that design rather than choosing a replay or provider model. |
Parallel delivery mapCurrent at PR head Parallel wavesShared-code collision rulesMaximum useful initial concurrency is three implementation lanes ( |
8d90204 to
503b114
Compare
Milestone story mapCreated the Adversarial implementation workflow milestone. Every task in the approved delivery sequence now has a canonical story:
PR #181 itself is also attached to the milestone as the living end-goal artifact. Each new story records scope, observable acceptance criteria, dependencies, and its sequence position. Existing stories #199, #203, #218, and #227 remain canonical rather than being duplicated. |
1a01e58 to
014447b
Compare
Fresh head on current main — #292Replaced the stale head The markup did not runEvery stage passed props through expression props as This is #305, still open — "Expression-valued component props can read The error model reaches the stagesA stage component is split by its
One claim I did not repeat: Stale claims removed
VocabularyCollapsed onto the concepts #289, #291 and #298 authorize — artifact ledger, artifact version, run identity, pinned source revision, stop reason, terminal record, stage boundary, declared inputs, cross-process continuation — replacing the four names these files used for a ledger and the three for a run. Engine state is described as scoped to the operation that owns it, with no module-scoped registry. Missing capabilities cite the issue that supplies them (#289, #291, #293, #294, #295, #296, #297, #298, #300, #302) rather than saying only "not implemented". ClassificationShipped: Evidence
Gates: Outstanding before merge#203 is still open and is an input to this target, not something this PR closes. Before merge this branch rebases onto the |
014447b to
68733d7
Compare
Rebased onto
|
User authority now gates execution — head
|
| Scenario | Agent turns | proceed |
Stages that ran |
|---|---|---|---|
| Material choice, user declines | 1 | false |
none — both bindings stayed unresolved |
| Material choice, user authorizes | 2 | true |
PLANNING-RAN, IMPLEMENTATION-RAN |
| No material choice | 2 | true (explicit <Parse> branch) |
PLANNING-RAN, IMPLEMENTATION-RAN |
The declining path records no subsequent agent turn. In the third scenario a declining <Answer> was in scope and never consulted, because <Elicit> never ran — the continuation came from the explicit branch. All four <Output> outcome branches were exercised separately and each renders its own outcome with no unbound-binding error, including rejected-at-acceptance.
5. Verification
Probes rerun: 9 frontmatters parse (UserCheckpoint now returnMode=value, the rest text), 5 embedded schemas compile, 21 shipped + 5 repository names resolve, 9 missing names unresolved. Vocabulary sweep clean.
Local gates: fmt, lint (0 errors), check, test (349 passed, 0 failed), check:jsr, tsc — all green.
All ten PR checks green: composability, deploy, jsr, lint, review, site, smoke, test-bun, test-deno, test-node.
Authority now survives the component boundary — head
|
| Scenario | Agent turns | commit | PR | issues | Result |
|---|---|---|---|---|---|
| Plan review declined | 3 | 0 | 0 | 0 | terminal=declined, authorized=false; no authorization question, no implementation turn |
| All approved, verdicts pass | 8 | 1 | 1 | 1 | accepted |
| PR review declined | 7 | 1 | 1 | 0 | implementation.terminal=declined; no acceptance question |
| Failing verdict, approved | 12 | 1 | 1 | 1 | revision turn runs; reaches implementation only after the later passing verdict |
| Exhaustion | 20 | 0 | 0 | 0 | terminal=exhausted, authorized=false; reaches neither |
Mutations, each changing an observed result:
| Mutation | Effect |
|---|---|
<Issue> moved ahead of its approval |
declined PR review goes issues 0 → 1 — the issue is created despite the decline |
Caller gate bypassed (planning.authorized → true) |
declined plan review goes prompts 3 → 6 — advances to the authorization question |
Planning's return reduced to text |
authorized / terminal / verdictPassed render as unresolved literals; the caller cannot gate at all |
Correction to my previous report
I described the earlier probe as discriminating the gating. It stubbed the outer stages, so it never exercised any internal-decision path — the harness was built not to see exactly what you found. The scenarios above run the real documents.
Verification
Probes rerun: 9 frontmatters parse (UserCheckpoint, Planning, Implementation now returnMode=value), 5 embedded schemas compile, 21 shipped + 5 repository names resolve, 9 missing names unresolved. Vocabulary sweep clean. #328's missing-capability classification kept, and #327's.
Local gates: fmt, lint (0 errors), check, test (349 passed, 0 failed), check:jsr, tsc.
Docs updated for the new boundaries: start.md (flow, gate section, data-flow table), artifacts.md (StageResult added; the three-kind split now says only parsed results can gate), the specification, and the PR description.
All ten checks green: composability, deploy, jsr, lint, review, site, smoke, test-bun, test-deno, test-node.
Sources returned, derived state removed — head
|
| Scenario | Agent turns | commit | PR | issues | Result |
|---|---|---|---|---|---|
| Plan review declined | 3 | 0 | 0 | 0 | no authorization question, no implementation turn |
| All approved | 8 | 1 | 1 | 1 | accepted |
| PR review declined | 7 | 1 | 1 | 0 | no deferred issue, no acceptance question |
| Failing verdict, approved | 12 | 1 | 1 | 1 | revision turn; reaches implementation only after the later passing verdict |
| Planning exhaustion | 20 | 0 | 0 | 0 | proceed=true passed=false; reaches neither |
| PR-review exhaustion | 24 | 5 | 5 | 5 | proceed=true passed=false; acceptance never reached |
Mutations, each changing an observed result:
| Mutation | Effect |
|---|---|
Gate on a duplicated authorized field |
declined plan review advances, prompts 3 → 6 |
Declare the handle as string |
Return validation failed — after commit and issue effects have run |
<Issue> moved ahead of its approval |
declined PR review goes issues 0 → 1 |
Verification
Probes rerun: 9 frontmatters parse, 5 embedded schemas compile, 21 shipped + 5 repository names resolve, 9 missing unresolved. Sweep clean, including StageResult. #327 and #328 classifications retained.
Local gates: fmt, lint (0 errors), check, test (349 passed, 0 failed), check:jsr, tsc. Docs updated: start.md, artifacts.md, the specification, the data-flow table, and the PR description.
All ten checks green: composability, deploy, jsr, lint, review, site, smoke, test-bun, test-deno, test-node.
Rebased onto 511776e. #326 closed #203 by proving the two projection lifetimes with real modifiers — L8 for a projected `persist` resource and Q14 for a projected daemon — which were the acceptance criteria this target had the weakest evidence for. Projected content keeping its caller's bindings while its live effects belong to the component invocation is now stated as shipped, along with why it matters: a process a stage starts stops before the workspace it ran in is removed, which is the ordering `<Worktree>` (#293) is built on. `architecture.md` is authoritative on the error model, so the `output` row reads "fails the run; `<PrintErrors>` can print instead" as the registry states it. The engine does not do that yet — an outer boundary ends the run instead, whether the failure arose in the region itself or in content projected into it — so the discrepancy is classified as missing and filed as #327 rather than allowed to redefine the settled contract. No stage writes `<PrintErrors>`, so nothing here depends on it. The distinction the target does keep is the one the registry makes: a `throw` decision is the mode a boundary cannot replace. Every stage's parsing sits in documentation, so the final `<Parse>` is a gate for that reason and not because printing is unavailable.
…entory `<UserCheckpoint>` captured `elicitation.proceed` and then returned only rendered prose, so a caller had nothing to branch on. `start.md` invoked `Planning` and `Implementation` unconditionally and the two stages branched on the planner verdict alone: a user answering `proceed: false` could not stop anything. That contradicts the central user-authority contract and #290's acceptance. `<UserCheckpoint>` now declares `returns` and binds a schema-validated decision — `proceed` plus the assessment fields a later prompt quotes. One `decisionSchema` serves both paths: `<Elicit>` binds it when the assessment reports a material choice, and an explicit `<Parse>` binds `"proceed": true` with its reason when there is none, so continuation is represented rather than inferred from absent elicitation data. Every affected transition is gated — a declined handoff does not start Planning, a declined authorization does not start Implementation, a declined review neither revises nor accepts, and a declined acceptance finishes as rejected rather than falling into the accepted branch. Nesting is what the language supports; it is not stopping. The run still expands to `<Output>` and completes, with no clean halt at a stage boundary and no stop reason recorded for one. That is classified as missing under #298 over #289 rather than described as enforcement. Two engine limitations surfaced while proving this and are filed rather than worked around silently. `<Content />` is substituted only at a body's top level or directly inside `<Output>`, never nested inside a `<Prompt>` (#328) — `Discovery` and `UserCheckpoint` used exactly that shape and could not expand, so both now take the material as a prop. And a binding made inside a Markdown component's projected content does not escape to its caller, while the same binding inside a registered function component does; the harness had to account for that to test the real components. artifacts.md claimed every listed result was JSON declared inline. It is corrected into three kinds: `PlannerHandoff` and `ImplementationPlan` are prose with no schema and cannot gate anything; the four parsed results are described using only fields their inline schemas declare; and `UserDecision` is the checkpoint's declared return, with the actor and timestamp that would make it auditable marked missing under #291. Discrimination over the shipped subset, with a turn-recording stub agent: a declined checkpoint records 1 agent turn and neither stage runs; the authorized path records 2 and both run; a no-material-choice assessment records 2 and proceeds on its explicit `<Parse>` branch while a declining answer in scope is never consulted.
`Planning` and `Implementation` consumed their internal checkpoint decisions and returned only prose, so authority leaked at the component boundary. A declined plan review broke the planning loop, but `start.md` still asked the authorization question and could reach `Implementation`; a declined pull-request review still let the change be reported accepted. `Implementation` also created deferred `<Issue>` effects before its review checkpoint ran, so the planner's proposed disposition became a durable GitHub object before the user approved it. The earlier probe stubbed the outer stages, so it never exercised those paths. Both stages are now value components. Each returns its prose, the parsed verdict's fields, the complete `UserDecision` it resolved, and two derived control fields the caller gates on: `authorized`, which is `proceed && verdict.passed`, and `terminal`, which distinguishes `converged` from `declined` and `exhausted`. A stage cannot approve its own advancement, and an exhausted loop reports itself distinctly and fails closed — which is not a decision about what exhaustion should do, still unresolved under #290. `start.md` gates on those returns: authorization is requested only after a passing plan verdict and an approving plan-review decision, acceptance only after a passing pull-request verdict and an approving review decision. It renders the human-readable reports from the returned fields. Deferred `<Issue>` creation moved inside the approved branch, after the checkpoint. `proceed: true` authorizes the exact proposed transition and effects; the free-text response records reasoning and never amends effects, because an effect that has already executed cannot be amended by prose. `proceed: false` performs none of them. Discrimination against the real `Planning`, `Implementation`, and `UserCheckpoint`, stubbing only the agent transport and the three deterministic components that do not exist. A declined plan review: 3 agent turns, no authorization question, no implementation turn, no effects. A declined pull-request review: no deferred issue, no acceptance question. A failing verdict with approval performs the revision turn and reaches the next stage only after a later passing verdict. Exhaustion reaches neither. Moving `<Issue>` ahead of approval creates the issue despite a decline; bypassing a caller gate advances past a declined review; reducing either return to text leaves the gate fields unresolved. Each mutation changes an observed result.
Two contract problems in the replacement for the authority leak.
`Implementation` declared `pullRequest: { type: string }` and returned the
raw binding. #295 specifies a structured handle — number, URL, head and
base identities, state, reviews, comments, checks — so a conforming
`<PullRequest>` would perform its durable effects and only then fail this
component's return validation. The previous harness stubbed the handle as
a string, which is exactly why the probe stayed green. `start.md` consumes
none of it and the artifact ledger records the effect and handle
independently (#291), so the field is gone and `report` renders the named
fields a reader needs. If a later caller needs the handle it gets #295's
object schema, never a placeholder.
Both stages also returned `authorized` and `terminal`, duplicating
`decision.proceed && verdictPassed` and a second reading of the same two
fields. A return schema can require both sources to be present but cannot
require a derived flag to agree with them, so a record pairing
`proceed: false` with `authorized: true` validated — and `authorized` read
confusingly next to the separate authorization checkpoint. Both are gone,
along with the `StageResult` name, which was coined without the permission
architecture.md's terminology registry requires; it is not being added
there.
`start.md` now computes each gate where it uses it, from the authoritative
pair. After a loop, `decision.proceed` false is a decline and
`decision.proceed` true with `verdictPassed` false is exhaustion; the gate
refuses both and the output tells them apart, while the terminal policy
stays unresolved under #290. Deferred `<Issue>` creation keeps its
corrected position after the checkpoint, inside the approved path.
Discrimination reruns against the real documents with effects stubbed at
their specified shapes. Declined plan review: 3 turns, no authorization
question, no effects. Declined pull-request review: no deferred issue, no
acceptance question. Fail-then-pass reaches implementation only after the
passing verdict. Planning exhaustion and pull-request-review exhaustion
each reach neither. Mutations: gating on a duplicated `authorized` field
advances a declined review (3 turns becomes 6); declaring the handle as a
string fails return validation after commit and issue effects have already
run; moving `<Issue>` ahead of approval creates it despite the decline.
Two inconsistencies left by the return-contract fix. The migration to value components was described in some places and not others. The specification still said `Planning` and `Implementation` return derived `authorized` and `terminal` fields, which they stopped doing. And the specification, `start.md`, and `primitives.md` all still described every component body as split by `<Output>` into documentation under `throw` and a region under `output`. That is true only of the text components: a value component declares `returns`, renders nothing, cannot contain `<Output>` at all, and runs its entire body fail-fast, binding nothing when it fails. Each document now says which kind it is talking about, and `Planning`, `Implementation`, and `UserCheckpoint` describe their own bodies as value bodies. The pull-request reviewer could not see what it was reviewing. #295 resolves reviews, comments, and checks alongside the identities, and the prompt rendered only number, state, URL, head, and base — while `runtime.md` denies agent network access, so the planner had no way to recover the rest. An adversarial review that cannot see a failing check or an existing objection is uninformed rather than adversarial. Every category is now rendered explicitly into the review prompt and into the checkpoint material the user reads, each collection iterated with `<Each>` rather than stringified, and the prompt names the revision under review: the diff at `headSha` against `baseSha`, with a verdict describing that head alone. The member field names inside reviews, comments, and checks are #295's to settle; these documents depend on that schema rather than defining a competing one. What is settled here is that the planner receives the complete snapshot. The handle stays internal — `start.md` gates on the verdict and decision — so the earlier claim that nothing consumes it was wrong, and the PR description is corrected too. Discrimination adds a structured `<PullRequest>` stub carrying non-empty reviews, comments, and checks with distinct markers. It captures the actual rendered planner prompt and the checkpoint material and asserts every category, both head identities, and the diff instruction reach them. Omitting any one category reddens exactly its own assertions and leaves the others green. Nothing in the test uses agent network access. The six gate scenarios and the three earlier mutations still hold.
…on text Three claims did not match what the documents do. `artifacts.md` still said nothing consumes the pull-request handle. The boundary is now stated exactly: `Implementation` consumes all of it internally, `start.md` never receives it — what crosses the stage boundary is the verdict and the decision — and the artifact ledger records the effect independently (#291). The specification said both text components do their work in documentation. Only `Discovery` does. `InstructionFiles` puts its `<Each>` and `<File>` reads inside `<Output>`, and probing that case corrected a second assumption in the same sentence: `<File>` prints its own failures, so an unreadable instruction file is decided as a printed error before the region's `output` mode sees it. The run continues; what stops the caller is the binding rule, since `as` refuses a body holding a printed error and `instructions` stays unbound. `start.md` gains the same qualifier, because "an undecided error fails the run" is only true of undecided ones. The user checkpoint did not carry what the PR claimed it did. The planner prompt rendered each review's body; `checkpointMaterial` rendered only the author, state, and head. A user approving a change has to read the original objection, not the planner's summary of it, so the body is now in both. The earlier probe missed this because it asserted the review *author* on the checkpoint material and the *body* only on the prompt — an assertion too weak to see the omission it was meant to cover. Discrimination now asserts the body on both surfaces. Dropping any category reddens both symmetrically, and dropping the body from the checkpoint alone reddens the checkpoint while the prompt stays green — the exact shape of the defect.
`checkpointMaterial` presented the pull request and the verdict but not the two things approval actually sets in motion. When a verdict fails, approval sends `verdict.revisionPrompt` to the implementor, and the checkpoint never showed it. A `defer` disposition sends the complete finding to `<Issue>`, including `finding.evidence`, and the checkpoint showed only the title, disposition, and description. That contradicts the document's own rule. `proceed: true` authorizes the exact transition and the exact effects presented for assessment, which means nothing if the material omits them: a user could approve revision instructions and issue evidence they had never read. `Planning` already showed its revision prompt; `Implementation` did not. Both are now in the material, unchanged and unsummarized — the literal `verdict.revisionPrompt`, and each finding's evidence rendered beneath it with a nested `<Each>`. The explanatory text in the component and the specification says why, rather than asserting that approval covers effects it never displayed. Discrimination gains distinct markers for both. The baseline asserts they reach the checkpoint, that the revision prompt the implementor receives carries the same value, and that `<Issue>` receives the same complete finding — the stub now records what it was passed. Removing either field from the checkpoint reddens exactly its own assertion while the effect assertions stay green, which is the shape of the leak: the effect still fires, the user just never saw it.
The target described a `<Workflow>`/`<Sandbox>`/`<Stage>` spine over a sidecar Git ledger at `refs/xmd/runs`, an implicit worktree, an implementor that edits files, and a `<PullRequest>` handle carrying reviews, comments and checks. None of that is the architecture any more, and three of those constructs were rejected rather than deferred. The command selects the environment: `xmd workflow start` creates the run and its one retained Workspace, and the root document is the workflow. Named `<Repository>` and `<Worktree>` compose the checkouts from ordinary root props, so nothing resolves through a hidden alias and no single worktree is implied. Agents are read-only under that host ceiling, so the implementor returns constrained XMD that `<Expand>` admits and expands as ordinary durable effects, and `<Git.Add>`, `<Git.Commit>`, an explicit `<Git.Push>` and `<PullRequest>` follow in that order. Waiting for the user is a durable suspension that `resume` continues, which is what `<Stage>` was reaching for. Retention is no longer speculative. #291 and #365 are shipped foundation: the run retains its filtered journal, Workspace roots, and metadata, and one expansion publishes its mutation, logical root and journal result in one transaction. `JournalProvenance` (#425) is what makes that history evidence rather than storage, and it is named where the documents discuss retained evidence. Every remaining capability is marked as target behavior against #218's dependency order. Two gaps are now stated rather than assumed away. #295 moved reviews, comments and checks out of the creation result, and no issue owns the forge read that returns them to a network-denied reviewer; and the journal records which run and expansion produced a decision but not which person answered. Props are namespaced throughout (#305), `<Content />` projects at any depth (#328), and the obsolete asymmetry notes are gone.
The living target still described `xmd workflow start` and `resume` as unbuilt and told the reader to wait for them. They exist: a run is created from committed Git bytes, streams its document in the foreground, retains its filtered journal and its commands' results, and resumes from the frontier. So does the retention beneath them, and namespaced props. Marking them shipped exposes what is still missing above them, and the target now says it: the run's Workspace holds no repository until named checkouts exist, and a pinned definition installs no component search path, so the stages beside it resolve to nothing under that command and are exercised under `xmd run`. The nine names that resolve to nothing are listed individually, each against the issue that owes it. Three settled contracts arrive with them. Routing, retention and failure are separate decisions about a command, and a bound block turns an exit status into data that no public modifier middleware can manufacture. Content a caller projects keeps its own region's error mode, so a component's declaration governs the component's work and never the caller's text — while the outer printing boundary that cannot print an `output` decision stays open and distinct. And canonical core alone brings an execution or an expansion into being: public middleware may inspect, narrow, refuse and delegate, and returns nothing anyone reads.
The previous commit reported that a workflow run passes no repository component search path, so the five stage components resolve to nothing under start and resume, without saying who owed the fix. Architecture has assigned it to #301, beside composing the supervised workflow those components belong to. The three documents that carry the observation now carry the assignment with it: #301 owes InstructionFiles, Discovery, UserCheckpoint, Planning and Implementation reachable under start and resume, from component code the trusted host authorized, accounted for by workflow-definition identity and retained-history admission, so neither a resume nor a replay substitutes what a mutable checkout holds now for the retained code. What is stated is the required outcome, not how to reach it. Ordinary xmd run resolution is unchanged and workflow execution gains no generic repository component search path; the mechanism is #301's to design and to take through architecture review.
The target still described the pre-#453 engine: `print` installed by the root, so a failure the document did not decide was printed into its own text and the run carried on. A text root now installs the fail-capable `output` mode for its whole body, and `<Output>` selects which regions render rather than deciding whether a failure counts. The consequence the reader most needs is the instruction files. A failed `<File>` read is still a component-owned printed error, but the stage cannot hand back a body holding one: `InstructionFiles as="instructions"` binds nothing, and the refusal is raised in the root's own body, where it is now the run's outcome. Discovery never starts and nothing after it does, so an unreadable AGENTS.md ends this workflow rather than being visible and survivable as the documents claimed. #327 stays as it was and stays distinct — an outer `<PrintErrors>` still does not print a failure a callee's `<Output>` region decided, which is a limitation of the printing boundary rather than of root settlement, and nothing here depends on it. #446's ownership rule is untouched: a caller's projected failure is still the caller's.
The flow's `<Output>` sat inside `<Repository>`/`<Worktree>`. Only a direct top-level `<Output>` is a declaration — caller content cannot introduce one for the document it is written in — so canonical structural preflight refused this document before `<Repository>` was ever resolved, and the entry document could not run even as far as its own first missing component. The declaration now opens before `<Repository>` and closes after it, with the composition, every stage invocation, every gate and the final report inside it. Nothing else about the flow moves. Wrapping the whole flow selects no more than it did: every element inside binds with `as`, and a binding keeps a private buffer, so a stage invocation contributes nothing to the document and the final-gate `<If>` tree remains the only thing that renders. The prose around the rendered gate, the checkpoint behaviour available today, and the root's error modes now say root declaration rather than describing a nested one as selecting or buffering the root.
The flow bound its worktree with a lexical `<Worktree as="worktree">` and expected that same invocation to render the stages inside it. Generic `as=` is an ordinary private capture, and #293 settled that Repository and Worktree get no exception to it — so that spelling would have captured the whole flow and left the root's `<Output>` with nothing to emit. The two existing forms compose instead: a self-closing `<Worktree … as>` creates or restores the retained checkout and binds its path, and a lexical `<Dir path={worktree}>` inside the same `<Repository>` makes that path cwd, renders the stages and the final report, and restores the enclosing cwd. Agent access is still `<Agent.AddDir>`; cwd never implies it. `<Dir>` is not built either, so the unresolved inventory is ten names rather than nine, and #293 owns the boundary this composition consumes. Reading a run back is no longer future work. `xmd workflow status`, `list` and `history` report immutable lifecycle snapshots — no lease, no replay, no Workspace, no append — so the documents say so, and say what history actually prints: an authored source position rather than a forkability reason it does not have. Suspension, executor ownership, cancellation and deletion stay #367; forks and forkability stay #368.
#290 settled the exhaustion behavior these documents still called an unresolved product decision. Exhausting the bounded attempts is neither convergence nor ordinary successful completion: it starts no implementation and no later durable effect, and it asks the user for direction rather than answering on the stage's own account. A composed workflow turns that request into a durable suspension, which is #367's and unbuilt. No return schema changes. The pair a stage already returns — `decision.proceed` true with `verdictPassed` false — is what makes exhaustion distinct, and nothing else produces it, so the rule against returning a second copy of a derivable answer still holds. What changes is that the report asks instead of stopping: the two exhaustion branches now await direction, while a decline still stops. The proof #290 asks for begins beside the stages it exercises. `Agent.AddDir` is #302's and does not exist, so a no-op stub sits in the test directory alone — repository resolution is scope-local, so every name the inventory calls unresolved still resolves to nothing from the workflow directory, which the component probe confirms. One of the seven acceptance criteria is proven so far: the exact instruction paths and contents reach the planner prompt, constrained literally in the scenario so a dropped or summarized instruction fails rather than matching.
Six of #290's seven criteria now execute. `InstructionFiles` reaches Discovery's prompt and both of Planning's with the instruction paths and contents constrained literally, so a prompt that dropped or summarized them fails. A passing verdict returns true/true and sends no revision. A failing verdict sends its exact review, revision prompt and complete checkpoint record back to the implementor's own session, as that session's second turn and before the revised plan. Both bounded repair loops stop where the documents say: an initial malformed reply and two malformed corrections reach the final `<Parse>`, which is what the assertion pins — the third reply is the one quoted in the failure, so no third correction was sent. A material choice reaches the checkpoint, takes the `<Elicit>` path, and an explicit decline stays `proceed: false`; no material choice returns the documented continue record word for word. The seventh is not proven, and the document says so where the coverage would be rather than only here. Exercising all five failing iterations costs twenty agent turns in one invocation — as many as every other test combined, which take thirty-one seconds across seven timeboxes. A test's timeout is a fixed twenty seconds with no prop that raises it. The scenarios for the run are written and correct; what is missing is a way to run them. Lowering `max` would prove a different document than the one the workflow ships.
#290's last criterion needs five failing iterations of `Planning` in one invocation: five plan prompts, five verdicts, five checkpoint assessments and the five revisions a failing round sends. Twenty ACP round trips do not fit a test's fixed twenty-second timeout, and lowering `max` would prove a different document than the one the workflow ships. So the run moves to an integration test that keeps the document and replaces the transport. The real `Planning` resolves from the workflow directory on the same search path `xmd test` derives, under one execution and one contextual working directory, against a synchronous stub root Agent provider installed through the public `rootProvider` seam. It finishes in about half a second. What it proves is the ordering as much as the outcome: exactly twenty calls, five of each turn in the order the document writes them, each routed to the session the document sends it to, every verdict validly failing and every checkpoint explicitly continuing, and each round's review, revision prompt and assessment carried into that round's revision. The returned pair is uniquely exhausted, and the plan, review and revision prompt are round five's. The root writes `start.md`'s own gate with a later effect behind it. That prompt never runs, which is what the twentieth-and-final call count says. Removing `verdictPassed` from the gate makes the count twenty-one and fails the test, so the assertion discriminates the gate rather than restating it.
Collapsing the calls to one session per turn kind kept only the last write, so any round but the fifth could have been routed to the wrong session and the assertion would still have matched. The test compares the complete twenty-entry `turn:session` sequence now. Routing round two's verdict to the implementor fails it, and reports that entry — the case the previous shape could not see. Both casts are gone, and neither needed to be there. `PromptOptions.session` is `string | Session`, so a `typeof` check narrows it and `sessionKey` is reachable without asserting a shape. The root's result is `Json`, so `isJsonObject` guards it where the execution completes and the fields are read from a `JsonObject` rather than from a shape the test claimed.
Three merges landed under this target, and the inventory moves twice. #293 is complete. `<Repository>`, the self-closing `<Worktree>` and the lexical `<Dir>` are registered by `@executablemd/workflow/composition`, so the composition this document is written against executes under a workflow run. The unresolved set is seven names, not ten, and each still names the issue that owes it. What that leaves is a host distinction rather than a build one: registration is scope-local and the workflow host installs it, so plain `xmd run` resolves none of the three — evidence about which host ran a document, never about whether a component exists. The documents say that in as many words, because the alternative reads as a regression. #367's second slice is here too. The executor lock owns lifecycle transitions, so single-executor ownership, atomic begin and settle, cancellation and deletion are shipped. Cancellation does not signal a live executor: it refuses without mutation and tells the caller to interrupt the foreground process, and Ctrl-C tears the scope down in order and leaves the run resumable. Durable suspension and releasing the executor at a checkpoint stay open, which is still what keeps a checkpoint answering inside the execution that asked. The composition itself is untouched — the settled self-closing `<Worktree … as>` followed by lexical `<Dir path={worktree}>`, with no binding exception — and #301 still owns reachability for the five stage components.
The previous commit moved the inventories and left three passages behind, each still describing the state before the merge it was reconciling. `start.md` called single-executor ownership, atomic lifecycle transitions, cancellation and deletion unbuilt. They are #466's, owned by the executor lock; only durable suspension and releasing the executor at a checkpoint remain #367. `runtime.md` said `xmd workflow cancel` asks an active executor to stop, which is the one thing cancellation never does. It refuses without mutation against a live executor and directs the caller to interrupt the foreground process; without one, an eligible retained state transitions to `cancelled` under the exact lock; a completed or failed outcome stays authoritative. The specification called `<Repository>` and `<Worktree>` unimplemented alongside `<Agent.AddDir>`, which conflated a shipped composition with an unbuilt Agent boundary, and opened its lifecycle paragraph with "the rest of the lifecycle is not built" before listing what #466 shipped. Both now lead with what is built and end with what is not. The sweeps carry each of those exact forms as a forbidden pattern, self-tested against the text that produced them, so a regression fails there rather than in review.
`start.md` now declares its five authored stages in its own frontmatter, and that is the whole integration — a declaration, not a mechanism. #493 does the rest: `xmd workflow start` and `resume` resolve exactly those names from the blobs the pinned definition commit holds, each normalized to a canonical repository-relative path and the blob's own object ID, and those entries join workflow-definition identity and retained-history admission. Change what a stage says and it is a different definition; a resume reconstructs the component from its retained source, a same-named file beside the definition answers nothing, and an undeclared name resolves to nothing at all. Workflow execution still searches no repository directories, and ordinary `xmd run` is untouched. That closes #301's component-bundle slice and nothing more. Reaching the stage names is not running the workflow: scheduling the loop and continuing it unattended are still owed, and this branch adds no scheduler, no automatic resume, no Agent ceiling, no generated-XMD admission, and no push, pull request or issue. The inventory shrinks to five as a result of what else has landed. `<Git.Switch>`, `<Git.Add>` and staged-only `<Git.Commit>` are built (#294), and the shared Git-host reconciliation the remote effects are built over is too (#297) — the surface, not the components over it. What still resolves to nothing is what reaches a remote or a forge, plus the Agent ceiling and the constrained evaluator: `<Agent.AddDir>`, `<Expand>`, `<Git.Push>`, `<PullRequest>`, `<Issue>`. Durable suspension and giving the executor lock back are shipped, and a typed answer can be delivered to a run that is waiting — but delivery executes nothing and no scheduler resumes anything, so the documents say the wait is real and the continuation is still someone's explicit act.
…alls The documents had swung from one error to its opposite. They once said durable suspension was unbuilt; the last commit corrected that and overshot, saying an `<Elicit>` under `xmd workflow` *becomes* a durable suspension and that these checkpoints release the executor. Neither is true, and the two mistakes are the same mistake: reading shipped substrate as a shipped integration. What is shipped is `suspendFor()` — a run waits, gives its executor lock back, and a typed answer can be delivered to it (#367, #300). It is an Api operation. There is no v1 Markdown element that spells it, a suspension is a different mechanism from `<Elicit>`, which asks inside the execution already running, and this workflow installs no component or middleware that turns one into the other. So no checkpoint here releases the executor, delivery executes nothing, and nothing schedules a resume. Consuming that substrate is #301's supervised composition, and the documents now say exactly that without inventing a spelling or a bridge for it. Two smaller corrections fall out of the same pass. `<Git.Add>` and `<Git.Commit>` were still listed among names that resolve to nothing, when they are two of the six registrations the workflow host supplies alongside `<Repository>`, `<Worktree>`, `<Dir>` and `<Git.Switch>`. And the forge read that hands a network-denied reviewer a pull request's reviews, comments and checks is no longer unowned: `<Fetch>` reads over HTTP and retains what it read (#456). No stage writes those fetches yet, and admitting `<Fetch>` inside generated XMD stays #369's. The component probe now asserts four exact categories rather than a set — 16 ordinary, 5 bundled stages, 5 issue-owned unresolved, 6 workflow-host-only — and fails if any component the documents write goes unclassified or lands in the wrong group.
Two passages still described `suspendFor()`'s work as missing, and my sweep let them through because it only recognised the word "unbuilt". They said it another way: "durable suspension, and releasing the executor at a checkpoint, remain #367" in `start.md`, and "What is not built is the wait" in the specification. Both are corrected the same way the rest of the target already reads. The wait is built — a run suspends durably and gives its executor lock back (#367), and a typed answer can be delivered to it (#300). What is not built is anything that reaches or acts on it: no v1 Markdown element spells the operation, this workflow calls nothing that suspends, delivery executes nothing, and no scheduler resumes a run. `start.md`'s "waiting is a suspension" section now says that at the point where a reader would otherwise conclude its checkpoints already suspend. The sweep is broadened to the phrasings it missed — "remain #367", "not built is the wait", "still missing is the wait", and an elicitation that "is to record its pending request" — each self-tested against the sentence that produced it, and each failing against the commit they were found in.
"Answered inside the execution that asked it" reads as a settled outcome, and it is not one. The workflow host installs the same WebForm provider `xmd run` does — the gate is `!mode.testing`, and the workflow path passes `testing: false` — so an unanswered `<Elicit>` opens a loopback browser form and blocks the run. A probe against a committed workflow root says so directly. `workflow start` on a root with an unanswered `<Elicit>` printed a form URL and was still running when the probe killed it at twenty-five seconds; its status stayed `running`, its history recorded no suspension request, and it never settled `suspended` or gave the executor lock back. The same root with an authored `<Answers>` value completed normally. So the four documents now say that: `<Elicit>` is ordinary live elicitation, distinct from `suspendFor()`; `xmd run` and the workflow host both install WebForm; an `<Answers>` region answers without a person; and an unanswered one blocks rather than suspending, publishing neither a validated answer nor a suspension request. The target stays explicit — #301's supervised composition must consume `suspendFor()` without inventing a v1 Markdown spelling. The sweep now forbids "answered inside the execution" and requires the provider, the blocking, and the absent `suspended` settlement in its place.
#302 settles that a workflow Agent receives no Workspace checkout, no read-only materialization of one, no Workspace or host path as cwd, no ACP `additionalDirectories`, and no component that registers a directory. These documents described the opposite in a dozen places: `<Agent.AddDir>` in four stages, a `worktree` prop threaded from the root into three of them, prompts telling agents to investigate a registered checkout, and an inventory listing a directory component as work still to do. All of it is gone. The stages take no `worktree`, the test stub is deleted, and the prompts say what is true — each agent reasons over what its prompt renders and says plainly what it cannot determine. `InstructionFiles` is the concrete case that survives: host-authored file effects produce exact repository-relative paths and contents, and that captured result reaches Discovery and both Planning prompts as data. General repository observation stays the bounded XMD request/result loop #302 and #369 owe, and this change invents no spelling for it. The boundary is now proved, not asserted. The root-provider stub records the whole Agent-facing surface — factory options, agent and session selection, prompt text and prompt options — and a root that reads real instruction files proves the paths and contents arrive while the absolute directory they were read from does not, along with no Workspace value, checkout path, registration input or `additionalDirectories` field. Injecting that directory trips the guard, and summarizing the instructions fails the arrival check. `<Agent.AddDir>` leaves the inventory rather than moving down it: it is excluded by architecture, not waiting on implementation. Four names remain owed — `<Expand>`, `<Git.Push>`, `<PullRequest>`, `<Issue>` — and #293's Worktree identity, replay and cleanup question moves from Open to Settled, where the shipped contract belongs.
Seven capabilities merged under this document's base, and four of them change what it may say in the present tense. `<Git.Push>` and `<PullRequest>` are registered workflow-host components now (#495, #500, #504), so they leave the unresolved inventory and join the built list beside the composition and the local Git effects. Push publishes the selected checkout's exact current branch and commit to the same branch at canonical `origin`, with no props, no result, no force-push, no upstream change and no implicit stage or commit. The pull request requires that Push's own matching successful evidence and never publishes anything itself. Eight names are host-registered; two — `<Expand>` and `<Issue>` — still resolve to nothing. The upsert is settled, and this markup does not reach it. Without a number `<PullRequest>` asks for one to exist; with a number it asks for that one to say this. A revision iteration commits again, so the head it asks about is not the head the existing pull request holds, and an unnumbered request over an open pull request saying anything else is a deliberate conflict. Carrying the bound number into the next iteration is a composition this document cannot write today, so the markup stands and the gap is recorded under #301 rather than spelled by guesswork. #497 shipped the evaluator, for observation. Complete preflight inside the admission, pinned identities that consult no directory or bundle, exact request ceilings, one retained decision, and a continuation held to the ceilings it was admitted under. What it does not admit is file mutation — so the prose that had `<Expand>` publishing each admitted `<File>` now says which half is built, and `<Expand>` itself stays a documented placeholder #369 owes. #498 shipped explicit history forks, so forkability stops being described as absent: `history --forkable` adds its two columns, and lineage, reasons, changed-definition replay admission and retained Workspace-root copying come with it. A run continuing another run's history is still a different thing from an Agent provider continuing a session, and nothing schedules either. A `<Test>` declares its own timeout (#503), so twenty seconds is a default rather than a ceiling. The two comments that justified the TypeScript proof by that ceiling now give the reason that actually holds: a stub provider makes the whole twenty-call trace observable in under a second, where a long ACP scenario would take longer to prove less. `<Execution host="run">` (#501) is inherited and unmentioned — no document here discusses nested root execution.
#301's architecture amendment settles the composition the revision loop was missing. An expression prop that evaluates to `undefined` is omitted before prop validation and before the durable JSON boundary, so a document can seed a binding with an empty object and say the same thing on every iteration. The loop does that now. `let pullRequest = {}` is declared once, before the loop; the single `<PullRequest number={pullRequest.number} …>` inside it reads `undefined` on the first pass, so `number` is omitted and the unnumbered create-or-adopt contract applies; the result replaces the seed through ordinary `as` binding, and every later pass carries the retained positive number into the numbered update contract for that exact pull request. What made this necessary is unchanged: a revision commits again, so an unnumbered second request would find a pull request holding a different head and refuse it as a conflict. Two of the three pieces already work, and the probe checks both rather than assuming them — a member read on the seed yields `undefined` in an expression prop without failing, and an `as=` binding shadows the eval-declared name for later props. The third does not: the engine refuses an undefined prop today rather than omitting it. So the documents carry the omission as #301's, unbuilt, and the probe fails if that ever silently becomes true without the prose catching up. The amendment is general, and the reference state says so: required props still fail as missing, `null` stays an explicit value, and an unbound name still fails. None of it is a `PullRequest` special case, and no engine code changes here — Planning owes that proof before implementation is handed off.
Two paragraphs in each of three documents contradicted each other. One named a third enforcement layer — "registered Workspace paths are presented as read-only filesystem views" — and the next denied that any path is registered at all. The third layer was the stale half: #302 settles that a workflow Agent receives no checkout, no materialization of one, no Workspace or host path as cwd, and no `additionalDirectories` over ACP, so there is nothing to present read-only. Two layers enforce the ceiling, and the reason there is no third is now the sentence that used to be the third. The data-flow table said `worktree` is consumed by "every stage that registers a directory with an Agent". No stage does. It is consumed by `<Dir>`, and through `<Dir>` by XMD's own file and Git effects. The executable inventory disagreed with its own classification. It said four names in the loop body resolve to nothing and named the three components that reach a remote or a forge — but only `<Expand>` and `<Issue>` resolve to nothing. What blocks the stage is now separated by kind: two unresolved names, one unbuilt engine capability (#301's omission of an expression prop evaluating to `undefined`, which the seeded pull-request number needs), and two components that are shipped and merely out of reach behind them. A shipped component behind an unbuilt prerequisite is unreachable, which is not the same claim as unbuilt, and the sweeps now hold that distinction open in both directions: the exact stale sentences fail, and the corrected denials pass.
Four merged capabilities change what this target may say, and two of them change what it writes. `<Capture>` left the reserved set with no alias (#528), so every structural invocation here is `<Let>` — nine of them across five documents, plus the prose that named the removed directive. The pull-request loop's seed stops being JavaScript: `<Let as="pullRequest" value={{}} />` binds the empty object directly, which is the shipped form for exactly this. It does not close #301's gap and the documents say so — `<Let value>` binds a value, and does not make an ordinary component prop whose expression evaluates to `undefined` become omitted, so the seeded `number={pullRequest.number}` still waits on the omission nothing has built. The deferred-issue markup was invalid against the shipped contract (#516). It passed `pullRequest` and `finding` props that do not exist on a component whose schema is closed. It now writes what #516 actually offers: an `<IssueTracker url={props.tracker}>` naming the container, a paired `<Issue title={finding.title} as="deferredIssue">` whose body is the description, and a result read as exactly `{ url }`. The body carries what a reader needs to know why the finding was deferred — the finding, its evidence, the pull request it came from, and the review and checkpoint that classified it. `tracker` is a new required root prop threaded to the stage; it selects a target and grants nothing, because the ceiling lives beside the host's credentials. That moves `<Issue>` out of the unresolved inventory and leaves one name in it. `<Expand>` is the only component here that resolves to nothing, and the stage still cannot reach the forge — which is unreachable, not unbuilt. Authentication and crash inspection are recorded where the documents already discuss the effects they belong to. Credentials are live host input acquired per invocation and are not a prop, a retained value, a journal field or anything an Agent sees (#532). A crashed run is inspected from a private recovered copy, leaving the authoritative source byte-identical (#521).
6305c42 to
568f19a
Compare
Synchronizes the living end-goal target with the contracts that have settled on
main(under #292), carriesthe document-logic proof #290
requires before this PR can merge, closes the workflow over the components its
root declares, and keeps every stage at the Agent ceiling
#302 settles.
568f19a528a4506230d5fd8c457191ced6abd261mainat5542c921bb8145458dc5f2446a61a1082a916f41clean, no conflicts. Force-pushed with
--force-with-lease=…:6305c429113583f0b8ecb29562801dc3deeeaf05; the leaseheld (
+ 6305c429...568f19a5 … (forced update)).a0210e1..348f0956istest-only — a helper that lets a test read a killed run's SQLite store without
writing to it, plus the two tests that use it. It ships no capability these
documents describe.
artifacts.md's claim is about the next connection — therun's next owner, which can write and does recover last-committed state — while
🧪 Recover the hot journal a killed run leaves before reading its store #512 concerns an observer that cannot, so the two are complementary rather than
in conflict. The one test in this PR's reported evidence that 🧪 Recover the hot journal a killed run leaves before reading its store #512 changed,
packages/cli/tests/workflow-crash.test.ts, was re-run:1 passed (3 steps).6305c42on348f0956,4961e03,35df675andbb367b2ona0210e1,fca704con1948bf0,51b4899on1948bf0,688395fonf3d777d,59f743fonf3d777d,9f10b9eon7a8a9b7,40382a5on6a777dd,e552c43onde6d835,9010557and443a591before those.
Why
The target is kept current as capabilities land: a settled contract is described
in the present tense, and a capability that does not exist yet says so with the
issue that would supply it. Seven merges landed under the previous base, and
four of them change what this document may say in the present tense.
The ten merges in
348f0956..5542c921<Let>replaces<Capture>Captureleft the reserved set and now resolves as an ordinary component. Nine structural invocations across five documents became<Let>, plus every prose reference to the removed directive.Implementation.md's seed becomes the shipped direct-value form<Let as="pullRequest" value={{}} />.<Issue>,<IssueTracker><Issue pullRequest={…} finding={…} />spelling was invalid against a closed schema. Replaced by the public contract:<IssueTracker url={props.tracker}>wrapping a paired<Issue title={finding.title} as="deferredIssue">whose body is the description, binding exactly{ url }. New requiredtrackerroot prop, threaded to the stage. Both names move into the built workflow-host category.GH_TOKEN, thenGITHUB_TOKEN, thengh auth token --hostname github.com. Live host input per provider invocation — never a prop, secret schema, copied credential, retained value, Workspace material, argument, URL, journal field, output or diagnostic cause. Replay acquires nothing.SQLITE_READONLY_ROLLBACKfalls through to recovery, which copies database and journal under recovery coordination and rolls back into the copy. The authoritative source stays byte-identical. Coordination grants no executor or lifecycle authority and opens no Workspace or provider effects.liststays complete-or-error.<Session.Launch><Prompt>and does not implement #302. A sweep check forbids either claim.<Json>The earlier merges, and what each did to the reference state
<Git.Push>origin; no props, no result, renders nothing, never force-pushes, changes no upstream tracking, performs no implicit stage or commit. #370's administrative state is not treated as the capability's state.<PullRequest>title; optional positivenumber; optionalbase; optionaldraft; rendered body; structured bound result. Requires matching successfulGit.Pushevidence, never pushes, never rewrites the head. The stale #295/#297 open question is gone.evaluateGeneratedXmd()and the pinned observation machinery are shipped trusted-host APIs. Prose that called all generated-XMD admission unbuilt is split from what is genuinely still owed.history --forkable, forkability reasons, lineage, changed-definition replay admission and retained Workspace-root copying are shipped under #368. Every "not built" claim about forks is gone.<Test timeout="<duration>">ships; 20 s is the default, not a fixed limit. Two stale comments corrected.<Execution host="run">inside canonical<Test>. Inherited with no reference-document edit — no affected document discusses nested root execution.workflow.componentsdeclaration is preserved byte-for-byte.What each merge did not make true
<Expand>stays a documented placeholder tied to Evaluate Agent-generated XMD through a constrained allowlist #369, not a shippedcomponent. The evaluator under it is a trusted-host API; no document calls it,
and no eventual public spelling is invented here.
<Fetch>on the allowlist only when the run states its requests, ordinaryretained observation effects, and replay of the retained decision. It admits
no file mutation, and the documents no longer imply otherwise.
<Expand>, and bundled-Markdown admission.fork, no generated-XMD admission and no generic component search path. An
explicit history fork stays distinct from Agent-provider session continuity.
host="workflow"and<WorkflowRun>remain unbuilt (🧪 Run nested document executions under production host profiles in Markdown tests #454); the testing specsays so and no host supplies a workflow profile.
The revision loop, settled by #301's amendment
The Architect's amendment on
#301
settles the composition this PR previously reported as a gap: an expression prop
that evaluates to
undefinedis omitted before prop validation and beforethe durable JSON boundary. Required props still fail as missing,
nullstays anexplicit value, an unbound name still fails, and it is the general expression-prop
contract rather than a
PullRequestspecial case.Implementation.mdnow composes it as the amendment specifies — the seed once,before the loop, and one invocation inside it:
First iteration:
pullRequest.numberisundefined,numberis omitted, and theunnumbered create-or-adopt contract applies. The result replaces the seed through
ordinary
asbinding, and later iterations pass the retained positive number andselect the numbered update contract for that exact pull request. This is what the
loop needs, because a revision commits again — so an unnumbered second request
would find a pull request holding a different
headShaand refuse it as adeliberate conflict.
The omission itself is not built. The engine refuses such a prop today
(
Expression prop … evaluated to a non-serializable value (undefined),packages/core/src/expand.ts:3043), so both inventories carry it as #301's,unbuilt. No engine code changes here; Planning owes the focused proof of optional
omission, required-prop refusal, JSON-clean retention/replay and the
first-create/later-update loop before implementation is handed off.
Component inventory
Re-run after rebasing; every category size is asserted and every written name
must land in exactly one category.
Agent,Break,Each,Elicit,Else,Fetch,File,Glob,If,Let,Loop,Output,Parse,Prompt,Return,SafeParse,SessionDiscovery,Implementation,InstructionFiles,Planning,UserCheckpoint<Repository>,<Worktree>,<Dir>,<Git.Switch>,<Git.Add>,<Git.Commit>,<Git.Push>,<PullRequest>,<Issue>,<IssueTracker><Expand>→ Evaluate Agent-generated XMD through a constrained allowlist #369Confirmed at the registration site:
packages/workflow/src/composition/installation.tsregisters exactly those ten names. Plain
xmd runresolves none of thembecause it composes no workflow — host-scoped resolution evidence, never an
unbuilt-component claim.
<Agent.AddDir>is in no category: #302's architectureexcludes it.
Shipped is not the same as reachable.
<Git.Push>and<PullRequest>arebuilt and registered, and the implementation stage still cannot reach them —
<Expand>(#369) has to produce the change before there is anything to stage,commit, push or open a pull request for. What blocks the stage is now separated
by kind in the documents: two unresolved names (
<Expand>,<Issue>), oneunbuilt engine capability (#301's
undefinedomission), and two shippedcomponents out of reach behind them.
Both explicit unresolved-name inventories (
start.md,primitives.md) areupdated, and the pairing sweep is 2/2 — one
<Expand>/#369 row in each.#290 evidence — unchanged, with one comment corrected
All existing planning-loop, revision-routing, user-authority, exhaustion,
ordering and mutation evidence is preserved. Per the Planner's decision the
TypeScript root-Agent-provider proof for criterion 6 is retained: it proves
the complete 20-call trace and the authorization boundary directly and in under
a second, and a configurable Markdown timeout does not call for a duplicate long
ACP scenario. What changed is only the justification in two comments, which
had cited a fixed 20-second ceiling that #503 removed.
How to verify it
Frozen document evidence
The new merged-state sweep
Rejects:
<Git.Push>or<PullRequest>described as unbuilt; #295 described asan open contract; forks or forkability described as unbuilt; a fixed 20-second
<Test>timeout; all generated-XMD admission described as unbuilt; observationadmission described as mutation admission;
<Expand>described as a shippedpublic component.
Every check is self-tested in both directions: each stale sample must match, and
each settled sample must be exempt without the exemption swallowing a stale one.
The reconciled contract sweeps fail at
fca704ctoo (8 findings), so their passhere is discriminating rather than vacuous.
Focused shipped regressions
Scope
This turn's commits change 11 files in total.
bb367b2(the mergereconciliation) changes 9:
35df675(the #301 amendment) changes 3, two of them again:Total branch scope against the base: 26 files — eleven reference documents
and fifteen test assets.
Intentionally unchanged
undefined-omission engine change is Compose the supervised adversarial implementation workflow #301'sown work, not this PR's. No
main-owned architecture or specification, and noissue edits.
workflow.componentsdeclaration is preserved byte-for-byte(verified by diff against
fca704c).<Expand>placeholder and its#369attribution.spelling is proposed here.
Risks and limitations
and shipped behavior in the present tense; a reader taking a fragment out of
context can still confuse the two.
<Git.Push>and<PullRequest>as shipped withoutthis branch executing either under a real workflow run — that evidence is the
merged PRs' own regressions, re-run above, not a run of this workflow.
it depends on is unbuilt — so the implementation stage still would not run
today, now at the seeded
numberprop rather than at a conflicting upsert.compilation, name resolution, text sweeps. Prove the shipped adversarial-planning document logic #290's proof, the boundary test and
the bundle probe are the exceptions: they execute.
claim rather than resolved.