From 86b6e6ff15d78c540a370086b06ae81aa9849ca5 Mon Sep 17 00:00:00 2001 From: Taras Mankovski <74687+taras@users.noreply.github.com> Date: Tue, 4 Aug 2026 15:48:31 -0400 Subject: [PATCH 1/8] =?UTF-8?q?=F0=9F=93=9D=20Synchronize=20the=20living?= =?UTF-8?q?=20end-goal=20target=20with=20current=20XMD=20contracts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The adversarial implementation workflow target was written against a main that predates the error-model rules (#315), the error vocabulary rename (#317), error-model semantics (#319), and operation-scoped state (#325). It asserted `` as structural syntax, "the unresolved diagnostic", and a durable layer that "replays recorded results" — none of which describe main. This re-derives it on 7d7bdf2. The error model reaches the documents. A stage component is split by its `` boundary: the region inside runs under the `output` error mode, everything outside is documentation and runs under `throw`, which no `` region replaces. So a stage returns a complete validated result or it fails, keeping only what it had already rendered — the final `` in each repair loop is a real gate. `throwOnError` is load-bearing for the same reason: without it a failed prompt records its failure and returns its text, raising nothing to decide. The markup did not run. Every stage passed props through expression props as `agent={props.planner}`, which fails on main with `props is not defined` — an expression prop reads the bare binding while text interpolation reads the namespace. Unifying them is #305, whose acceptance includes expression props reading `props.name`. 22 sites are corrected to the spelling main supports, and the asymmetry is recorded with the issue that removes it. Vocabulary is collapsed 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, and cross-process continuation, in place of the four names these files used for a ledger and the three for a run. Missing capabilities now cite the issue that supplies them rather than saying only "not implemented", and replay is described as reaching the state execution resumes from, never as the continuation itself. Planning-loop exhaustion stays open. It is recorded against #290, which pins the behavior; this change reports `verdict.passed` and does not call an exhausted loop converged. Evidence: `inspectDocument` parses all 9 frontmatters and compiles both schema kinds; `compileParseSchema` compiles all 5 embedded draft-07 schemas; `inspectComponent` resolves 21 shipped and 5 repository names and confirms 9 missing ones unresolved; `InstructionFiles` runs end to end against the repository's own AGENTS.md. --- specs/adversarial-implementation-workflow.md | 574 ++++++++++++++++++ specs/markdown-agents-vision.md | 78 +++ .../adversarial-implementation/Discovery.md | 63 ++ .../Implementation.md | 330 ++++++++++ .../InstructionFiles.md | 29 + .../adversarial-implementation/Planning.md | 215 +++++++ .../UserCheckpoint.md | 210 +++++++ .../adversarial-implementation/artifacts.md | 128 ++++ .../adversarial-implementation/primitives.md | 237 ++++++++ .../adversarial-implementation/runtime.md | 86 +++ workflows/adversarial-implementation/start.md | 185 ++++++ 11 files changed, 2135 insertions(+) create mode 100644 specs/adversarial-implementation-workflow.md create mode 100644 workflows/adversarial-implementation/Discovery.md create mode 100644 workflows/adversarial-implementation/Implementation.md create mode 100644 workflows/adversarial-implementation/InstructionFiles.md create mode 100644 workflows/adversarial-implementation/Planning.md create mode 100644 workflows/adversarial-implementation/UserCheckpoint.md create mode 100644 workflows/adversarial-implementation/artifacts.md create mode 100644 workflows/adversarial-implementation/primitives.md create mode 100644 workflows/adversarial-implementation/runtime.md create mode 100644 workflows/adversarial-implementation/start.md diff --git a/specs/adversarial-implementation-workflow.md b/specs/adversarial-implementation-workflow.md new file mode 100644 index 00000000..ff617b3b --- /dev/null +++ b/specs/adversarial-implementation-workflow.md @@ -0,0 +1,574 @@ +# Adversarial Implementation Workflow + +- **Status:** Living end-goal target +- **Audience:** Maintainers and contributors + +This document describes a workflow for developing software with a user, a +planner agent, and an implementor agent. It is the target the runtime is being +built toward, and it 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. Sections marked as not implemented +define what must be built, not what the runtime does today. + +The compact workflow map and its stage-level details live in +[the adversarial implementation workflow directory](../workflows/adversarial-implementation/start.md). + +The executable sketch is organized around a compact +[entry document](../workflows/adversarial-implementation/start.md), with stage and +runtime details in adjacent Markdown files. + +## Purpose + +The workflow helps a user turn a design conversation into a sequence of +reviewable, validated changes without making hidden agent transcripts the source +of truth. + +The planner and implementor are equally capable of analysis. Their roles create +useful opposition: + +- The planner interviews the user, constructs a theory of implementation, and + challenges plans and implementations. +- The implementor investigates the repository, validates the planner's theory, + and proposes a concrete implementation plan. +- The user makes final decisions about behavior, scope, architecture, risk, + sequencing, and lasting constraints. + +The agents converge by exchanging evidence. Neither agent wins a disagreement +by role or authority. + +An authored `` component asks its supplied agent whether the +next transition contains a material choice. It involves the user when one +exists or when the agent is unsure. The current workflow supplies the planner, +but the component does not encode that role. It may determine that user +involvement is unnecessary, but only the user resolves a material choice. + +## Smallest complete path + +User and planner discuss the change + → Planner writes an implementor handoff + → User validates the handoff when discovery was required + → Implementor validates the theory and writes a plan + → Planner reviews the plan + → Implementor revises or the user resolves remaining decisions + → User authorizes the shared plan + → Implementor changes the repository + → Planner reviews the pull request + → Review and revision repeat until the change is accepted + +Implementation does not begin merely because the planner produced a handoff. +The handoff is a theory for the implementor to test, not an implementation plan +to follow unquestioningly. + +### Initial execution model + +The first exercise runs one stage at a time under user control. Each stage reads +declared inputs, publishes explicit results, and returns. The user inspects the +result and manually starts the next stage. + +This manual boundary lets the exercise test the interview, handoff, plan, +review, decision, and revision contracts without first implementing a resident +process, file watcher, suspension protocol, or resumable resource scope. + +The automated form treats script execution as a loop. An iteration stops on the +first applicable signal in this priority order: + +1. The iteration reaches its defined completion. +2. A configured file is created or updated in a configured directory. +3. The user provides direct input. + +Stop arbitration is not implemented (#300). The manual exercise records where +each signal would have been used without detecting or prioritizing them. + +Signal 3 has a shipped in-run form. `` asks a person a schema-validated +question during execution and binds the validated answer, and `xmd run` +composes the WebForm provider so that question opens a loopback browser form. +What remains missing is cross-process continuation: stopping at a stage +boundary and resuming in a later invocation. That belongs to `` and +`` (#298, #289), so the user still supplies decisions between manually +invoked stages whenever a stage boundary — rather than a question inside one +run — is what stopped the work. + +### Runtime intervention + +The Effection inspector provides future meta-control over a running loop. It may +interrupt execution so that a user can inspect or manipulate program state +during a major runtime intervention. + +Inspector control is distinct from ordinary workflow input. The workflow still +needs an in-band mechanism for routine user decisions; the inspector is an +out-of-band operational tool rather than the decision protocol. Neither +mechanism is required by the manual exercise. + +## Entering the workflow + +Work enters by one of two paths. + +### Design discovery + +A new, ambiguous, or design-sensitive request begins with a planner interview. +The planner asks for the decisions needed to produce a coherent handoff. The +user validates that handoff before it reaches the implementor. + +The interview does not need to exhaust every possible detail. It establishes +enough intent, constraints, evidence, and open questions for the implementor to +investigate productively. + +### A bounded deferred issue + +An issue discovered while implementing or reviewing another change may go +directly to the implementor when it already states: + +- the observed problem and supporting evidence; +- the desired observable outcome; +- relevant constraints and non-goals; +- why it was excluded from the originating change; and +- the originating issue, pull request, review, or execution. + +If the issue leaves material behavior, scope, architecture, or risk unresolved, +it returns to planner discovery. Deferral does not make an unclear issue ready +for implementation. + +## The planner handoff + +The handoff records both user-validated design and a falsifiable implementation +theory: + +- purpose and desired observable behavior; +- constraints, non-goals, and accepted risks; +- repository and architectural context; +- likely affected boundaries and change nucleus; +- the planner's proposed implementation theory; +- assumptions the implementor must confirm or refute; +- evidence and experiments needed to evaluate the theory; +- expected validation; +- likely pull-request topology; and +- unresolved decisions that remain with the user. + +User decisions and planner hypotheses remain distinguishable. Later evidence may +invalidate a planner hypothesis without reopening settled product intent. + +## Plan convergence + +The implementor investigates before committing to the handoff's theory. Its plan +reports: + +- confirmed assumptions and their evidence; +- refuted assumptions and their evidence; +- amendments to the theory; +- the proposed implementation and validation; +- expected environmental and public effects; +- pull-request boundaries and dependencies; and +- questions that require a user decision. + +The planner reviews the plan against the user intent, repository evidence, +instructions, specifications, and observable validation. A failed review returns +a focused prompt that the implementor can apply directly. + +Factual disagreement calls for more evidence. When evidence leaves more than one +viable choice, the agents present the options, consequences, and recommendations +to the user. Convergence means that the final plan reflects a shared +understanding and the user's decisions; it does not require the agents to have +started with the same preference. + +Agents may decide reversible implementation details within the authorized plan. +They return choices affecting behavior, scope, architecture, risk, pull-request +decomposition, or lasting constraints to the user. + +### Structured agent results + +Agent output remains prose when later execution only needs to read it. Output +that controls a branch or deterministic effect is JSON validated against +draft-07 JSON Schema captured as ordinary document content. + +`` returns either a validated value or the candidate and normalized +validation errors, preserving the rendered input exactly so a correction prompt +can quote what was said. A bounded `` uses nested `` control +flow to show any correction prompt explicitly in the producing agent's session. +Correction turns receive the candidate, validation errors, and schema; they do +not run tools, modify files, or perform additional analysis. A final `` +fails the stage if the candidate remains invalid. + +Validation judges the value and never edits it: no declared default is +inserted, no type is coerced, and no undeclared property is removed. What a +document binds is exactly what its content said. + +Parsing and validation are shipped, provider-neutral core behavior. ACP +supplies an agent provider but does not own parsing or hidden repair behavior. +The workflow does not use named schema strings or a `` prop. + +This in-document parsing is separate from a component's own return value, and +both are shipped. A component that declares no `returns` returns its rendered +Markdown, and `as` binds that text; a component that declares `returns` renders +nothing, holds one direct top-level ``, must be invoked with `as`, and +binds one schema-validated JSON value. The two are mutually exclusive. + +A registered function component that declares no `returns` binds by reference: +`as` binds the object itself, and nothing is journaled for it. Declaring +`returns` is the opt-in that makes a return a validated JSON record instead — +the value crosses the JSON boundary, is validated against a clone, and only the +normalized clone reaches the caller. `` declares `returns`, so the +`string[]` this workflow binds is that validated clone rather than a +by-reference binding. + +Every stage component in this workflow is a text component, because each +stage's output is also material a user reads at a checkpoint. + +### A stage fails rather than returning a half-record + +A stage's `` region runs under the `output` error mode; everything +outside it is documentation and runs under `throw`. An undecided error at either +position fails the run rather than producing a result a caller would bind, and +no `` region can convert a documentation failure into a printed +error. That is what makes each stage's final `` a gate: malformed agent +output cannot reach control flow or a deterministic effect. + +A failing region keeps only what it had already rendered. That partial text +reaches the output stream; nothing after the failure does. Continuing after a +failure is always explicit — a bounded repair turn the document shows, not +recovery the engine performs on the author's behalf. + +`throwOnError` on a `` is required for the same reason: without it a +failed prompt records its failure and returns its text, raising nothing for the +error mode to decide, and the stage would continue with an empty reply. + +## Workflow-owned development assets + +The workflow owns worktrees, working directories, captured handoffs, plans, +feedback, decisions, branches, issues, and pull requests. An agent does not own +an asset merely because its process created it. + +Deterministic components provide and pass those assets. Two of them are +shipped: + +- `` resolves explicit include and exclude patterns against `Env.cwd` + into a `string[]` of relative paths — `/`-separated on every platform, + deduplicated, and sorted lexically by code point. Directories and symbolic + links are never results. +- A self-closing `` reads and renders exact repository content relative + to `Env.cwd`, and `as` captures that text. Its content-writing form + atomically replaces the target and renders nothing at all: no output, no + path, no write handle. It is used for source changes, explicit exports, or + external tools that require a path, not as the default agent handoff. + +Two are not implemented: + +- `` creates or resolves a workspace and sets `Env.cwd` while + rendering its children (#293). The contextual working directory it would + establish is itself shipped and already inherited by files, globs, processes, + and agents. +- `` creates or resolves the pull request for a branch and returns + its identity and state (#295). + +Each environmental operation declares its inputs and preconditions, reconciles +existing state, returns a structured handle, and records its observed effects. +Rerunning a pull-request operation resolves the existing pull request rather +than creating a duplicate (#297). + +Nested agent prompts receive required handoffs, plans, reviews, decisions, +commit identities, and pull-request metadata as exact content. Repository +evidence remains available through paths and read tools for selective +investigation. The workflow does not depend on a user copying output between +agent transcripts or asking an agent to locate and read another agent's file. + +### The artifact ledger + +The artifact ledger is not implemented (#291). This section defines what +`` must provide. + +Handoffs, plans, reviews, user decisions, and execution events live in sidecar +Git history rooted at `refs/xmd/runs`. They are Git objects in the same +repository, but they do not appear in the source tree or the history of the +main source branch. + +Each run records the pinned source revision it investigated along with its +artifact contents and provenance. The sidecar ref keeps the objects reachable, +and the workflow pushes and fetches it explicitly because ordinary branch +refspecs do not imply transport of custom refs. + +Captured results become artifact versions keyed by stable component and +loop-iteration identity. Resuming a named run restores those values and renders +required content directly into later prompts. A generated file is an optional +export, not canonical run state. Reading such a file does not inherently save +tokens because its contents still enter model context. + +`` creates or resolves a run identity and installs it through a +contextual Run API (#289). Worktrees, decisions, pull requests, and issues +consume that identity internally. The workflow author does not pass it through +component props; the API exposes it only when authoring logic genuinely needs +the identity. That state is created inside the operation that owns it and torn +down with it — there is no module-scoped registry and no library object that +accumulates runs. + +Ledger entries are automatic. A completed stage, a completed loop iteration, +and a terminal success, failure, or cancellation record the observed props, +artifact versions, decisions, effects, outcome, and stop reason. Authors do not +repeat those paths in an explicit checkpoint. + +The execution journal already holds part of that material. `` records +every iteration it enters and one terminal record whose outcome is `break`, +`exhausted`, or `error`, and refuses a replay whose stored outcome or iteration +count disagrees with what this run reached — there is no `cancelled` outcome +and no stage-stop record. Each `` is one durable operation carrying its +identity, input, agent and session, terminal status, text, and structured +failure. `` journals only its validated answer, keyed by a fingerprint +of the compiled schema and the rendered message, and refuses a recorded answer +whose question does not match the one this run computed. What is missing is one +run identity correlating those records, artifact versions above them, and +persistence outside the executing process. + +Replay is what makes a resumed stage possible: a run that failed is still a +complete record, and replaying it restores the output and the failure without +re-executing anything. Replay arrives at the same state, where execution can +resume; it is not itself the continuation. + +The first exercise creates and reads this history with ordinary Git commands. +It does not require an Executable.md component for artifact storage. Content is +screened for credentials and other data that must not become durable before it +is written to the history; that screening becomes a default-on execution policy +under #199. + +### Experiment isolation + +The run resolves and records its pinned source revision, then creates the +worktree before discovery. Planner discovery, handoff validation, implementor +planning, implementation, and review use the same pinned filesystem even if the +base branch moves. + +A worktree isolates experimental Git state from the user's current checkout, +but it is not a security boundary. A supervised manual exercise combines that +disposable worktree, the narrowest available agent permission policy, a +deliberately limited task, and explicit user approval before durable or remote +effects. + +`` (#293) and `` (#302) are not implemented. `` and +`` confine traversal to `Env.cwd` today, but that guarantee is about +traversal rather than about the filesystem being stable: a directory that is +real when it is read could be replaced afterwards. Containment that does not +depend on observed filesystem state is issue #227, and an unattended loop is +bound to its resolution. + +An unattended implementation loop requires an enforceable sandbox boundary. Its +policy declares: + +- readable and writable roots; +- inherited working directory; +- available environment variables and secrets; +- process and command capabilities; +- network destinations and operations; and +- which deterministic components may perform durable Git or GitHub effects. + +The planner normally receives repository read and search access. The +implementor receives write access only to the workflow-owned worktree. +Deterministic components receive narrow capabilities for sidecar Git history, +worktree metadata, commits, issues, and pull requests rather than passing those +capabilities through an agent prompt. The implementor does not require write +access to the repository's shared Git metadata. + +The sandbox owns the processes it starts and closes them with its execution +scope. Retaining a worktree preserves filesystem evidence after the processes +stop; it does not preserve running effects or broaden their permissions. + +### Cleanup and recovery + +Resources clean up with their execution by default. Agent sessions, processes, +streams, and other ongoing effects always stop before their enclosing scope +closes. + +An execution may explicitly retain its workspace for inspection. A failed or +cancelled execution also retains a worktree when removing it would discard +uncommitted or unpushed changes. The execution reports the retained path, branch, +state, and recovery reason. + +Durable published effects such as commits, issues, and pull requests remain +after temporary execution resources close. + +## Reviewable pull-request chains + +A feature is designed outside-in: desired behavior, public contracts, +architectural spine, and validation boundaries precede internal implementation +details. + +The pull-request series is ordered according to the topology of the change. +Vertical feature slices often remain outside-in. Infrastructure, abstraction, +migration, or fan-out work may use dependency, inside-out, execution, data-flow, +or risk order when that makes each change easier to understand and validate. + +Pull requests optimize for comprehension rather than a fixed line count. A +reviewable pull request normally has: + +- one falsifiable behavioral claim; +- one primary change nucleus or coherent cluster; +- understandable fan-out from that nucleus; +- explicit invariants and deliberate non-changes; +- independent validation evidence; +- a safe state when merged without later slices; and +- a review path that lets the user reconstruct the change without reading the + diff linearly. + +Multiple unrelated nuclei, tangled concerns, and independent behavioral claims +are stronger reasons to split than raw size. Broad mechanical fan-out may remain +reviewable when its nucleus is clear and mechanical or generated changes are +identified. + +The planner proposes decomposition, the implementor validates dependencies and +feasibility, and the user decides material sequencing or scope choices. + +## Review findings and deferred obligations + +A review finding has four possible dispositions: + +1. Fix it in the current pull request because correctness depends on it. +2. Insert a focused repair because the remaining pull-request chain depends on + it. +3. Create a provenance-linked issue because the problem is valid but fixing it + now would derail the current chain. +4. Reject it because it is unsupported, unrelated, or intentionally outside the + product direction. + +The planner selects a disposition from the evidence and asks the user for +verification when scope, impact, or urgency is uncertain. The user may override +the classification. + +A deferred issue records: + +- the originating review and pull request; +- evidence that the problem is real; +- why it is outside the current slice; +- how immediate work would disrupt the chain; +- whether later slices depend on it; +- its intended timing, such as after the chain, before release, or backlog; and +- the pull request or decision that eventually resolves it. + +After the planned chain completes, the planner audits deferred obligations and +decides which remain part of the initiative. It asks the user to verify that +decision when uncertain. Creating an issue is therefore a scope-preservation +operation, not silent abandonment. + +## The lab and `xmd play` + +The lab initially remains conceptual. At each stage, the user and agents inspect +the workflow, identify friction, and decide what to change about the process. +The workflow does not modify itself. + +The automated implementation loop establishes the asset, decision, review, +cleanup, and recovery behavior needed by a later `xmd play` mode. Play turns the +document into a living collaboration surface where agents propose visible +executions or document changes, the runtime validates and enforces approved +effects, and the user remains the final authority for material changes. + +Play follows a working implementation loop. It helps the user step back from +managing implementation mechanics and focus on the design of the workflow. + +## First exercise + +The first exercise uses this workflow to design its own initial automation. +The user triggers each stage manually within a named run. `` restores +captured values and renders required content directly into later prompts. +Generated artifacts do not appear in the repository or worktree unless the +user explicitly exports them. + +Until `` and `` exist, the exercise runs in one process and +one existing working directory. The document logic — instruction discovery, +the planner interview, plan convergence, the bounded repair turns, and the user +gate — is executable on shipped syntax today; what is not yet executable is the +workflow spine around it. + +The exercise succeeds when: + +1. Each manually invoked stage declares its props, records named artifact + versions, and returns. +2. The planner completes the technical interview and produces an implementor + handoff. +3. The user validates the handoff. +4. The implementor returns a repository-grounded plan that confirms, refutes, or + amends the planner's theory. +5. The planner returns a verdict with evidence, a focused revision prompt on + failure, and explicit user decisions when needed. +6. The loop reaches a user-authorized shared plan before implementation. +7. The run creates the workflow-owned worktree from its pinned source revision + before discovery; implementation changes only that worktree and records + validation evidence. +8. The planner reviews the resulting pull request, and implementation and + review repeat when the verdict fails. +9. The user decides whether to accept the completed change. +10. The participants record every hidden-state dependency or optional file + export encountered during the exercise. +11. The completed artifact versions remain reachable through sidecar Git history + without appearing in the main source tree. +12. Those observations determine the smallest useful runtime implementation + rather than a speculative complete orchestration system. + +## Technical questions + +### Settled + +These were open when this document was written and have since been answered by +shipped behavior. They are recorded because the answers constrain what remains. + +1. **How does a component return a structured value later components consume + without prose parsing?** A component declaring `returns` renders nothing, + holds one direct top-level ``, requires `as`, and binds one JSON + value validated against a clone of what it produced. A registered function + component that declares no `returns` binds its return by reference instead. +2. **Which repository reads and writes belong in ``, and how are writes + confined?** Self-closing reads and renders exact content; the content form + atomically replaces the target and renders nothing. Confinement is lexical + path arithmetic against `Env.cwd` before any filesystem call, re-checked + against resolved symlinks immediately before the write. +3. **How does `` present document-defined options and bind a validated + response without assuming decision policy?** The author's schema defines + every available response; there is no built-in approve, decline, or cancel, + and no `mode`, `provider`, or `uiSchema` prop. The schema compiles, the + content expands, the provider answers, and core validates that answer + against the same compiled schema. `` supplies answers from the + document without choosing a transport. +4. **How do `` and `` bind values and report errors without + conflating them with component output?** Both require `schema` and `as` and + render nothing. `` absorbs JSON syntax and schema-validation + failures and nothing else, so an unusable schema and a child execution + failure both still fail. +5. **What ``, ``, and `` semantics repeat stages without + hiding why they stopped?** `` requires `max`, opens no binding scope, + records every iteration it enters, and writes one terminal `break`, + `exhausted`, or `error` outcome that a stale replay cannot contradict. + Reaching `max` completes normally; whether that means success is the + document's own `` to write. +6. **How does `` define ordering, duplicate removal, symlink traversal, + and confinement?** Relative `/`-separated paths, deduplicated, sorted + lexically by code point; directories and symlinks are never results, which + is what keeps traversal inside `Env.cwd`. +7. **How is the contextual working directory inherited?** `Env.cwd` is + installed by a component for its content and read by files, globs, + processes, daemons, and agents without any of them being handed a path. +8. **What happens to a stage whose agent returns unusable output?** The stage + fails. Documentation runs under the `throw` error mode and an `` + region under `output`, so the final `` ends the run rather than + binding malformed data; a failing region keeps only what it had already + rendered. + +### Open + +The exercise must resolve enough of these to implement one vertical slice: + +1. How does `` choose an idempotent identity, branch name, location, + and cleanup policy from the pinned source revision the run already recorded + (#293)? +2. Which permission policies distinguish planner investigation from implementor + modification (#302)? +3. What state makes environmental operations safe to repeat or resume after + interruption, including an effect recorded inside an ephemeral environment + the current run did not create (#218)? +4. Which pull-request and issue operations belong in the first local experiment + and which can follow after plan convergence works (#295, #296, #297)? +5. Which host sandbox can enforce the first experiment's filesystem, process, + environment, and network policy, given that traversal confinement alone does + not survive concurrent filesystem mutation (#227)? +6. How does a later invocation select and resume the same workflow run and + stage without hidden transcript state (#298)? +7. What does a planning loop that reaches `max` without a passing verdict do — + return the failing plan, fail the stage, or return to the user (#290)? An + exhausted loop is not a failure and produces no diagnostic, so the answer is + the document's policy to state, and it is not stated yet. + +The first implementation need not answer every question. It establishes one +observable, testable path and leaves explicit follow-up issues for the rest. diff --git a/specs/markdown-agents-vision.md b/specs/markdown-agents-vision.md index 43f7c118..9c33210b 100644 --- a/specs/markdown-agents-vision.md +++ b/specs/markdown-agents-vision.md @@ -178,6 +178,80 @@ Structured distillation is preferred when later control flow depends on the result. Free-form summaries are useful context, but they are not substitutes for validated workflow state. +## Workflow-owned development artifacts + +A development workflow owns its material environment and logical run state. +Worktrees, working directories, captured handoffs, implementation plans, +feedback, decisions, branches, and pull requests do not belong to whichever +agent happened to create them. The document captures or resolves those assets +deterministically and passes required content into agent prompts explicitly. + +`` and `` already perform such operations. `` (#289), +`` (#293), and `` (#295) describe the rest and are not +built. Together they cover the work that should not depend on model judgment: + +- record artifact versions of handoffs, plans, reviews, and decisions; +- create or resolve a named workspace, branch, file, or pull request only when + that environmental asset is needed; +- establish the working directory inherited by child operations; +- read and write exact artifact content; +- return paths, commit identities, pull-request numbers, and URLs as workflow + data; +- reconcile existing state when an execution resumes; and +- record the inputs, observed state, effects, and outputs of each operation. + +Agent calls analyze evidence and propose changes. Deterministic components apply +approved environmental changes and provide exact required content to the next +call. Generated files are optional exports rather than the handoff protocol. +This removes manual copying between agent-owned transcripts, plan files, and +working directories. + +That run state is scoped to the operation that owns it: created inside the run +it describes, provided contextually, and torn down with it. Nothing accumulates +runs in a module-scoped registry, so concurrent runs cannot observe each other. + +Resources clean up with their enclosing execution by default. Agent sessions, +processes, streams, and other ongoing effects always stop. An execution may +explicitly retain a workspace for inspection. A failed or cancelled execution +also retains a workspace when cleanup would discard uncommitted or unpushed +changes, and reports the path, branch, and reason that recovery is required. +Durable published results such as commits, issues, and pull requests remain +addressable after scoped resources close. + +## Living workflows with `xmd play` + +`xmd run` executes a fixed document. `xmd play` treats the document as a living +collaborative workspace: + +```sh +xmd play workflow.md +``` + +The executable document, rather than a hidden conversation, is the shared source +of workflow intent and progress. Agents propose visible document changes or new +executions. The runtime validates proposals, enforces policy, and performs +deterministic effects. The user approves material changes and remains the final +authority for product behavior, scope, architecture, risk, and lasting +constraints. + +An accepted proposal becomes an inspectable document revision. Rejected +proposals, failed executions, reviewer rejections, and later successful attempts +retain their provenance so the engineering history explains how the workflow +changed. Hidden session history may help an agent reason, but it is never the +only source of consequential workflow state. + +Named agent sessions remain scope-owned while Play is active. Each invocation +receives explicit workflow context and references to workflow-owned artifacts. +The document and execution record identify what each agent received, what it +proposed, what the runtime applied, and which user decision authorized a +material transition. + +Play rests on the same deterministic asset and agent orchestration needed by an +automated implementation loop. The loop is the proving ground for worktree, +file, pull-request, review, decision, cleanup, and recovery semantics. Play adds +collaborative document evolution after those operations are reliable; it does +not replace them with agent-managed shell work. + ## Foundation and agent layer Executable.md separates two concerns: @@ -222,6 +296,10 @@ its declared props: 6. What happens when it fails or returns invalid output? 7. Why did the workflow take a branch or stop? 8. What evidence in the execution record supports those answers? +9. Which environmental assets did the workflow create or resolve, and who owns + their cleanup or retention? +10. In Play, what document change was proposed, what effects were validated, and + which user decision accepted it? If those answers depend on hidden host behavior, implicit transcript sharing, or an agent's own account of what it did, the design does not satisfy the product diff --git a/workflows/adversarial-implementation/Discovery.md b/workflows/adversarial-implementation/Discovery.md new file mode 100644 index 00000000..e42fed1f --- /dev/null +++ b/workflows/adversarial-implementation/Discovery.md @@ -0,0 +1,63 @@ +--- +required: [instructions, planner] + +props: + instructions: { type: string } + planner: { type: string } +--- + +# Discovery + +The workflow enters through design discovery or a bounded deferred issue. +Discovery includes a user-planner interview. A sufficiently specified deferred +issue may enter directly at implementor planning. + +## Target shape + + + + + Repository instructions: + + {props.instructions} + + User request: + + + + Produce a user-validated design handoff and a falsifiable implementation + theory. Distinguish user decisions from hypotheses the implementor must + test. + + + + +{handoff} + +## Handoff contents + +- Purpose and observable behavior +- User decisions, constraints, non-goals, and accepted risks +- Repository and architectural context +- Falsifiable implementation theory +- Assumptions to confirm or refute +- Required evidence and validation +- Likely pull-request topology +- Decisions that remain with the user + +The component declares no `returns`, so its `` region is its return +value and a caller's `as` binds that rendered text. `` +selects the agent from a validated prop rather than a literal: the agent +components take their props from a literal or from an expression that resolves +to a string. An expression prop reads the bare binding, while the prompt body +above interpolates `{props.instructions}` — the two spellings that #305 will +unify. Its caller supplies the request as content and decides whether and where +to persist the result. The handoff is a theory for investigation, not an +implementation plan that the implementor follows unquestioningly. + +The prompt sits outside ``, so it runs under the `throw` error mode: +`throwOnError` turns a failed prompt into a failure the mode then ends the +stage on. Without it a failed prompt records its failure and returns its text, +and the stage would hand its caller an empty handoff. + +This component runs today. diff --git a/workflows/adversarial-implementation/Implementation.md b/workflows/adversarial-implementation/Implementation.md new file mode 100644 index 00000000..9cea4ecc --- /dev/null +++ b/workflows/adversarial-implementation/Implementation.md @@ -0,0 +1,330 @@ +--- +required: [plan, authorization, instructions, planner, implementor] + +props: + plan: { type: string } + authorization: { type: string } + instructions: { type: string } + planner: { type: string } + implementor: { type: string } +--- + +# Implementation + +Implementation begins only after the user authorizes the converged plan. The +implementor edits worktree files; deterministic operations own Git metadata and +remote effects. + +## Target shape + + +```json +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "changedFiles": { + "type": "array", + "items": { "type": "string" } + }, + "commitMessage": { "type": "string", "minLength": 1 }, + "report": { "type": "string" } + }, + "required": ["changedFiles", "commitMessage", "report"], + "additionalProperties": false +} +``` + + + +```json +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "passed": { "type": "boolean" }, + "review": { "type": "string" }, + "revisionPrompt": { "type": "string" }, + "findings": { + "type": "array", + "items": { + "type": "object", + "properties": { + "disposition": { + "type": "string", + "enum": ["fix", "insert-repair", "defer", "reject"] + }, + "title": { "type": "string" }, + "description": { "type": "string" }, + "evidence": { + "type": "array", + "items": { "type": "string" } + } + }, + "required": [ + "disposition", + "title", + "description", + "evidence" + ], + "additionalProperties": false + } + } + }, + "required": ["passed", "review", "revisionPrompt", "findings"], + "additionalProperties": false +} +``` + + + + + + + Repository instructions: + + {props.instructions} + + Authorized plan: + + {props.plan} + + Authorization record: + + {props.authorization} + + Result contract: + + {implementationSchema} + + Implement the authorized plan in the current working directory. Report + changed files, validation, and newly discovered scope. Do not modify + shared Git metadata. Return only JSON matching the supplied result + contract. + + + + + {implementationCandidate} + + + + + + + Correct your previous response without changing its meaning. + Do not use tools, modify files, or perform additional analysis. + + Previous response: + + {implementationCandidate} + + Validation errors: + + + - {error.instancePath}: {error.message} + + + Result contract: + + {implementationSchema} + + Return only corrected JSON. + + + + + + + {implementationCandidate} + + + + + + + + + + + Repository instructions: + + {props.instructions} + + Authorized plan: + + {props.plan} + + Authorization record: + + {props.authorization} + + Pull request: + + {pullRequest} + + Result contract: + + {pullRequestVerdictSchema} + + Review the pull request against the authorized plan and instruction + content above. Classify every finding and include a focused revision + prompt when the review fails. Return only JSON matching the supplied + result contract. + + + + + {verdictCandidate} + + + + + + + Correct your previous response without changing its meaning. + Do not use tools or perform additional analysis. + + Previous response: + + {verdictCandidate} + + Validation errors: + + + - {error.instancePath}: {error.message} + + + Result contract: + + {pullRequestVerdictSchema} + + Return only corrected JSON. + + + + + + + {verdictCandidate} + + + + + + + + + + + + ## Authorized plan + + {props.plan} + + ## Pull request + + {pullRequest} + + ## Planner review + + Passed: {verdict.passed} + + {verdict.review} + + + ### {finding.title} + + Disposition: {finding.disposition} + + {finding.description} + + + + + + + Revise the implementation using this review: + + {verdict.review} + + Focused revision prompt: + + {verdict.revisionPrompt} + + User involvement record: + + {reviewCheckpoint} + + + + + + + # Implementation result + + ## Pull request + + {pullRequest} + + ## Planner review + + Passed: {verdict.passed} + + {verdict.review} + + ## Findings + + + ### {finding.title} + + Disposition: {finding.disposition} + + {finding.description} + + + +The agent, parsing, and control-flow syntax in this component runs today, on +the same terms as `Planning`: the body outside `` runs under the +`throw` error mode, so the final `` in each repair loop ends the stage +rather than passing malformed data to a durable effect. + +`` (#294), `` (#295), and `` (#296) do not exist, +and reconciling those durable GitHub effects idempotently is #297. Until they +land, the loop above cannot expand at all — the three names resolve to nothing, +which is the unresolved printed error, so the missing capability is inside the +stage rather than only around it. The effects they stand for remain explicit +user-run steps between manual stages. + +The surrounding workflow will record each parsed implementation result, commit +and pull-request handle, planner verdict, and user-checkpoint result as artifact +versions (#291). Only implementation source files are written into the worktree. + +``, `agent={planner}` and the other expression props +read bare bindings; the prompt bodies interpolate `{props.plan}` and +`{props.instructions}`. #305 unifies the two spellings. + +## Finding dispositions + +1. Fix in the current pull request. +2. Insert a focused repair needed by the remaining pull-request chain. +3. Create a provenance-linked issue when immediate work would derail the chain. +4. Reject an unsupported, unrelated, or intentionally excluded finding. + +The planner proposes a disposition and asks the user when scope, urgency, or +impact remains uncertain. The user makes the final decision. diff --git a/workflows/adversarial-implementation/InstructionFiles.md b/workflows/adversarial-implementation/InstructionFiles.md new file mode 100644 index 00000000..a7cbfc7c --- /dev/null +++ b/workflows/adversarial-implementation/InstructionFiles.md @@ -0,0 +1,29 @@ +--- +required: [paths] + +props: + paths: + type: array + items: + type: string +--- + +# Instruction Files + +This component renders the exact path and content of every instruction file +selected by the calling workflow. + +It runs today. ``, ``, and a self-closing `` are all +shipped, and `paths` is the `string[]` a `` invocation bound. The `in` +prop reads the bare binding rather than `props.paths`, which is the expression- +prop spelling current main supports (#305). + + + + +## `{instructionPath}` + + + + + diff --git a/workflows/adversarial-implementation/Planning.md b/workflows/adversarial-implementation/Planning.md new file mode 100644 index 00000000..7d85a82b --- /dev/null +++ b/workflows/adversarial-implementation/Planning.md @@ -0,0 +1,215 @@ +--- +required: [handoff, handoffCheckpoint, instructions, planner, implementor] + +props: + handoff: { type: string } + handoffCheckpoint: { type: string } + instructions: { type: string } + planner: { type: string } + implementor: { type: string } +--- + +# Planning + +The implementor and planner are equally capable of analysis. The implementor +tests the handoff's theory; the planner tests the resulting plan. Evidence +resolves factual disagreement, while the user resolves material choices. + +## Target shape + + +```json +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "passed": { "type": "boolean" }, + "review": { "type": "string" }, + "revisionPrompt": { "type": "string" } + }, + "required": ["passed", "review", "revisionPrompt"], + "additionalProperties": false +} +``` + + + + + Repository instructions: + + {props.instructions} + + Planner handoff: + + {props.handoff} + + User involvement record: + + {props.handoffCheckpoint} + + Investigate the current working directory. Confirm, refute, or amend the + implementation theory with evidence. Do not modify the repository. Return a + concrete implementation plan with the evidence, validation, effects, and + pull-request boundaries described by this workflow. + + + + + + Repository instructions: + + {props.instructions} + + Planner handoff: + + {props.handoff} + + User involvement record: + + {props.handoffCheckpoint} + + Implementation plan: + + {plan} + + Result contract: + + {verdictSchema} + + Review the plan against the handoff, recorded user response, and + repository evidence. Include a focused revision prompt on failure. + Return only JSON matching the supplied result contract. + + + + + {verdictCandidate} + + + + + + + Correct your previous response without changing its meaning. + Do not use tools or perform additional analysis. + + Previous response: + + {verdictCandidate} + + Validation errors: + + + - {error.instancePath}: {error.message} + + + Result contract: + + {verdictSchema} + + Return only corrected JSON. + + + + + + + {verdictCandidate} + + + + + + ## Implementation plan + + {plan} + + ## Planner review + + Passed: {verdict.passed} + + {verdict.review} + + Revision prompt: + + {verdict.revisionPrompt} + + + + + + Revise the implementation plan using this review: + + {verdict.review} + + Focused revision prompt: + + {verdict.revisionPrompt} + + User involvement record: + + {planCheckpoint} + + + + + + + # Implementation plan + + {plan} + + ## Planner review + + Passed: {verdict.passed} + + {verdict.review} + + +The loop is bounded and records why it stopped. `` journals every +iteration it enters and one terminal record whose outcome is `break` — the +passing verdict above — `exhausted`, or `error`, and it refuses a replay whose +stored outcome or iteration count disagrees with what this run reached. +`` opens no binding scope, so `plan`, `verdict`, and `planCheckpoint` +hold their final values in the `` region above. + +The body outside `` runs under the `throw` error mode, which is what +makes the bounded repair turns a real gate: `` absorbs a malformed +verdict so the document can show the correction prompt, and the final `` +ends the stage if the candidate is still invalid. `throwOnError` on each +`` is required for the same reason — a failed prompt without it records +its failure and returns its text, raising nothing. + +**Outstanding gap: exhaustion is unhandled.** Reaching `max` completes the loop +normally — exhaustion is not a failure and produces no diagnostic — so whether +an exhausted planning loop counts as converged is this document's policy to +state, and this document does not yet state it. The body has no branch after +``, so a plan that never passed review falls straight into the same +`` as a plan that did. The output above therefore reports +`verdict.passed` rather than titling itself converged, but reporting the flag is +not deciding what should happen. What the workflow does when five rounds end +without a passing verdict — return the failing plan, fail the stage, or return +to the user — is an unresolved product decision recorded against +[issue #290](https://github.com/taras/executable.md/issues/290), whose +acceptance pins the behavior. This synchronization slice does not choose it. + +The surrounding workflow will record every `plan`, `verdict`, and +`planCheckpoint` as an artifact version under its loop-iteration identity. That +artifact ledger does not exist yet (#291), and neither does a `cancelled` loop +outcome — workflow-level cancellation and stop reasons belong to `` +(#289). The component does not create handoff files either way. + +`agent={implementor}` and `` read the bare binding, which +is the expression-prop spelling current main supports, while the prompt bodies +interpolate `{props.instructions}` and `{props.handoff}`; #305 unifies the two. + +Everything in this component's body runs today. diff --git a/workflows/adversarial-implementation/UserCheckpoint.md b/workflows/adversarial-implementation/UserCheckpoint.md new file mode 100644 index 00000000..4391b0cf --- /dev/null +++ b/workflows/adversarial-implementation/UserCheckpoint.md @@ -0,0 +1,210 @@ +--- +required: [purpose, agent] + +props: + purpose: { type: string } + agent: { type: string } +--- + +# User Checkpoint + +This authored component asks its supplied agent whether a transition contains a +material choice. It obtains the user's answer when needed; it never resolves +that choice on the user's behalf. + +## Target shape + + + +```json +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "requiresUser": { "type": "boolean" }, + "assessment": { "type": "string" }, + "question": { "type": "string" }, + "options": { + "type": "array", + "items": { "type": "string" } + }, + "recommendation": { "type": "string" } + }, + "required": [ + "requiresUser", + "assessment", + "question", + "options", + "recommendation" + ], + "additionalProperties": false +} +``` + + + + +```json +{ + "type": "object", + "properties": { + "proceed": { + "type": "boolean", + "title": "Continue the workflow" + }, + "response": { "type": "string" }, + "rationale": { "type": "string" } + }, + "required": ["proceed", "response", "rationale"], + "additionalProperties": false +} +``` + + + + + + Determine whether the user must be involved to {props.purpose}. + + Material to assess: + + + + Result contract: + + {assessmentSchema} + + Require user involvement for choices affecting behavior, scope, + architecture, risk, pull-request decomposition or sequencing, and lasting + constraints. Do not require it for reversible implementation details + within an already authorized plan. If uncertain, require verification. + + Explain the material choice, viable options, consequences, evidence, and + recommendation. Do not choose for the user. Return only JSON matching the + supplied result contract. When involvement is unnecessary, return an + empty question and options list. + + + + + {candidate} + + + + + + + Correct your previous response without changing its meaning. + Do not use tools or perform additional analysis. + + Previous response: + + {candidate} + + Validation errors: + + + - {error.instancePath}: {error.message} + + + Result contract: + + {assessmentSchema} + + Return only corrected JSON matching the supplied result contract. + + + + + + + {candidate} + + + + + + + {assessment.question} + + Options: + + + - {option} + + + Recommendation: {assessment.recommendation} + + + + +## User involvement assessment + +{assessment.assessment} + +Recommendation: {assessment.recommendation} + + +Question: {assessment.question} + +Options: + + +- {option} + + +## User response + +Proceed: {elicitation.proceed} + +Response: {elicitation.response} + +Rationale: {elicitation.rationale} + + + +`UserInvolvementAssessment` distinguishes whether involvement is required from +the choice itself. The caller renders the complete material to assess as child +content rather than asking the agent to locate or read it. + +`` asks without choosing how. It requires `schema` and `as`, compiles +the schema before its content expands, renders that content as the request +message, and validates the provider's answer against the same compiled schema +before binding it. There is no `mode`, `provider`, or `uiSchema` prop and no +built-in approve, decline, or cancel — `elicitationSchema` above defines every +response available. Where the asking happens is the host's decision, made +through the Elicitation Api: `xmd run` composes WebForm as its current +provider, so this checkpoint opens a loopback browser form under the CLI. +Only the validated answer is journaled, keyed by a fingerprint of the compiled +schema and the rendered message, so a resumed run restores the answer instead +of asking twice and refuses a recorded answer whose question does not match. +A document that already knows the answer — a test, a demo, a non-interactive +region — wraps this component in an `` region and supplies it with +`` matchers, which changes who answers without changing this file. + +What `` does not solve is stopping between processes. It answers a +question inside one run; selecting and resuming a stopped stage in a later +invocation is cross-process continuation, which belongs to `` and +`` (#298, #289) and does not exist yet. + +`` exposes validation failures as data so the document can show the +repair turn explicitly. It absorbs JSON syntax and schema-validation failures +and nothing else: an unusable schema still fails, and a child execution failure +propagates unchanged. Its failure shape is `{ ok: false, input, errors }` and +preserves the rendered input exactly, which is what lets the correction prompt +quote what the agent actually said. The final `` prevents the workflow +from continuing after the bounded repair loop with malformed output. The schema +is ordinary captured document content rather than a registry entry or +`` prop. + +Every construct above sits outside ``, so the whole assessment runs +under the `throw` error mode: the final `` failing ends the stage rather +than printing an error into a region nobody reads. The `` region itself +runs under `output` and is equally fail-fast, keeping only what it had rendered +before any failure. + +`` reads the bare binding because that is the expression- +prop spelling current main supports, while `{props.purpose}` in the prompt body +is text interpolation; #305 unifies the two. + +This component runs today. diff --git a/workflows/adversarial-implementation/artifacts.md b/workflows/adversarial-implementation/artifacts.md new file mode 100644 index 00000000..ac4f8329 --- /dev/null +++ b/workflows/adversarial-implementation/artifacts.md @@ -0,0 +1,128 @@ +# Artifacts and Structured Results + +Every consequential input, result, decision, and durable effect is explicit +workflow data. Agent session history may assist reasoning but is not the only +record of a decision. + +## The artifact ledger + +The artifact ledger is not implemented. This section describes what +`` must do once it exists (#291). + +Artifact versions live in Git objects reachable from `refs/xmd/runs` — sidecar +Git history. They remain in the repository without appearing in the main source +tree or source history. Each run records the pinned source revision it +investigated. + + + + + +The first exercise uses ordinary Git commands to create, update, push, and +fetch this history. Objects must remain reachable through a ref. Content is +screened for credentials and other data that must not become durable — the +default-on execution policy for that screening is #199. + +`` derives the ledger's entries from the execution record. It records +the pinned source revision, component and loop-iteration identity, named +captures, props, file effects, agent results, user decisions, Git and GitHub +effects, outcome, and stop reason. Each captured result becomes an immutable +artifact version with a content hash. It appends when a stage or loop iteration +completes and writes a terminal record when execution succeeds, fails, or is +cancelled. + +Part of that identity already exists in the execution journal. `` records +every iteration it enters and one terminal `break`, `exhausted`, or `error` +outcome, and refuses a replay that disagrees with what this run reached. Each +`` is one durable operation carrying its identity, input, agent and +session, terminal status, text, and structured failure, and `` +journals its validated answer keyed by a fingerprint of the compiled schema +and the rendered message. Those are execution records rather than the artifact +ledger: what is missing is one run identity that correlates them, artifact +versions on top of them, and persistence outside the process. + +When a later stage resumes the same run, `` restores its declared +inputs from those recorded values. Required workflow context is rendered +directly into the next prompt. The agent is not asked to locate or read a +generated handoff file, so prompt construction does not depend on tool use, +working directory, permissions, or mutable filesystem content. + +Persisting a generated file does not itself reduce model tokens: reading the +file adds its content to context and normally adds tool-call overhead. Files +remain useful as explicit user exports or when an external tool requires a +path, but they are derived views rather than canonical run state. + +## Structured results + +A component has one return path, defined by the +[executable MDX specification](../../specs/executable-mdx-spec.md). A Markdown +component that declares no `returns` is a **text component**: its rendered +Markdown is its return value, `` selects which region renders, and `as` +binds that text. Every stage component in this workflow is one. A component +that declares `returns` is a **value component**: it renders nothing, holds +exactly one direct top-level ``, must be invoked with `as`, +and binds one JSON value validated against its schema. The two are mutually +exclusive — `` in a component that declares `returns` is a structural +error. + +A registered function component that declares no `returns` binds **by +reference**: `as` binds the object the component returned rather than a +rendering of it, which is how a component hands its caller something a schema +could not describe. Such a binding is not durable — nothing is journaled for +it, and a re-expansion recomputes it by running the component again. + +`returns` is the opt-in that makes a particular return a **validated JSON +record** instead. The produced value crosses the JSON boundary before its +schema, validation runs against a clone so defaults fill without mutating the +producer's object, and only that normalized clone reaches the caller. `` +declares `returns`, so the `string[]` this workflow binds is that validated +clone rather than a by-reference binding. + +A component declaring `returns` must be invoked with `as`, because it renders +nothing. Without `as` there is nowhere to bind, so only text is observable: a +component returning a string renders it, and a component returning anything +else renders nothing — not an error, a value with no destination. + +### What a failing stage returns + +Nothing partial. A stage's `` region runs under the `output` error mode +and everything outside it runs under `throw`, so an undecided error at either +position fails the run rather than producing a result the caller would bind. A +failing `` region keeps only the text it had already rendered, and that +text reaches the output stream; nothing after the failure does. A failed run is +still a complete record — replay restores its output and its failure without +re-executing anything. + +### Logical result contracts + +The following names describe the JSON these agents produce. They are labels for +shapes these documents declare inline, not component return declarations, not +entries in a schema registry, and the workflow does not assume a +`` prop. Each name is used for exactly one shape, here and in +the stage that produces it. + +- `PlannerHandoff` separates user decisions from implementation hypotheses. +- `ImplementationPlan` records confirmed and refuted assumptions, evidence, + validation, environmental effects, and pull-request boundaries. +- `PlannerVerdict` records pass/fail state, evidence, user questions, and a + focused revision prompt. +- `ImplementationResult` records changed files, proposed commit metadata, + validation, and newly discovered scope. +- `PullRequestVerdict` records findings, dispositions, evidence, user + questions, and a focused revision prompt. +- `UserInvolvementAssessment` records whether involvement is required, the + material choice, viable options, consequences, evidence, and recommendation. +- `UserDecision` records the request, options, selection, rationale, actor, and + time. + +Prompt output used for control flow is JSON parsed against captured draft-07 +JSON Schema content. `` exposes the candidate and normalized errors +for a visible, bounded correction turn; a final `` prevents invalid data +from reaching control flow or deterministic effects. Prose capture remains +acceptable when no later transition depends on internal fields. + +Parsing content inside a document and declaring a component's return value are +separate mechanisms, and both are shipped. A stage component could declare +`returns` and hand its caller the parsed verdict directly instead of rendered +text; these documents keep the text form because each stage's output is also +material a user reads at a checkpoint. diff --git a/workflows/adversarial-implementation/primitives.md b/workflows/adversarial-implementation/primitives.md new file mode 100644 index 00000000..30d23341 --- /dev/null +++ b/workflows/adversarial-implementation/primitives.md @@ -0,0 +1,237 @@ +# Primitive Inventory + +## XMD execution foundation + +XMD already supplies component expansion, root document props, prompt capture, +agent selection, named sessions, collection iteration, scoped permission +policies, and scope-owned process and agent teardown. Its durable execution +layer also assigns deterministic execution identities, journals effects, and +observes completion, failure, or cancellation. Replay restores a recorded +outcome without re-executing it, and a run that failed is still a complete +record: replaying it restores the output and the failure alike. Replay +determinism means the journal does not lose the execution chain — replaying +arrives at the same state, where execution can resume. + +Those capabilities remain internal execution machinery. The low-level journal +is not the user-facing artifact ledger of an implementation workflow. + +## How a name resolves + +Name resolution has tiers, and the first tier that answers wins: + +1. **structural syntax** — the language's own constructs; +2. **a reserved registration** — a host protecting a language or security + invariant; +3. **a repository-local file**; +4. **a registered default**, including everything core supplies; and +5. **nothing**, which is the unresolved printed error. + +Two consequences govern what this workflow may rely on. A **repository +component overrides any ordinary package default**, core's own included — so +``, ``, ``, ``, ``, ``, ``, +and every other registered default sits *below* a repository file of the same +name, and a repository `Elicit.md` is chosen ahead of core's. Only genuine +absence falls through to a default: a candidate that exists but cannot be read, +imported, parsed, or compiled fails where it is loaded rather than being +quietly replaced. **Structural names are reserved**, so a registration cannot +claim one and a repository file never stands in for it. + +**Registration is scope-local.** `registerComponents()` makes names resolvable +for the installing scope and its descendants. A child scope may register a name +its parent already registered — that shadows, and the parent is unchanged. +Siblings and concurrent executions never see one another's registrations, and +leaving the installing scope removes them. Registering describes a component; it +runs nothing and acquires nothing, and names and schemas are validated where +they are installed rather than the first time a document writes the name. Two +registrations for one name and kind at the same scope are a configuration error +naming both origins; installation order is not a resolution mechanism. This is +the general rule that all engine state is scoped to the operation that owns it: +created inside the run it describes, provided contextually, and torn down with +it. There is no module-scoped registry for this workflow to reach. + +## How an error is decided + +Every region of every document carries an error mode, set by the lexical +structure and read where an error is raised: + +| Mode | An undecided error… | Installed by | +| --- | --- | --- | +| `print` | is printed into the document; the run continues | the root; `` | +| `output` | fails the run; the region keeps what it already rendered | every `` region | +| `throw` | fails the run, and no printing boundary replaces it | documentation; value roots | + +A component body is split by its `` boundary: the region inside runs +under `output`, everything outside is documentation and runs under `throw`. +Every stage in this workflow puts its prompts, parsing, and control flow outside +``, so a stage returns a complete validated result or it fails — it +never returns a half-record. `` and `` would let a document +handle a failure instead of ending on it; both are defined and unbuilt. + +## What the workflow already writes + +**Structural syntax** is the language's own. A registration cannot claim one of +these names and a repository file never stands in for it: + +1. `` with an optional nested ``, and `` with ``, provide + visible bounded control flow. `` requires `max`, opens no binding + scope, and completes normally when it reaches that bound. +2. `` and `` supply elicitation responses from the document. + `` installs a provider around its body and answers from its + matchers; it reads them as elements before they expand, which is why a + registered component could not implement it. +3. `` selects a value component's return value. +4. ``, ``, ``, ``, and `` complete + the set. `` accepts no props: it names a region, sets `print` + for it, and turns a failure that reaches it into one printed error whose + `cause` is the complete original failure. `throw` is the one mode it does not + replace, so it cannot rescue a stage's documentation. + +**Core defaults** ship in the compiled binary and every published package, with +no search path and no `--component-dir`. They are ordinary defaults rather than +reserved names, so a repository component may override each one: + +5. `` evaluates explicit include and exclude patterns relative to + `Env.cwd`. It declares `returns`, so it renders nothing, must be invoked + with `as`, and binds one `string[]` validated against a clone of what it + produced rather than a by-reference binding: relative paths, `/`-separated + on every platform, deduplicated, and sorted lexically by code point. + Directories and symbolic links are never results, which is what keeps a + search inside `Env.cwd` without judging any destination. Finding nothing is + an empty array rather than a failure. +6. `` reads or writes UTF-8 text relative to `Env.cwd`. Self-closing it + reads and renders the file's exact content, and `as` captures that text. + Written with content it expands its children, atomically replaces the target, + and renders nothing at all — no output, no path, no write handle. Everything + it touches stays inside `Env.cwd`, checked lexically before any filesystem + call and again against resolved symlinks immediately before the write. That + is traversal confinement, not a security sandbox: containment that does not + depend on observed filesystem state is [issue + #227](https://github.com/taras/executable.md/issues/227). +7. `` renders its children, decodes the result as JSON, validates it + against a draft-07 schema supplied as captured text or as a structured + value, and binds the validated value through `as`. `` performs + the same deterministic work but binds either `{ ok: true, value }` or + `{ ok: false, input, errors }`, preserving the rendered input exactly so a + corrective prompt can quote what was said. Both require `schema` and `as`, + render nothing, and compile the complete schema before their children + expand. `` absorbs JSON syntax and schema-validation failures and + nothing else: an unusable schema still fails, and a child execution failure + propagates unchanged. Validation judges the value and never edits it — no + default is inserted, no type coerced, no undeclared property removed. + Neither component repairs content; repair is written in Markdown where a + reader can see it. Only references contained within the supplied schema + resolve, and an external `$ref` fails at compilation ([issue + #192](https://github.com/taras/executable.md/issues/192)). +8. `` renders its children as the request message, requires a `schema` + that defines the exact fields and options available to the user, and binds + the validated response through `as`. The schema compiles first, the content + expands second, and the provider is asked third; that order is the contract. + There is no `mode`, `provider`, or `uiSchema` prop and no built-in approve, + decline, or cancel — the schema defines every available response, and + cancelling execution stays an Effection lifecycle event unless the document + models it as schema data. Where the asking happens is the host's decision, + made through the Elicitation Api: `xmd run` composes WebForm as its current + provider, and changing the provider changes no Markdown. Only the validated + answer is journaled, keyed by a fingerprint of the compiled schema and the + rendered message, so a resumed run restores it rather than asking twice and + refuses an answer recorded against a different question. +9. `` establishes a fresh contextual working directory for its content + and removes it when the content finishes, fails, or is cancelled. + +**Registered agent components** are defaults on the same terms. +`installAgentComponents()` registers them for the installing scope, and a +repository `Prompt.md` or `Agent.ts` outranks them: + +10. `` and `` pin an agent and a session onto nested + prompts; `` sends one prompt and renders the reply, with `agent`, + `session`, and `timeout` overriding the enclosing scope. `throwOnError` + turns a failed prompt into a failure the enclosing error mode then decides; + without it a failed prompt records its failure and returns its text, so + nothing is raised and the stage carries on with an empty reply. Their props + take a literal or an expression that resolves to a string, so this workflow + selects the planner and implementor from validated root props rather than + literals. Each prompt is one durable operation whose record carries its + identity, input, agent and session, terminal status, text, and structured + failure. + +**One asymmetry to know.** An expression prop reads a **bare** binding, while +text and content interpolation read the **namespace**: `agent={planner}` and +`{props.instructions}` are both correct today, and `agent={props.planner}` fails +with `props is not defined`. Removing that split is [issue +#305](https://github.com/taras/executable.md/issues/305), which will let +expression props read `props.name` and migrate these documents. + +The document-level logic in `InstructionFiles`, `Discovery`, `Planning`, and +`UserCheckpoint` therefore uses shipped syntax throughout. `Implementation` does +not: its loop body invokes ``, ``, and ``, which +resolve to nothing, so that stage cannot expand. + +## What the workflow still needs + +`` organizes existing XMD behavior into an authored process. It +correlates captures and deterministic effects, presents durable replay as one +run reaching the state a later stage resumes from, and derives the run outcome +from XMD's execution result. None of it exists yet ([issue +#289](https://github.com/taras/executable.md/issues/289)). + +The component adds the product behavior that XMD does not supply: + +- create or resolve a stable run identity without exposing a `runId` prop; +- resolve `base` once to a pinned source revision; +- install run identity and pinned source revision through a contextual Run API; +- restore a stage's declared inputs by stable component and loop-iteration + identity; +- record artifact versions, environmental effects, and stop reasons; +- reconcile external effects with the run identity; and +- persist the artifact ledger when `historyRef` selects a sidecar location + (#291). + +Descendants consume this context internally. Authors access the run identity +through the API only when workflow logic genuinely needs it. Every workflow has +an execution-local run manifest; `historyRef` makes that record durable outside +the executing process. + +The remaining contracts are missing on the same terms: + +1. `` selects one stage, restores its declared inputs, publishes its + outputs, and stops cleanly at the stage boundary + ([#298](https://github.com/taras/executable.md/issues/298)). Cross-process + continuation is its problem rather than ``'s: elicitation answers a + question inside one run and does not resume a stopped one. +2. `` enforces filesystem, environment, process, network, and durable + effect capabilities as a boundary that can be relied on + ([#302](https://github.com/taras/executable.md/issues/302)). `` and + `` confine traversal today, but confinement that survives concurrent + filesystem mutation is [issue + #227](https://github.com/taras/executable.md/issues/227). +3. `` reconciles a workspace from the run's pinned source revision, + sets `Env.cwd` while rendering its children, and cleans up safely + ([#293](https://github.com/taras/executable.md/issues/293)). This workflow + keeps discovery, implementor planning, implementation, and review in that + same workspace. `Env.cwd` itself is implemented; what is missing is the + workspace that establishes it and the retention rules that survive failure. +4. `` validates exact changes and owns Git metadata writes + ([#294](https://github.com/taras/executable.md/issues/294)). +5. `` ([#295](https://github.com/taras/executable.md/issues/295)) + and `` ([#296](https://github.com/taras/executable.md/issues/296)) + reconcile durable GitHub effects idempotently, over the shared + reconciliation described by + [#297](https://github.com/taras/executable.md/issues/297). +6. Replay across replaced ephemeral environments, so a recorded effect is never + restored under a directory the current run did not create ([issue + #218](https://github.com/taras/executable.md/issues/218)). +7. Default-on rejection of secrets before a journal event or a sidecar Git + object becomes durable ([issue + #199](https://github.com/taras/executable.md/issues/199)). The pre-persistence + guard and the offline scanner are built; the execution policy, its default-on + wiring, and the CLI opt-out and warning are not. + +``, ``, ``, and `` are +authored Markdown components, not runtime primitives. `` +combines an agent prompt, conditional control flow, and `` to determine +whether a material choice requires the user and to obtain the user's answer +when it does. + +Where a primitive is still missing, the manual exercise replaces it with an +explicit user-run step and records the replacement as evidence for prioritizing +implementation. diff --git a/workflows/adversarial-implementation/runtime.md b/workflows/adversarial-implementation/runtime.md new file mode 100644 index 00000000..b98b0f02 --- /dev/null +++ b/workflows/adversarial-implementation/runtime.md @@ -0,0 +1,86 @@ +--- +props: + base: + type: string + default: main +--- + +# Runtime and Isolation + +The workflow's run owns its worktree, artifact ledger, processes, and +deterministic effects. It pins the source revision before creating the +worktree. A worktree isolates Git state from the user's checkout but is not a +security boundary. + +`` (#289), `` (#302), and `` (#293) do not exist +yet. The contextual working directory they depend on does: `Env.cwd` is +implemented and already inherited by ``, ``, exec blocks, and +daemons (#222), and `` already establishes one for its content (#216). + +## Target shape + + + + + + + + + +## Manual exercise + +- Resolve `base` to a pinned source revision before planner discovery and + record it in the artifact ledger. +- Create a disposable worktree from that pinned revision before discovery. +- Keep handoffs and user decisions as restored artifact versions rather than + transfer files within the worktree. +- Let `` set `Env.cwd` while it renders every child. +- Give the planner read and search access. +- Restrict implementor writes to worktree files. +- Do not give the implementor write access to shared Git metadata. +- Deny agent network access for the first deliberately limited feature. +- Let explicit user-run commands perform commits, issues, and pull requests. +- Stop all processes when a stage ends. +- Retain a dirty worktree after failure or cancellation and report its path and + recovery state. + +An enforceable sandbox becomes mandatory before implementation runs +unattended. The manual exercise uses the narrowest host sandbox and permission +policy already available. + +`` will record an artifact version when a stage or loop iteration +completes, and a terminal record on success, failure, and cancellation, then +restore a resumed stage's declared inputs. Authors do not repeat observed +artifact paths or generate transfer files in an explicit checkpoint. + +`` already records part of that: it journals every iteration it enters +and one terminal record whose outcome is `break`, `exhausted`, or `error`, and +it refuses a replay whose stored outcome or iteration count disagrees with what +this run reached. There is no `cancelled` loop outcome, and no stage-stop +record — workflow-level cancellation and stop reasons belong to `` +and ``, which do not exist yet. + +## Loop interruption + +An automated iteration stops on the first applicable signal: + +1. The iteration reaches its defined completion. +2. A configured file is created or updated in a configured directory. +3. The user provides runtime input. + +None of that arbitration is implemented ([issue +#300](https://github.com/taras/executable.md/issues/300)). Signal 3 has a +shipped in-run form: `` asks a person a schema-validated question +during execution, and under `xmd run` the WebForm provider answers it in a +browser. What remains missing is cross-process continuation — stopping at a +stage boundary and resuming in a later invocation — which `` owns rather +than `` (#298). The Effection inspector remains out-of-band +meta-control for exceptional inspection and intervention, not the routine +decision protocol. diff --git a/workflows/adversarial-implementation/start.md b/workflows/adversarial-implementation/start.md new file mode 100644 index 00000000..fac53407 --- /dev/null +++ b/workflows/adversarial-implementation/start.md @@ -0,0 +1,185 @@ +--- +required: [request] + +props: + request: { type: string } + base: { type: string, default: main } + planner: { type: string, default: codex } + implementor: { type: string, default: claude } +--- + +# Adversarial Implementation Workflow + +- **Status:** Living end-goal target +- **Execution:** Manual, one stage at a time + +This entry document is the complete workflow map. The linked files define the +prompts, artifacts, permissions, and deterministic effects used by each stage. +Its component markup is the intended executable form. The three wrappers — +``, ``, and `` — are workflow-owned capabilities +that do not exist yet, and one of the stages they wrap is not expressible +either. "What runs today" below says exactly which. + +Each stage component is a text component: it declares no `returns`, so its +`` region is its return value and the caller's `as` binds that rendered +text. `` is the exception: it declares `returns`, so it renders nothing, +requires `as`, and binds a `string[]` validated against a clone of what it +produced. The [executable MDX +specification](../../specs/executable-mdx-spec.md) is the authority for both. +`` will create a run identity, record those captured results as +artifact versions, and restore them when a later stage resumes (#289, #291); +today each stage's values live only in the executing process. Generated handoffs +are prompt content, not files that an agent must choose to read. Root `props` +supplies `request`, `base`, `planner`, and `implementor` (#179). + +## Complete flow + + + + + + + + {props.request} + + + {handoff} + + + + {plan} + + + + {implementationResult} + + {acceptance} + + + + +`Workflow` will resolve `base` to a pinned source revision before creating the +worktree. Its run identity keeps discovery through implementation on that +pinned filesystem even if the branch moves while execution is in progress. + +## How props are read + +Two spellings, and they are not interchangeable today: + +- **Text and content** read the namespace: `{props.request}` above, and + `{props.instructions}` inside every stage's prompt. +- **Expression props** read the **bare** binding: `planner={planner}`, not + `planner={props.planner}`. A `props.` reference in an expression prop fails + with `props is not defined`. + +Removing that asymmetry is [issue +#305](https://github.com/taras/executable.md/issues/305), whose acceptance +includes expression props reading `props.name`. Until it lands these documents +use the bare spelling in expression props, and #305 migrates them. + +## Error modes in a stage + +Every stage component is split by its `` boundary, and the two halves +fail differently: + +- Everything **outside** `` is documentation and runs under the `throw` + error mode. The first error stops the body and fails the run, and no + `` region can print it instead — which is what makes each + stage's final `` a real gate rather than a formality. +- The **`` region** runs under the `output` error mode: an undecided + error there fails the run too, and the region keeps what it had already + rendered. That partial text reaches the output stream; nothing after the + failure does. + +So a stage either returns a complete, schema-validated result or it fails. It +never returns a half-record. The `throwOnError` on each `` is +load-bearing for the same reason: a failed prompt without it records its +failure and returns its text, raising nothing for the error mode to decide. + +## What runs today + +**Expressible now.** Four of the five authored stages — +[`InstructionFiles`](./InstructionFiles.md), [`Discovery`](./Discovery.md), +[`UserCheckpoint`](./UserCheckpoint.md), and [`Planning`](./Planning.md) — are +written entirely in shipped syntax: ``, ``, ``, +``, ``, ``/``, ``/``, ``, +``, ``, and the ``, ``, and `` agent +components. A caller that already knows an answer wraps a checkpoint in an +`` region instead of reaching a person. + +**Not expressible.** [`Implementation`](./Implementation.md) is the fifth +stage and does not run. Its agent prompts, schema parsing, bounded repair +turns, and control flow are all shipped, but its loop body invokes `` +(#294), `` (#295), and `` (#296), none of which exist. +Those three names resolve to nothing, so the stage cannot expand — the missing +capability is inside the stage, not only around it. + +**Not expressible.** The three wrappers in the flow above — `` +(#289), `` (#302), and `` (#293) — do not exist, and neither +does `` (#298), the artifact ledger in sidecar Git history (#291), or +cross-process continuation (#298). + +So the complete flow is non-executable at two levels: the workflow spine that +pins a source revision, owns a workspace, enforces capabilities, and persists +run state; and the implementation stage's durable Git and GitHub effects. What +can be exercised today is discovery through plan convergence and the user gates +around them, running in one process and one existing working directory, with +commits, pull requests, and issues performed as explicit user-run steps between +manual stages. + +## Rendered data flow + +| Captured value | Produced by | Consumed by | +| ---------------------- | -------------------------- | --------------------------------------------------- | +| `instructionPaths` | `Glob` (`string[]`) | `InstructionFiles` | +| `instructions` | `InstructionFiles` | every agent prompt | +| `handoff` | `Discovery` | handoff `UserCheckpoint`, `Planning` | +| `handoffCheckpoint` | handoff `UserCheckpoint` | `Planning` | +| `plan` | `Planning` | authorization `UserCheckpoint`, `Implementation` | +| `authorization` | authorization checkpoint | `Implementation` | +| `implementationResult` | `Implementation` | acceptance `UserCheckpoint` | +| `acceptance` | acceptance checkpoint | workflow output, terminal record | + +## Details + +- [Runtime and isolation](./runtime.md) +- [Instruction materialization](./InstructionFiles.md) +- [Discovery](./Discovery.md) +- [User checkpoint](./UserCheckpoint.md) +- [Planning](./Planning.md) +- [Implementation](./Implementation.md) +- [Artifacts and structured results](./artifacts.md) +- [Primitive inventory](./primitives.md) + +The governing role, review, pull-request, and deferral contracts remain in the +[workflow specification](../../specs/adversarial-implementation-workflow.md). From 68733d7d5a62345e4a6ab1a5d827324cd33ace0d Mon Sep 17 00:00:00 2001 From: Taras Mankovski <74687+taras@users.noreply.github.com> Date: Tue, 4 Aug 2026 16:29:40 -0400 Subject: [PATCH 2/8] =?UTF-8?q?=F0=9F=93=9D=20Align=20the=20target=20with?= =?UTF-8?q?=20#203=20as=20merged=20and=20restore=20the=20printing=20contra?= =?UTF-8?q?ct?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 `` (#293) is built on. `architecture.md` is authoritative on the error model, so the `output` row reads "fails the run; `` 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 ``, 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 `` is a gate for that reason and not because printing is unavailable. --- specs/adversarial-implementation-workflow.md | 24 ++++++++++++++--- .../adversarial-implementation/primitives.md | 26 ++++++++++++++++--- workflows/adversarial-implementation/start.md | 8 +++--- 3 files changed, 47 insertions(+), 11 deletions(-) diff --git a/specs/adversarial-implementation-workflow.md b/specs/adversarial-implementation-workflow.md index ff617b3b..0959147f 100644 --- a/specs/adversarial-implementation-workflow.md +++ b/specs/adversarial-implementation-workflow.md @@ -217,16 +217,23 @@ stage's output is also material a user reads at a checkpoint. A stage's `` region runs under the `output` error mode; everything outside it is documentation and runs under `throw`. An undecided error at either -position fails the run rather than producing a result a caller would bind, and -no `` region can convert a documentation failure into a printed -error. That is what makes each stage's final `` a gate: malformed agent -output cannot reach control flow or a deterministic effect. +position fails the run rather than producing a result a caller would bind. The +two modes differ in what a printing boundary may do about it: a `` +region can print an `output` decision instead of failing, and `throw` is the one +mode it cannot replace — a printed error in documentation is one nobody can +read. Neither rescues a stage here, because every stage's parsing sits in +documentation. That is what makes each stage's final `` a gate: malformed +agent output cannot reach control flow or a deterministic effect. A failing region keeps only what it had already rendered. That partial text reaches the output stream; nothing after the failure does. Continuing after a failure is always explicit — a bounded repair turn the document shows, not recovery the engine performs on the author's behalf. +Printing an `output` decision is settled contract that the engine has not built +yet: an outer `` currently ends the run instead (#327). No stage +depends on it today. + `throwOnError` on a `` is required for the same reason: without it a failed prompt records its failure and returns its text, raising nothing for the error mode to decide, and the stage would continue with an empty reply. @@ -374,6 +381,15 @@ Resources clean up with their execution by default. Agent sessions, processes, streams, and other ongoing effects always stop before their enclosing scope closes. +Ownership follows the invocation, not the author. Content a caller writes and a +component only projects keeps the caller's bindings, but its live effects belong +to the component invocation and stop before that invocation cleans up its own +(#203). A daemon or a `persist` resource started inside projected content is +signalled while the component's directory still exists, and is gone once the +invocation returns. That ordering is what makes `` safe to build: a +process a stage starts stops before the workspace it ran in is removed, so +cleanup cannot pull the ground out from under a running effect. + An execution may explicitly retain its workspace for inspection. A failed or cancelled execution also retains a worktree when removing it would discard uncommitted or unpushed changes. The execution reports the retained path, branch, diff --git a/workflows/adversarial-implementation/primitives.md b/workflows/adversarial-implementation/primitives.md index 30d23341..f5fc4c69 100644 --- a/workflows/adversarial-implementation/primitives.md +++ b/workflows/adversarial-implementation/primitives.md @@ -4,7 +4,11 @@ XMD already supplies component expansion, root document props, prompt capture, agent selection, named sessions, collection iteration, scoped permission -policies, and scope-owned process and agent teardown. Its durable execution +policies, and scope-owned process and agent teardown. Every component invocation +owns a resource scope: projected content keeps its caller's bindings but its +live effects — daemons, `persist` resources, watchers — belong to the invocation +and stop before it cleans up its own, on success, failure, and cancellation +alike (#203). Its durable execution layer also assigns deterministic execution identities, journals effects, and observes completion, failure, or cancellation. Replay restores a recorded outcome without re-executing it, and a run that failed is still a complete @@ -57,9 +61,12 @@ structure and read where an error is raised: | Mode | An undecided error… | Installed by | | --- | --- | --- | | `print` | is printed into the document; the run continues | the root; `` | -| `output` | fails the run; the region keeps what it already rendered | every `` region | +| `output` | fails the run; `` can print instead | every `` region | | `throw` | fails the run, and no printing boundary replaces it | documentation; value roots | +A failing region keeps what it had already rendered: that text reaches the +output stream, and nothing after the failure does. + A component body is split by its `` boundary: the region inside runs under `output`, everything outside is documentation and runs under `throw`. Every stage in this workflow puts its prompts, parsing, and control flow outside @@ -67,6 +74,15 @@ Every stage in this workflow puts its prompts, parsing, and control flow outside never returns a half-record. `` and `` would let a document handle a failure instead of ending on it; both are defined and unbuilt. +**Missing: printing an `output` decision.** The `output` row above is the +settled contract, and the engine does not meet it yet — an outer +`` around a region that failed under `output` ends the run instead +of printing, whether the failure arose in the region itself or in content +projected into it ([issue +#327](https://github.com/taras/executable.md/issues/327)). Nothing in this +workflow writes ``, so no stage depends on it today; a stage that +wanted to survive a failed region would. + ## What the workflow already writes **Structural syntax** is the language's own. A registration cannot claim one of @@ -208,8 +224,10 @@ The remaining contracts are missing on the same terms: sets `Env.cwd` while rendering its children, and cleans up safely ([#293](https://github.com/taras/executable.md/issues/293)). This workflow keeps discovery, implementor planning, implementation, and review in that - same workspace. `Env.cwd` itself is implemented; what is missing is the - workspace that establishes it and the retention rules that survive failure. + same workspace. `Env.cwd` itself is implemented, and so is the lifetime rule + it composes with: a process a stage starts stops before the invocation that + established the directory cleans up (#203). What is missing is the workspace + that establishes it and the retention rules that survive failure. 4. `` validates exact changes and owns Git metadata writes ([#294](https://github.com/taras/executable.md/issues/294)). 5. `` ([#295](https://github.com/taras/executable.md/issues/295)) diff --git a/workflows/adversarial-implementation/start.md b/workflows/adversarial-implementation/start.md index fac53407..fb9eee77 100644 --- a/workflows/adversarial-implementation/start.md +++ b/workflows/adversarial-implementation/start.md @@ -117,9 +117,11 @@ fail differently: `` region can print it instead — which is what makes each stage's final `` a real gate rather than a formality. - The **`` region** runs under the `output` error mode: an undecided - error there fails the run too, and the region keeps what it had already - rendered. That partial text reaches the output stream; nothing after the - failure does. + error there fails the run too, though a `` region may print it + instead. Either way the region keeps what it had already rendered — that + partial text reaches the output stream, and nothing after the failure does. + Printing an `output` decision is the contract; the engine does not do it yet + (#327). So a stage either returns a complete, schema-validated result or it fails. It never returns a half-record. The `throwOnError` on each `` is From 545fd93a484828689f70bd1239ca3e5af600ebd0 Mon Sep 17 00:00:00 2001 From: Taras Mankovski <74687+taras@users.noreply.github.com> Date: Tue, 4 Aug 2026 17:04:50 -0400 Subject: [PATCH 3/8] =?UTF-8?q?=F0=9F=93=9D=20Make=20the=20user's=20decisi?= =?UTF-8?q?on=20gate=20execution,=20and=20correct=20the=20result=20invento?= =?UTF-8?q?ry?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `` 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. `` now declares `returns` and binds a schema-validated decision — `proceed` plus the assessment fields a later prompt quotes. One `decisionSchema` serves both paths: `` binds it when the assessment reports a material choice, and an explicit `` 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 `` 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. `` is substituted only at a body's top level or directly inside ``, never nested inside a `` (#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 `` branch while a declining answer in scope is never consulted. --- specs/adversarial-implementation-workflow.md | 14 ++ .../adversarial-implementation/Discovery.md | 11 +- .../Implementation.md | 111 ++++++++---- .../adversarial-implementation/Planning.md | 122 +++++++++---- .../UserCheckpoint.md | 108 +++++++----- .../adversarial-implementation/artifacts.md | 82 ++++++--- .../adversarial-implementation/primitives.md | 3 +- workflows/adversarial-implementation/start.md | 162 ++++++++++++------ 8 files changed, 421 insertions(+), 192 deletions(-) diff --git a/specs/adversarial-implementation-workflow.md b/specs/adversarial-implementation-workflow.md index 0959147f..6a1f29a0 100644 --- a/specs/adversarial-implementation-workflow.md +++ b/specs/adversarial-implementation-workflow.md @@ -42,6 +42,17 @@ exists or when the agent is unsure. The current workflow supplies the planner, but the component does not encode that role. It may determine that user involvement is unnecessary, but only the user resolves a material choice. +That authority controls execution rather than describing it. A checkpoint binds +a schema-validated decision, and every material transition is gated on its +`proceed`: `proceed: false` never advances the workflow. 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. A checkpoint that found no material choice produces an explicit +`proceed: true` recording why, so a transition never advances because a decision +was missing. What remains missing is stopping *at* the boundary — halting +cleanly where the user answered so a later invocation resumes there, with a +recorded stop reason — which is `` (#298) over `` (#289). + ## Smallest complete path User and planner discuss the change @@ -212,6 +223,9 @@ by-reference binding. Every stage component in this workflow is a text component, because each stage's output is also material a user reads at a checkpoint. +`` is not a stage and is the one exception: it declares +`returns` and binds a validated transition decision, because its result is +branched on rather than read. ### A stage fails rather than returning a half-record diff --git a/workflows/adversarial-implementation/Discovery.md b/workflows/adversarial-implementation/Discovery.md index e42fed1f..0c8a035a 100644 --- a/workflows/adversarial-implementation/Discovery.md +++ b/workflows/adversarial-implementation/Discovery.md @@ -1,9 +1,10 @@ --- -required: [instructions, planner] +required: [instructions, planner, request] props: instructions: { type: string } planner: { type: string } + request: { type: string } --- # Discovery @@ -23,7 +24,7 @@ issue may enter directly at implementor planning. User request: - + {props.request} Produce a user-validated design handoff and a falsifiable implementation theory. Distinguish user decisions from hypotheses the implementor must @@ -51,8 +52,10 @@ selects the agent from a validated prop rather than a literal: the agent components take their props from a literal or from an expression that resolves to a string. An expression prop reads the bare binding, while the prompt body above interpolates `{props.instructions}` — the two spellings that #305 will -unify. Its caller supplies the request as content and decides whether and where -to persist the result. The handoff is a theory for investigation, not an +unify. Its caller supplies the request as the `request` prop and decides whether +and where to persist the result. It would read better as projected content, but +`` does not substitute inside a `` (#328), so a prop carries +it. The handoff is a theory for investigation, not an implementation plan that the implementor follows unquestioningly. The prompt sits outside ``, so it runs under the `throw` error mode: diff --git a/workflows/adversarial-implementation/Implementation.md b/workflows/adversarial-implementation/Implementation.md index 9cea4ecc..2bcd274f 100644 --- a/workflows/adversarial-implementation/Implementation.md +++ b/workflows/adversarial-implementation/Implementation.md @@ -3,7 +3,14 @@ required: [plan, authorization, instructions, planner, implementor] props: plan: { type: string } - authorization: { type: string } + authorization: + type: object + properties: + proceed: { type: boolean } + assessment: { type: string } + response: { type: string } + rationale: { type: string } + required: [proceed, assessment, response, rationale] instructions: { type: string } planner: { type: string } implementor: { type: string } @@ -91,7 +98,9 @@ remote effects. Authorization record: - {props.authorization} + {props.authorization.assessment} + User response: {props.authorization.response} + Rationale: {props.authorization.rationale} Result contract: @@ -165,7 +174,9 @@ remote effects. Authorization record: - {props.authorization} + {props.authorization.assessment} + User response: {props.authorization.response} + Rationale: {props.authorization.rationale} Pull request: @@ -228,11 +239,7 @@ remote effects. - + ## Authorized plan {props.plan} @@ -254,49 +261,82 @@ remote effects. {finding.description} - - - - - - Revise the implementation using this review: + + + + + + + + Revise the implementation using this review: - {verdict.review} + {verdict.review} - Focused revision prompt: + Focused revision prompt: - {verdict.revisionPrompt} + {verdict.revisionPrompt} - User involvement record: + User involvement record: - {reviewCheckpoint} - + {reviewCheckpoint.assessment} + User response: {reviewCheckpoint.response} + Rationale: {reviewCheckpoint.rationale} + + + + + - # Implementation result + + # Implementation result - ## Pull request + ## Pull request - {pullRequest} + {pullRequest} - ## Planner review + ## Planner review - Passed: {verdict.passed} + Passed: {verdict.passed} - {verdict.review} + {verdict.review} - ## Findings + ## Findings - - ### {finding.title} + + ### {finding.title} - Disposition: {finding.disposition} + Disposition: {finding.disposition} - {finding.description} - + {finding.description} + + + # Pull-request review rejected + + The user declined to continue at the pull-request review checkpoint. The + implementation below was neither revised nor accepted. + + {reviewCheckpoint.rationale} + + ## Pull request + + {pullRequest} + + ## Planner review + + Passed: {verdict.passed} + + {verdict.review} + + The agent, parsing, and control-flow syntax in this component runs today, on @@ -304,6 +344,13 @@ the same terms as `Planning`: the body outside `` runs under the `throw` error mode, so the final `` in each repair loop ends the stage rather than passing malformed data to a durable effect. +The user's decision outranks the verdict here too. `reviewCheckpoint.proceed` is +read before `verdict.passed`, so a declined pull-request review leaves the loop +without revising the implementation and without reporting it as reviewed. The +stage is reached at all only because `start.md` gated it on +`authorization.proceed`; a declined authorization means these prompts and the +durable effects below them never run. + `` (#294), `` (#295), and `` (#296) do not exist, and reconciling those durable GitHub effects idempotently is #297. Until they land, the loop above cannot expand at all — the three names resolve to nothing, diff --git a/workflows/adversarial-implementation/Planning.md b/workflows/adversarial-implementation/Planning.md index 7d85a82b..f501cfc5 100644 --- a/workflows/adversarial-implementation/Planning.md +++ b/workflows/adversarial-implementation/Planning.md @@ -3,7 +3,14 @@ required: [handoff, handoffCheckpoint, instructions, planner, implementor] props: handoff: { type: string } - handoffCheckpoint: { type: string } + handoffCheckpoint: + type: object + properties: + proceed: { type: boolean } + assessment: { type: string } + response: { type: string } + rationale: { type: string } + required: [proceed, assessment, response, rationale] instructions: { type: string } planner: { type: string } implementor: { type: string } @@ -50,7 +57,9 @@ resolves factual disagreement, while the user resolves material choices. User involvement record: - {props.handoffCheckpoint} + {props.handoffCheckpoint.assessment} + User response: {props.handoffCheckpoint.response} + Rationale: {props.handoffCheckpoint.rationale} Investigate the current working directory. Confirm, refute, or amend the implementation theory with evidence. Do not modify the repository. Return a @@ -71,7 +80,9 @@ resolves factual disagreement, while the user resolves material choices. User involvement record: - {props.handoffCheckpoint} + {props.handoffCheckpoint.assessment} + User response: {props.handoffCheckpoint.response} + Rationale: {props.handoffCheckpoint.rationale} Implementation plan: @@ -124,11 +135,7 @@ resolves factual disagreement, while the user resolves material choices. - + ## Implementation plan {plan} @@ -142,45 +149,85 @@ resolves factual disagreement, while the user resolves material choices. Revision prompt: {verdict.revisionPrompt} - - - - - - Revise the implementation plan using this review: + + + + + + + + Revise the implementation plan using this review: - {verdict.review} + {verdict.review} - Focused revision prompt: + Focused revision prompt: - {verdict.revisionPrompt} + {verdict.revisionPrompt} - User involvement record: + User involvement record: - {planCheckpoint} - + {planCheckpoint.assessment} + User response: {planCheckpoint.response} + Rationale: {planCheckpoint.rationale} + + + + + - # Implementation plan + + # Implementation plan - {plan} + {plan} - ## Planner review + ## Planner review - Passed: {verdict.passed} + Passed: {verdict.passed} - {verdict.review} + {verdict.review} + + # Plan review rejected + + The user declined to continue at the plan-review checkpoint. The plan below + was neither revised nor accepted. + + {planCheckpoint.rationale} + + ## Implementation plan as it stood + + {plan} + + ## Planner review + + Passed: {verdict.passed} + + {verdict.review} + + The loop is bounded and records why it stopped. `` journals every -iteration it enters and one terminal record whose outcome is `break` — the -passing verdict above — `exhausted`, or `error`, and it refuses a replay whose -stored outcome or iteration count disagrees with what this run reached. -`` opens no binding scope, so `plan`, `verdict`, and `planCheckpoint` -hold their final values in the `` region above. +iteration it enters and one terminal record whose outcome is `break` — a passing +verdict or a declined checkpoint — `exhausted`, or `error`, and it refuses a +replay whose stored outcome or iteration count disagrees with what this run +reached. `` opens no binding scope, so `plan`, `verdict`, and +`planCheckpoint` hold their final values in the `` region above. + +The user's decision outranks the verdict. The outer `` reads +`planCheckpoint.proceed` before the inner one reads `verdict.passed`, so a +declined checkpoint leaves the loop without revising the plan and without +presenting it as reviewed — a rejection is neither a revision request nor an +acceptance. `` works from that nested position, so the two conditions +compose without a flag binding between them. The body outside `` runs under the `throw` error mode, which is what makes the bounded repair turns a real gate: `` absorbs a malformed @@ -192,13 +239,14 @@ its failure and returns its text, raising nothing. **Outstanding gap: exhaustion is unhandled.** Reaching `max` completes the loop normally — exhaustion is not a failure and produces no diagnostic — so whether an exhausted planning loop counts as converged is this document's policy to -state, and this document does not yet state it. The body has no branch after -``, so a plan that never passed review falls straight into the same -`` as a plan that did. The output above therefore reports -`verdict.passed` rather than titling itself converged, but reporting the flag is -not deciding what should happen. What the workflow does when five rounds end -without a passing verdict — return the failing plan, fail the stage, or return -to the user — is an unresolved product decision recorded against +state, and this document does not yet state it. An exhausted loop leaves +`planCheckpoint.proceed` true and `verdict.passed` false, so it takes the same +`` branch as a converged plan and is distinguished only by the flag it +reports. That branch reports `verdict.passed` rather than titling itself +converged, but reporting the flag is not deciding what should happen. What the +workflow does when five rounds end without a passing verdict — return the +failing plan, fail the stage, or return to the user — is an unresolved product +decision recorded against [issue #290](https://github.com/taras/executable.md/issues/290), whose acceptance pins the behavior. This synchronization slice does not choose it. diff --git a/workflows/adversarial-implementation/UserCheckpoint.md b/workflows/adversarial-implementation/UserCheckpoint.md index 4391b0cf..afd2ba26 100644 --- a/workflows/adversarial-implementation/UserCheckpoint.md +++ b/workflows/adversarial-implementation/UserCheckpoint.md @@ -1,9 +1,23 @@ --- -required: [purpose, agent] +required: [purpose, agent, material] props: purpose: { type: string } agent: { type: string } + material: { type: string } + +returns: + requiresUser: { type: boolean } + proceed: { type: boolean } + assessment: { type: string } + recommendation: { type: string } + question: { type: string } + options: + type: array + items: + type: string + response: { type: string } + rationale: { type: string } --- # User Checkpoint @@ -12,6 +26,13 @@ This authored component asks its supplied agent whether a transition contains a material choice. It obtains the user's answer when needed; it never resolves that choice on the user's behalf. +It is a **value component**: it declares `returns`, renders nothing, and must be +invoked with `as`. What it binds is a schema-validated transition decision, so a +caller gates on `checkpoint.proceed` rather than reading prose. `proceed: false` +never advances the workflow. The human-readable material travels in the same +value — `assessment`, `recommendation`, `question`, `options`, `response`, and +`rationale` — so a later prompt interpolates exactly the fields it needs. + ## Target shape @@ -43,9 +64,10 @@ that choice on the user's behalf. - + ```json { + "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "proceed": { @@ -68,7 +90,7 @@ that choice on the user's behalf. Material to assess: - + {props.material} Result contract: @@ -124,7 +146,7 @@ that choice on the user's behalf. - + {assessment.question} Options: @@ -135,52 +157,51 @@ that choice on the user's behalf. Recommendation: {assessment.recommendation} + + + {"proceed": true, "response": "continue", "rationale": "The assessing agent found no material choice, so this transition needs no user decision."} + + - -## User involvement assessment - -{assessment.assessment} - -Recommendation: {assessment.recommendation} - - -Question: {assessment.question} + -Options: +## Continuation is represented, never inferred - -- {option} - - -## User response - -Proceed: {elicitation.proceed} - -Response: {elicitation.response} - -Rationale: {elicitation.rationale} - - +Both branches bind `decision` against the same `decisionSchema`, so `proceed` is +always a validated boolean that some path explicitly produced. When the agent +reports no material choice, the `` branch parses an explicit +`"proceed": true` with the reason recorded. Nothing reads a missing elicitation +as consent, which is what keeps #290's "cannot become implicit approval" +requirement intact: a transition advances because a decision said so, not +because no decision was found. `UserInvolvementAssessment` distinguishes whether involvement is required from -the choice itself. The caller renders the complete material to assess as child -content rather than asking the agent to locate or read it. +the choice itself. The caller passes the complete material to assess as the +`material` prop rather than asking the agent to locate or read it. + +**Missing: projecting the material as content.** `material` would read better as +`` — the caller writing the material as children — but `` +is substituted only at a body's top level or directly inside ``, never +nested inside a `` where this component needs it +([issue #328](https://github.com/taras/executable.md/issues/328)). A prop +interpolates anywhere, so that is what this component takes until #328 lands. `` asks without choosing how. It requires `schema` and `as`, compiles the schema before its content expands, renders that content as the request message, and validates the provider's answer against the same compiled schema before binding it. There is no `mode`, `provider`, or `uiSchema` prop and no -built-in approve, decline, or cancel — `elicitationSchema` above defines every -response available. Where the asking happens is the host's decision, made -through the Elicitation Api: `xmd run` composes WebForm as its current -provider, so this checkpoint opens a loopback browser form under the CLI. -Only the validated answer is journaled, keyed by a fingerprint of the compiled -schema and the rendered message, so a resumed run restores the answer instead -of asking twice and refuses a recorded answer whose question does not match. -A document that already knows the answer — a test, a demo, a non-interactive -region — wraps this component in an `` region and supplies it with -`` matchers, which changes who answers without changing this file. +built-in approve, decline, or cancel — `decisionSchema` above defines every +response available, and `proceed` is a field the author declared rather than a +built-in verb. Where the asking happens is the host's decision, made through the +Elicitation Api: `xmd run` composes WebForm as its current provider, so this +checkpoint opens a loopback browser form under the CLI. Only the validated +answer is journaled, keyed by a fingerprint of the compiled schema and the +rendered message, so a resumed run restores the answer instead of asking twice +and refuses a recorded answer whose question does not match. A document that +already knows the answer — a test, a demo, a non-interactive region — wraps this +component in an `` region and supplies it with `` matchers, +which changes who answers without changing this file. What `` does not solve is stopping between processes. It answers a question inside one run; selecting and resuming a stopped stage in a later @@ -197,11 +218,10 @@ from continuing after the bounded repair loop with malformed output. The schema is ordinary captured document content rather than a registry entry or `` prop. -Every construct above sits outside ``, so the whole assessment runs -under the `throw` error mode: the final `` failing ends the stage rather -than printing an error into a region nobody reads. The `` region itself -runs under `output` and is equally fail-fast, keeping only what it had rendered -before any failure. +A value component's body is not documentation in the ordinary sense — a value +root and a value component both run fail-fast, so the assessment, the repair +loop, and the elicitation all end the stage rather than binding something a +caller would mistake for a decision. `` reads the bare binding because that is the expression- prop spelling current main supports, while `{props.purpose}` in the prompt body diff --git a/workflows/adversarial-implementation/artifacts.md b/workflows/adversarial-implementation/artifacts.md index ac4f8329..bb68e4a4 100644 --- a/workflows/adversarial-implementation/artifacts.md +++ b/workflows/adversarial-implementation/artifacts.md @@ -95,34 +95,68 @@ re-executing anything. ### Logical result contracts -The following names describe the JSON these agents produce. They are labels for -shapes these documents declare inline, not component return declarations, not -entries in a schema registry, and the workflow does not assume a -`` prop. Each name is used for exactly one shape, here and in -the stage that produces it. - -- `PlannerHandoff` separates user decisions from implementation hypotheses. -- `ImplementationPlan` records confirmed and refuted assumptions, evidence, - validation, environmental effects, and pull-request boundaries. -- `PlannerVerdict` records pass/fail state, evidence, user questions, and a - focused revision prompt. -- `ImplementationResult` records changed files, proposed commit metadata, - validation, and newly discovered scope. -- `PullRequestVerdict` records findings, dispositions, evidence, user - questions, and a focused revision prompt. -- `UserInvolvementAssessment` records whether involvement is required, the - material choice, viable options, consequences, evidence, and recommendation. -- `UserDecision` records the request, options, selection, rationale, actor, and - time. +These names label the results the stages pass between each other. They are not +component return declarations and not entries in a schema registry, and the +workflow does not assume a `` prop. They fall into three kinds, +and the difference matters: only the parsed ones can be branched on. + +**Prose.** No schema, no parsing. The stage renders text and its caller binds +that text, because nothing downstream reads an individual field. + +- `PlannerHandoff` — what `Discovery` returns. It separates user decisions from + implementation hypotheses in prose the implementor reads, and the sections it + should contain are listed in [`Discovery`](./Discovery.md) rather than + enforced by a schema. +- `ImplementationPlan` — what `Planning` returns. Confirmed and refuted + assumptions, evidence, validation, environmental effects, and pull-request + boundaries appear inside `plan`, which is one ``'s rendered reply. + +Neither is validated, so neither can gate a transition. `Planning` branches on +the separately parsed `PlannerVerdict`, not on the plan text. + +**Parsed JSON.** Each has a draft-07 schema captured inline in the stage that +produces it, and each description below names only fields that schema actually +declares. + +- `PlannerVerdict` — `passed`, `review`, `revisionPrompt` + ([`Planning`](./Planning.md)). Evidence and any user question live inside the + `review` prose; they are not separate fields. +- `ImplementationResult` — `changedFiles`, `commitMessage`, `report` + ([`Implementation`](./Implementation.md)). Validation and newly discovered + scope are reported inside `report`. +- `PullRequestVerdict` — `passed`, `review`, `revisionPrompt`, and `findings`, + each finding carrying `disposition`, `title`, `description`, and `evidence` + ([`Implementation`](./Implementation.md)). +- `UserInvolvementAssessment` — `requiresUser`, `assessment`, `question`, + `options`, `recommendation` ([`UserCheckpoint`](./UserCheckpoint.md)). + +**A declared return.** `UserCheckpoint` is the one value component here, so its +result is a validated JSON value bound through `as` rather than parsed out of +rendered text. + +- `UserDecision` — the transition decision a caller gates on. Two parts combine + into it. The **decision** is `proceed`, `response`, and `rationale`, validated + against one schema on both paths: `` binds it when the assessment + reports a material choice, and an explicit `` binds it when there is + none. `UserCheckpoint` returns those alongside the assessment fields, so one + value carries both the gate and the material a later prompt quotes. + **Missing:** the provenance that makes a decision auditable after the fact — + which actor answered, when, against which run and stage — belongs to the + artifact ledger (#291) and does not exist. Nothing in the returned value + identifies the person who answered. Prompt output used for control flow is JSON parsed against captured draft-07 JSON Schema content. `` exposes the candidate and normalized errors for a visible, bounded correction turn; a final `` prevents invalid data from reaching control flow or deterministic effects. Prose capture remains -acceptable when no later transition depends on internal fields. +acceptable when no later transition depends on internal fields — which is +exactly the line between the first group above and the second. Parsing content inside a document and declaring a component's return value are -separate mechanisms, and both are shipped. A stage component could declare -`returns` and hand its caller the parsed verdict directly instead of rendered -text; these documents keep the text form because each stage's output is also -material a user reads at a checkpoint. +separate mechanisms, and both are shipped. The stages keep the text form because +each stage's output is also material a user reads at a checkpoint. +`UserCheckpoint` is the exception, and the reason is the distinction above: its +result is not something a user reads, it is something the workflow branches on. +A checkpoint that returned prose could not gate anything — a caller would have +to guess consent from text — so it declares `returns` and hands back a validated +decision instead. diff --git a/workflows/adversarial-implementation/primitives.md b/workflows/adversarial-implementation/primitives.md index f5fc4c69..ed1cd531 100644 --- a/workflows/adversarial-implementation/primitives.md +++ b/workflows/adversarial-implementation/primitives.md @@ -248,7 +248,8 @@ The remaining contracts are missing on the same terms: authored Markdown components, not runtime primitives. `` combines an agent prompt, conditional control flow, and `` to determine whether a material choice requires the user and to obtain the user's answer -when it does. +when it does. It is the one of the four that declares `returns`: a gate has to +bind a validated boolean, not prose a caller would have to interpret. Where a primitive is still missing, the manual exercise replaces it with an explicit user-run step and records the replacement as evidence for prioritizing diff --git a/workflows/adversarial-implementation/start.md b/workflows/adversarial-implementation/start.md index fb9eee77..e60f4bcd 100644 --- a/workflows/adversarial-implementation/start.md +++ b/workflows/adversarial-implementation/start.md @@ -20,11 +20,12 @@ Its component markup is the intended executable form. The three wrappers — that do not exist yet, and one of the stages they wrap is not expressible either. "What runs today" below says exactly which. -Each stage component is a text component: it declares no `returns`, so its -`` region is its return value and the caller's `as` binds that rendered -text. `` is the exception: it declares `returns`, so it renders nothing, -requires `as`, and binds a `string[]` validated against a clone of what it -produced. The [executable MDX +Each of the four *stage* components is a text component: it declares no +`returns`, so its `` region is its return value and the caller's `as` +binds that rendered text. Two things here declare `returns` instead, render +nothing, require `as`, and bind a JSON value validated against a clone of what +they produced: ``, which binds a `string[]`, and ``, which +binds the transition decision the flow gates on. The [executable MDX specification](../../specs/executable-mdx-spec.md) is the authority for both. `` will create a run identity, record those captured results as artifact versions, and restore them when a later stage resumes (#289, #291); @@ -46,44 +47,79 @@ supplies `request`, `base`, `planner`, and `implementor` (#179). - {props.request} - + /> - {handoff} - - - - {plan} - - - - {implementationResult} - - {acceptance} + /> + + + + + + + + + + + + + # Accepted + + {acceptance.rationale} + + {implementationResult} + + # Rejected at acceptance + + The change was completed but the user did not accept it. + + {acceptance.rationale} + + {implementationResult} + + + + # Stopped: implementation was not authorized + + {authorization.rationale} + + {plan} + + + + # Stopped: the handoff was not validated + + {handoffCheckpoint.rationale} + + {handoff} + + + @@ -92,15 +128,37 @@ supplies `request`, `base`, `planner`, and `implementor` (#179). worktree. Its run identity keeps discovery through implementation on that pinned filesystem even if the branch moves while execution is in progress. +## User authority is a gate, not a report + +Every material transition is gated on a checkpoint's `proceed`. `UserCheckpoint` +declares `returns`, so what a caller binds is a schema-validated decision rather +than prose to be read: a declined handoff never starts `Planning`, and a declined +authorization never starts `Implementation`. A checkpoint that found no material +choice still produces an explicit `proceed: true` with its reason, so nothing +advances because a decision was absent. + +`` reports which gate the run reached. A rejected acceptance finishes as +rejected — the flow does not fall into the accepted branch — and a run stopped +earlier renders the artifact it stopped on rather than a value it never +produced. + +**Missing: stopping at the boundary.** Nesting expresses the gate, and it is +what the language supports today, but it is not the same as *stopping*. The run +still expands to `` and completes; there is no clean halt at a stage +boundary that a later invocation resumes from, and no stop reason recorded for +one. That is `` (#298) over ``'s run identity (#289). Until +they exist, a declined checkpoint means the remaining stages do not run and the +outcome says so — not that the process stopped where the user answered. + ## How props are read Two spellings, and they are not interchangeable today: -- **Text and content** read the namespace: `{props.request}` above, and - `{props.instructions}` inside every stage's prompt. -- **Expression props** read the **bare** binding: `planner={planner}`, not - `planner={props.planner}`. A `props.` reference in an expression prop fails - with `props is not defined`. +- **Text and content** read the namespace: `{props.instructions}` and + `{props.material}` inside a stage's prompt body. +- **Expression props** read the **bare** binding: `planner={planner}` and + `request={request}`, not `planner={props.planner}`. A `props.` reference in an + expression prop fails with `props is not defined`. Removing that asymmetry is [issue #305](https://github.com/taras/executable.md/issues/305), whose acceptance @@ -164,13 +222,17 @@ manual stages. | Captured value | Produced by | Consumed by | | ---------------------- | -------------------------- | --------------------------------------------------- | | `instructionPaths` | `Glob` (`string[]`) | `InstructionFiles` | -| `instructions` | `InstructionFiles` | every agent prompt | -| `handoff` | `Discovery` | handoff `UserCheckpoint`, `Planning` | -| `handoffCheckpoint` | handoff `UserCheckpoint` | `Planning` | -| `plan` | `Planning` | authorization `UserCheckpoint`, `Implementation` | -| `authorization` | authorization checkpoint | `Implementation` | -| `implementationResult` | `Implementation` | acceptance `UserCheckpoint` | -| `acceptance` | acceptance checkpoint | workflow output, terminal record | +| `instructions` | `InstructionFiles` (text) | every agent prompt | +| `handoff` | `Discovery` (text) | handoff `UserCheckpoint`, `Planning` | +| `handoffCheckpoint` | handoff `UserCheckpoint` (decision) | the `Planning` gate, and `Planning` | +| `plan` | `Planning` (text) | authorization `UserCheckpoint`, `Implementation` | +| `authorization` | authorization checkpoint (decision) | the `Implementation` gate, and `Implementation` | +| `implementationResult` | `Implementation` (text) | acceptance `UserCheckpoint` | +| `acceptance` | acceptance checkpoint (decision) | workflow output, terminal record | + +A stage binds rendered text; a checkpoint binds a decision object. The four +`UserCheckpoint` invocations are the only value components here — everything +else declares no `returns`, so `as` binds what it rendered. ## Details From 173e96aac33388b715ab9b4e1fff7e3cb50e4f13 Mon Sep 17 00:00:00 2001 From: Taras Mankovski <74687+taras@users.noreply.github.com> Date: Tue, 4 Aug 2026 17:41:16 -0400 Subject: [PATCH 4/8] =?UTF-8?q?=F0=9F=93=9D=20Return=20stage=20control=20s?= =?UTF-8?q?tate=20so=20user=20authority=20survives=20the=20boundary?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `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 `` 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 `` 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 `` 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. --- specs/adversarial-implementation-workflow.md | 53 ++++-- .../Implementation.md | 151 +++++++++------- .../adversarial-implementation/Planning.md | 120 +++++++------ .../adversarial-implementation/artifacts.md | 66 ++++--- workflows/adversarial-implementation/start.md | 167 ++++++++++++------ 5 files changed, 355 insertions(+), 202 deletions(-) diff --git a/specs/adversarial-implementation-workflow.md b/specs/adversarial-implementation-workflow.md index 6a1f29a0..c45bb14f 100644 --- a/specs/adversarial-implementation-workflow.md +++ b/specs/adversarial-implementation-workflow.md @@ -43,15 +43,32 @@ but the component does not encode that role. It may determine that user involvement is unnecessary, but only the user resolves a material choice. That authority controls execution rather than describing it. A checkpoint binds -a schema-validated decision, and every material transition is gated on its -`proceed`: `proceed: false` never advances the workflow. 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. A checkpoint that found no material choice produces an explicit -`proceed: true` recording why, so a transition never advances because a decision -was missing. What remains missing is stopping *at* the boundary — halting -cleanly where the user answered so a later invocation resumes there, with a -recorded stop reason — which is `` (#298) over `` (#289). +a schema-validated decision, and every material transition is gated on it: +`proceed: false` never advances the workflow. A declined handoff does not start +planning; a declined authorization does not start implementation; a declined +review neither revises, nor creates the deferred issues it proposed, nor +accepts; a declined acceptance finishes as rejected. + +A decision resolved inside a stage is gated the same way, because the stage +returns it. `authorized` — the review's approval *and* a passing verdict — +is what the caller reads, so a stage cannot approve its own advancement. An +exhausted review loop reports `terminal: "exhausted"` with `authorized` false +and therefore fails closed; what the workflow should ultimately do with an +exhausted loop is an unresolved product decision under #290, and failing closed +is not an answer to it. + +`proceed: true` authorizes the exact transition and effects the checkpoint +assessed, and nothing more. Free-text fields in a decision record the user's +reasoning; nothing reads them to amend what runs, because an effect that has +already executed cannot be amended by prose. This is why durable effects follow +their approval rather than preceding it — the deferred issues a review proposes +are created inside the approved path, after the checkpoint. + +A checkpoint that found no material choice produces an explicit `proceed: true` +recording why, so a transition never advances because a decision was missing. +What remains missing is stopping *at* the boundary — halting cleanly where the +user answered so a later invocation resumes there, with a recorded stop reason — +which is `` (#298) over `` (#289). ## Smallest complete path @@ -221,11 +238,19 @@ normalized clone reaches the caller. `` declares `returns`, so the `string[]` this workflow binds is that validated clone rather than a by-reference binding. -Every stage component in this workflow is a text component, because each -stage's output is also material a user reads at a checkpoint. -`` is not a stage and is the one exception: it declares -`returns` and binds a validated transition decision, because its result is -branched on rather than read. +A stage that only produces material for the next prompt is a text component: +`InstructionFiles` and `Discovery` render their result and a caller's `as` binds +that text. A stage that resolves a user decision inside itself declares +`returns` instead. `Planning` and `Implementation` each run a review loop that +asks the user a question, so each returns its prose, its parsed verdict, the +complete `UserDecision` it resolved, and the derived `authorized` and `terminal` +its caller gates on. `` does the same for a single decision. + +The rule is about where authority lives. A controller that resolves a decision +and returns only a rendering of it has discarded the thing its caller needs: the +caller would go on to ask the next question regardless, and could report a change +accepted whose review the user rejected. Control state crosses a component +boundary as data or it does not cross at all. ### A stage fails rather than returning a half-record diff --git a/workflows/adversarial-implementation/Implementation.md b/workflows/adversarial-implementation/Implementation.md index 2bcd274f..000b9c13 100644 --- a/workflows/adversarial-implementation/Implementation.md +++ b/workflows/adversarial-implementation/Implementation.md @@ -14,6 +14,46 @@ props: instructions: { type: string } planner: { type: string } implementor: { type: string } + +returns: + report: { type: string } + pullRequest: { type: string } + verdictPassed: { type: boolean } + review: { type: string } + revisionPrompt: { type: string } + authorized: { type: boolean } + terminal: { type: string } + findings: + type: array + items: + type: object + properties: + disposition: { type: string } + title: { type: string } + description: { type: string } + evidence: + type: array + items: + type: string + required: [disposition, title, description, evidence] + additionalProperties: false + decision: + type: object + properties: + requiresUser: { type: boolean } + proceed: { type: boolean } + assessment: { type: string } + recommendation: { type: string } + question: { type: string } + options: + type: array + items: + type: string + response: { type: string } + rationale: { type: string } + required: + [requiresUser, proceed, assessment, recommendation, question, options, response, rationale] + additionalProperties: false --- # Implementation @@ -230,15 +270,6 @@ remote effects. - - - - - - ## Authorized plan @@ -269,6 +300,14 @@ remote effects. as="reviewCheckpoint" /> + + + + + @@ -295,61 +334,53 @@ remote effects. - - - # Implementation result - - ## Pull request - - {pullRequest} - - ## Planner review - - Passed: {verdict.passed} - - {verdict.review} - - ## Findings - - - ### {finding.title} - - Disposition: {finding.disposition} - - {finding.description} - - - # Pull-request review rejected - - The user declined to continue at the pull-request review checkpoint. The - implementation below was neither revised nor accepted. - - {reviewCheckpoint.rationale} - - ## Pull request - - {pullRequest} - - ## Planner review - - Passed: {verdict.passed} - - {verdict.review} - - - - -The agent, parsing, and control-flow syntax in this component runs today, on -the same terms as `Planning`: the body outside `` runs under the -`throw` error mode, so the final `` in each repair loop ends the stage -rather than passing malformed data to a durable effect. + + +## The stage returns its control state + +Like `Planning`, this is a **value component**: it resolves a user decision +internally, so it returns that decision rather than a rendering of it. +`authorized` is `reviewCheckpoint.proceed && verdict.passed`, and `terminal` is +`converged`, `declined`, or `exhausted`. Final acceptance is reachable only when +`authorized` is true, so a declined or exhausted review cannot be reported as an +accepted change. + +The agent, parsing, and control-flow syntax runs today. A value component's body +runs fail-fast, so the final `` in each repair loop ends the stage rather +than passing malformed data to a durable effect. The user's decision outranks the verdict here too. `reviewCheckpoint.proceed` is read before `verdict.passed`, so a declined pull-request review leaves the loop without revising the implementation and without reporting it as reviewed. The -stage is reached at all only because `start.md` gated it on -`authorization.proceed`; a declined authorization means these prompts and the -durable effects below them never run. +stage is reached at all only because `start.md` gated it on both +`planning.authorized` and `authorization.proceed`. + +## Approval precedes durable effects + +Deferred `` creation sits **inside** the approved branch, after the +checkpoint. The planner proposes a disposition; the user's approval is what +turns that proposal into a durable GitHub object. Creating the issues first +would make the planner's classification take effect before anyone approved it, +and an issue is not undone by a later decline. + +`proceed: true` authorizes the exact transition and the exact effects proposed +in the material the checkpoint assessed — here, an `` for every finding +the verdict marked `defer`. It is not an invitation to amend them. The free-text +`response` and `rationale` are a record of the user's reasoning, and nothing +reads them to change which effects run: an effect that already executed cannot +be silently amended by prose. A user who wants different effects declines, and +`proceed: false` performs none of them — no issue, no revision turn, no +acceptance. `` (#294), `` (#295), and `` (#296) do not exist, and reconciling those durable GitHub effects idempotently is #297. Until they diff --git a/workflows/adversarial-implementation/Planning.md b/workflows/adversarial-implementation/Planning.md index f501cfc5..767dd9e6 100644 --- a/workflows/adversarial-implementation/Planning.md +++ b/workflows/adversarial-implementation/Planning.md @@ -14,6 +14,31 @@ props: instructions: { type: string } planner: { type: string } implementor: { type: string } + +returns: + plan: { type: string } + verdictPassed: { type: boolean } + review: { type: string } + revisionPrompt: { type: string } + authorized: { type: boolean } + terminal: { type: string } + decision: + type: object + properties: + requiresUser: { type: boolean } + proceed: { type: boolean } + assessment: { type: string } + recommendation: { type: string } + question: { type: string } + options: + type: array + items: + type: string + response: { type: string } + rationale: { type: string } + required: + [requiresUser, proceed, assessment, recommendation, question, options, response, rationale] + additionalProperties: false --- # Planning @@ -183,44 +208,42 @@ resolves factual disagreement, while the user resolves material choices. - - - # Implementation plan - - {plan} - - ## Planner review - - Passed: {verdict.passed} - - {verdict.review} - - # Plan review rejected - - The user declined to continue at the plan-review checkpoint. The plan below - was neither revised nor accepted. - - {planCheckpoint.rationale} - - ## Implementation plan as it stood - - {plan} - - ## Planner review - - Passed: {verdict.passed} - - {verdict.review} - - - + + +## The stage returns its control state + +This is a **value component**. A stage that resolves a user decision internally +cannot discard it as rendered prose: its caller has to gate on that decision, +and prose gives a caller nothing to branch on. So `Planning` declares `returns`, +renders nothing, and hands back the plan, the parsed verdict's fields, the +complete plan-review `UserDecision`, and two derived fields the caller reads +directly: + +- **`authorized`** is `planCheckpoint.proceed && verdict.passed`. Only a plan + that both passed review *and* was approved may reach authorization. Either one + alone is not enough. +- **`terminal`** distinguishes how the loop ended: `converged` (approved and + passing), `declined` (the user stopped it), or `exhausted` (approved but still + failing after `max` rounds). + +`authorized` is false for both `declined` and `exhausted`, so neither can +advance the workflow. That is failing closed, not a decision about what an +exhausted loop *should* do. The loop is bounded and records why it stopped. `` journals every iteration it enters and one terminal record whose outcome is `break` — a passing verdict or a declined checkpoint — `exhausted`, or `error`, and it refuses a replay whose stored outcome or iteration count disagrees with what this run reached. `` opens no binding scope, so `plan`, `verdict`, and -`planCheckpoint` hold their final values in the `` region above. +`planCheckpoint` hold their final values where `` reads them. The user's decision outranks the verdict. The outer `` reads `planCheckpoint.proceed` before the inner one reads `verdict.passed`, so a @@ -229,24 +252,21 @@ presenting it as reviewed — a rejection is neither a revision request nor an acceptance. `` works from that nested position, so the two conditions compose without a flag binding between them. -The body outside `` runs under the `throw` error mode, which is what -makes the bounded repair turns a real gate: `` absorbs a malformed -verdict so the document can show the correction prompt, and the final `` -ends the stage if the candidate is still invalid. `throwOnError` on each -`` is required for the same reason — a failed prompt without it records -its failure and returns its text, raising nothing. - -**Outstanding gap: exhaustion is unhandled.** Reaching `max` completes the loop -normally — exhaustion is not a failure and produces no diagnostic — so whether -an exhausted planning loop counts as converged is this document's policy to -state, and this document does not yet state it. An exhausted loop leaves -`planCheckpoint.proceed` true and `verdict.passed` false, so it takes the same -`` branch as a converged plan and is distinguished only by the flag it -reports. That branch reports `verdict.passed` rather than titling itself -converged, but reporting the flag is not deciding what should happen. What the -workflow does when five rounds end without a passing verdict — return the -failing plan, fail the stage, or return to the user — is an unresolved product -decision recorded against +A value component's body runs fail-fast, which is what makes the bounded repair +turns a real gate: `` absorbs a malformed verdict so the document can +show the correction prompt, and the final `` ends the stage if the +candidate is still invalid. `throwOnError` on each `` is required for the +same reason — a failed prompt without it records its failure and returns its +text, raising nothing. + +**Outstanding gap: the terminal policy for exhaustion.** Reaching `max` +completes the loop normally — exhaustion is not a failure and produces no +diagnostic. This stage reports it as `terminal: "exhausted"` with `authorized` +false, so an exhausted loop is distinguishable from a converged one and cannot +advance. That is the minimum needed to keep the workflow safe; it is not the +policy. What the workflow *should* do when five rounds end without a passing +verdict — return the failing plan, fail the stage, or return to the user — is an +unresolved product decision recorded against [issue #290](https://github.com/taras/executable.md/issues/290), whose acceptance pins the behavior. This synchronization slice does not choose it. diff --git a/workflows/adversarial-implementation/artifacts.md b/workflows/adversarial-implementation/artifacts.md index bb68e4a4..96914e28 100644 --- a/workflows/adversarial-implementation/artifacts.md +++ b/workflows/adversarial-implementation/artifacts.md @@ -58,7 +58,7 @@ A component has one return path, defined by the [executable MDX specification](../../specs/executable-mdx-spec.md). A Markdown component that declares no `returns` is a **text component**: its rendered Markdown is its return value, `` selects which region renders, and `as` -binds that text. Every stage component in this workflow is one. A component +binds that text. `InstructionFiles` and `Discovery` are ones. A component that declares `returns` is a **value component**: it renders nothing, holds exactly one direct top-level ``, must be invoked with `as`, and binds one JSON value validated against its schema. The two are mutually @@ -85,13 +85,15 @@ else renders nothing — not an error, a value with no destination. ### What a failing stage returns -Nothing partial. A stage's `` region runs under the `output` error mode -and everything outside it runs under `throw`, so an undecided error at either -position fails the run rather than producing a result the caller would bind. A -failing `` region keeps only the text it had already rendered, and that -text reaches the output stream; nothing after the failure does. A failed run is -still a complete record — replay restores its output and its failure without -re-executing anything. +Nothing partial. A text component's `` region runs under the `output` +error mode and everything outside it under `throw`; a value component's whole +body runs fail-fast. Either way an undecided error fails the run rather than +producing a result the caller would bind, and a value component that fails binds +nothing at all — there is no half-validated return. A failing `` region +keeps only the text it had already rendered, and that text reaches the output +stream; nothing after the failure does. A failed run is still a complete +record — replay restores its output and its failure without re-executing +anything. ### Logical result contracts @@ -100,16 +102,18 @@ component return declarations and not entries in a schema registry, and the workflow does not assume a `` prop. They fall into three kinds, and the difference matters: only the parsed ones can be branched on. -**Prose.** No schema, no parsing. The stage renders text and its caller binds -that text, because nothing downstream reads an individual field. +**Prose.** No schema, no parsing. Text a stage produces and a later prompt +quotes, because nothing downstream reads an individual field. -- `PlannerHandoff` — what `Discovery` returns. It separates user decisions from +- `PlannerHandoff` — what `Discovery` returns, and the one result that is a + whole component's rendered output. It separates user decisions from implementation hypotheses in prose the implementor reads, and the sections it should contain are listed in [`Discovery`](./Discovery.md) rather than enforced by a schema. -- `ImplementationPlan` — what `Planning` returns. Confirmed and refuted - assumptions, evidence, validation, environmental effects, and pull-request - boundaries appear inside `plan`, which is one ``'s rendered reply. +- `ImplementationPlan` — the `plan` field inside `Planning`'s structured return. + Confirmed and refuted assumptions, evidence, validation, environmental + effects, and pull-request boundaries appear inside it, because it is one + ``'s rendered reply. Neither is validated, so neither can gate a transition. `Planning` branches on the separately parsed `PlannerVerdict`, not on the plan text. @@ -130,11 +134,20 @@ declares. - `UserInvolvementAssessment` — `requiresUser`, `assessment`, `question`, `options`, `recommendation` ([`UserCheckpoint`](./UserCheckpoint.md)). -**A declared return.** `UserCheckpoint` is the one value component here, so its -result is a validated JSON value bound through `as` rather than parsed out of -rendered text. - -- `UserDecision` — the transition decision a caller gates on. Two parts combine +**Declared returns.** `UserCheckpoint`, `Planning`, and `Implementation` declare +`returns`, so their results are validated JSON values bound through `as` rather +than text a caller would have to interpret. + +- `StageResult` — what `Planning` and `Implementation` return. Each carries its + prose (`plan` or `report`), the parsed verdict's fields, the complete + `UserDecision` that stage resolved internally, and two derived control fields: + `authorized` (`proceed && verdict.passed`) and `terminal` (`converged`, + `declined`, or `exhausted`). `start.md` gates the next transition on + `authorized` and reports `terminal`. A stage that resolved a user decision + cannot return prose alone — its caller has nothing to branch on, and the + authority the checkpoint exercised would be lost at the boundary. +- `UserDecision` — the transition decision a caller gates on, returned directly + by `UserCheckpoint` and embedded in each `StageResult`. Two parts combine into it. The **decision** is `proceed`, `response`, and `rationale`, validated against one schema on both paths: `` binds it when the assessment reports a material choice, and an explicit `` binds it when there is @@ -153,10 +166,11 @@ acceptable when no later transition depends on internal fields — which is exactly the line between the first group above and the second. Parsing content inside a document and declaring a component's return value are -separate mechanisms, and both are shipped. The stages keep the text form because -each stage's output is also material a user reads at a checkpoint. -`UserCheckpoint` is the exception, and the reason is the distinction above: its -result is not something a user reads, it is something the workflow branches on. -A checkpoint that returned prose could not gate anything — a caller would have -to guess consent from text — so it declares `returns` and hands back a validated -decision instead. +separate mechanisms, and both are shipped. Which one a component uses follows +from what its caller does with the result. `InstructionFiles` and `Discovery` +produce material a prompt quotes, so text is enough. `UserCheckpoint`, +`Planning`, and `Implementation` each resolve a decision the caller must branch +on, and a caller cannot branch on prose without guessing — so they declare +`returns` and hand back validated values. The human-readable report is rendered +by `start.md` from those fields, which is the same material, addressed rather +than pre-flattened. diff --git a/workflows/adversarial-implementation/start.md b/workflows/adversarial-implementation/start.md index e60f4bcd..0ae8bcc1 100644 --- a/workflows/adversarial-implementation/start.md +++ b/workflows/adversarial-implementation/start.md @@ -20,12 +20,15 @@ Its component markup is the intended executable form. The three wrappers — that do not exist yet, and one of the stages they wrap is not expressible either. "What runs today" below says exactly which. -Each of the four *stage* components is a text component: it declares no -`returns`, so its `` region is its return value and the caller's `as` -binds that rendered text. Two things here declare `returns` instead, render -nothing, require `as`, and bind a JSON value validated against a clone of what -they produced: ``, which binds a `string[]`, and ``, which -binds the transition decision the flow gates on. The [executable MDX +Components here split by what their caller needs from them. `InstructionFiles` +and `Discovery` are **text components**: they declare no `returns`, so an +`` region is the return value and `as` binds that rendered text, which +is all a prompt downstream needs. `Planning`, `Implementation`, +`UserCheckpoint`, and `` declare `returns` instead: they render nothing, +require `as`, and bind a JSON value validated against a clone of what they +produced. A stage that resolves a user decision inside itself has to be in the +second group — a controller cannot discard its control state as prose and still +let its caller gate on it. The [executable MDX specification](../../specs/executable-mdx-spec.md) is the authority for both. `` will create a run identity, record those captured results as artifact versions, and restore them when a later stage resumes (#289, #291); @@ -62,53 +65,91 @@ supplies `request`, `base`, `planner`, and `implementor` (#179). instructions={instructions} planner={planner} implementor={implementor} - as="plan" /> - - - + as="planning" /> + + + ## Implementation plan + + {planning.plan} + + ## Planner review + + {planning.review} + + + + + + + - - - # Accepted + + + + + # Accepted - {acceptance.rationale} + {acceptance.rationale} - {implementationResult} - - # Rejected at acceptance + {implementation.report} + + # Rejected at acceptance - The change was completed but the user did not accept it. + The change was completed and reviewed, but the user did not + accept it. - {acceptance.rationale} + {acceptance.rationale} - {implementationResult} + {implementation.report} + + + + # Stopped in implementation: {implementation.terminal} + + The pull-request review ended `{implementation.terminal}`, so + the change was never offered for acceptance. + + {implementation.decision.rationale} + + {implementation.report} + + + + # Stopped: implementation was not authorized + + {authorization.rationale} + + {planning.plan} - # Stopped: implementation was not authorized + # Stopped in planning: {planning.terminal} - {authorization.rationale} + The plan review ended `{planning.terminal}`, so authorization was + never requested. - {plan} + {planning.decision.rationale} + + {planning.plan} @@ -130,17 +171,36 @@ pinned filesystem even if the branch moves while execution is in progress. ## User authority is a gate, not a report -Every material transition is gated on a checkpoint's `proceed`. `UserCheckpoint` -declares `returns`, so what a caller binds is a schema-validated decision rather -than prose to be read: a declined handoff never starts `Planning`, and a declined -authorization never starts `Implementation`. A checkpoint that found no material -choice still produces an explicit `proceed: true` with its reason, so nothing -advances because a decision was absent. - -`` reports which gate the run reached. A rejected acceptance finishes as -rejected — the flow does not fall into the accepted branch — and a run stopped -earlier renders the artifact it stopped on rather than a value it never -produced. +Every material transition is gated on a decision, and a decision never crosses a +component boundary as prose. + +Two of the gates read a checkpoint this document invoked directly: +`handoffCheckpoint.proceed` before `Planning`, and `authorization.proceed` +before `Implementation`. The other two read a decision a stage resolved +*internally* and returned: `planning.authorized` and +`implementation.authorized`. Each is `proceed && verdict.passed` — a plan that +was approved but never passed review cannot reach authorization, and one that +passed review but was declined cannot either. + +That second pair is what keeps authority from leaking across a boundary. A stage +that asks the user a question and then returns only a report leaves its caller +guessing; the caller would ask the next question anyway and could accept a change +whose review the user rejected. Returning `authorized` and `terminal` makes the +internal decision the caller's gate. + +An exhausted loop fails closed. `terminal` distinguishes `converged`, `declined`, +and `exhausted`, and `authorized` is false for the last two, so neither advances. +What an exhausted planning loop *should* do remains an unresolved product +decision under #290 — failing closed is not an answer to it. + +A checkpoint that found no material choice still produces an explicit +`proceed: true` with its reason, so nothing advances because a decision was +absent. + +`` reports which gate the run reached, naming the stage's `terminal` +where a stage stopped. A rejected acceptance finishes as rejected — the flow does +not fall into the accepted branch — and a run stopped earlier renders the +artifact it stopped on rather than a value it never produced. **Missing: stopping at the boundary.** Nesting expresses the gate, and it is what the language supports today, but it is not the same as *stopping*. The run @@ -225,14 +285,17 @@ manual stages. | `instructions` | `InstructionFiles` (text) | every agent prompt | | `handoff` | `Discovery` (text) | handoff `UserCheckpoint`, `Planning` | | `handoffCheckpoint` | handoff `UserCheckpoint` (decision) | the `Planning` gate, and `Planning` | -| `plan` | `Planning` (text) | authorization `UserCheckpoint`, `Implementation` | +| `planning` | `Planning` (structured) | the authorization gate (`.authorized`), the authorization checkpoint (`.plan`, `.review`), and `Implementation` (`.plan`) | | `authorization` | authorization checkpoint (decision) | the `Implementation` gate, and `Implementation` | -| `implementationResult` | `Implementation` (text) | acceptance `UserCheckpoint` | +| `implementation` | `Implementation` (structured) | the acceptance gate (`.authorized`), the acceptance checkpoint (`.report`) | | `acceptance` | acceptance checkpoint (decision) | workflow output, terminal record | -A stage binds rendered text; a checkpoint binds a decision object. The four -`UserCheckpoint` invocations are the only value components here — everything -else declares no `returns`, so `as` binds what it rendered. +`instructions` and `handoff` are rendered text. `planning` and `implementation` +are structured stage results carrying `authorized` and `terminal` alongside the +plan or report, the parsed verdict's fields, and the complete `UserDecision` +that stage resolved. The three checkpoints bind decisions. This document renders +the human-readable reports from those returned fields rather than receiving them +pre-rendered. ## Details From 5b434db6f5da3576937d95fdd1c749ece9d6cee0 Mon Sep 17 00:00:00 2001 From: Taras Mankovski <74687+taras@users.noreply.github.com> Date: Tue, 4 Aug 2026 18:01:01 -0400 Subject: [PATCH 5/8] =?UTF-8?q?=F0=9F=93=9D=20Return=20the=20gate's=20sour?= =?UTF-8?q?ces,=20not=20a=20second=20copy=20of=20its=20answer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 `` 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 `` 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 `` ahead of approval creates it despite the decline. --- specs/adversarial-implementation-workflow.md | 20 ++-- .../Implementation.md | 44 +++++--- .../adversarial-implementation/Planning.md | 44 ++++---- .../adversarial-implementation/artifacts.md | 36 +++++-- workflows/adversarial-implementation/start.md | 102 ++++++++++++------ 5 files changed, 158 insertions(+), 88 deletions(-) diff --git a/specs/adversarial-implementation-workflow.md b/specs/adversarial-implementation-workflow.md index c45bb14f..6599feb8 100644 --- a/specs/adversarial-implementation-workflow.md +++ b/specs/adversarial-implementation-workflow.md @@ -50,12 +50,14 @@ review neither revises, nor creates the deferred issues it proposed, nor accepts; a declined acceptance finishes as rejected. A decision resolved inside a stage is gated the same way, because the stage -returns it. `authorized` — the review's approval *and* a passing verdict — -is what the caller reads, so a stage cannot approve its own advancement. An -exhausted review loop reports `terminal: "exhausted"` with `authorized` false -and therefore fails closed; what the workflow should ultimately do with an -exhausted loop is an unresolved product decision under #290, and failing closed -is not an answer to it. +returns it. The caller reads the decision together with that stage's verdict — +approval *and* a passing review — so a stage cannot approve its own advancement. +A stage returns those two sources and nothing derived from them: a precomputed +flag would be a second copy of the same answer that no schema could hold to +agreement with its sources. The same pair distinguishes a decline from a review +that reached its bound still failing, and neither advances; what the workflow +should ultimately do with an exhausted loop is an unresolved product decision +under #290, and refusing to advance is not an answer to it. `proceed: true` authorizes the exact transition and effects the checkpoint assessed, and nothing more. Free-text fields in a decision record the user's @@ -306,7 +308,11 @@ Two are not implemented: its identity and state (#295). Each environmental operation declares its inputs and preconditions, reconciles -existing state, returns a structured handle, and records its observed effects. +existing state, returns a structured handle, and records its observed effects. A +handle is a structure, never a string standing in for one: `` +resolves a number, URL, head and base identities, state, reviews, comments, and +checks, and a stage that declared any of that as text would fail its own return +validation after the effect had already happened. Rerunning a pull-request operation resolves the existing pull request rather than creating a duplicate (#297). diff --git a/workflows/adversarial-implementation/Implementation.md b/workflows/adversarial-implementation/Implementation.md index 000b9c13..67d9308c 100644 --- a/workflows/adversarial-implementation/Implementation.md +++ b/workflows/adversarial-implementation/Implementation.md @@ -17,12 +17,9 @@ props: returns: report: { type: string } - pullRequest: { type: string } verdictPassed: { type: boolean } review: { type: string } revisionPrompt: { type: string } - authorized: { type: boolean } - terminal: { type: string } findings: type: array items: @@ -220,7 +217,8 @@ remote effects. Pull request: - {pullRequest} + #{pullRequest.number} ({pullRequest.state}) {pullRequest.url} + head {pullRequest.headSha} onto base {pullRequest.baseSha} Result contract: @@ -277,7 +275,8 @@ remote effects. ## Pull request - {pullRequest} + #{pullRequest.number} ({pullRequest.state}) {pullRequest.url} + head {pullRequest.headSha} onto base {pullRequest.baseSha} ## Planner review @@ -336,24 +335,31 @@ remote effects. ## The stage returns its control state Like `Planning`, this is a **value component**: it resolves a user decision -internally, so it returns that decision rather than a rendering of it. -`authorized` is `reviewCheckpoint.proceed && verdict.passed`, and `terminal` is -`converged`, `declined`, or `exhausted`. Final acceptance is reachable only when -`authorized` is true, so a declined or exhausted review cannot be reported as an -accepted change. +internally, so it returns that decision rather than a rendering of it. It hands +back the complete `reviewCheckpoint` decision and the parsed verdict's fields, +and nothing derived from them. The caller reads +`decision.proceed && verdictPassed` directly, so there is no second copy of that +answer to disagree with the first. + +The pull-request handle is **not** returned. `` (#295) resolves a +structured handle carrying the number, URL, head and base identities, state, +reviews, comments, and checks, and `start.md` consumes none of it — the artifact +ledger (#291) records the effect and its handle independently. The `report` +above renders the specific fields a reader needs. A return field typed `string` +would be worse than useless here: a conforming `` would perform its +durable effects and only then fail this component's return validation. If a +later caller genuinely needs the handle, it is added with #295's object schema, +never a placeholder. The agent, parsing, and control-flow syntax runs today. A value component's body runs fail-fast, so the final `` in each repair loop ends the stage rather @@ -362,8 +368,14 @@ than passing malformed data to a durable effect. The user's decision outranks the verdict here too. `reviewCheckpoint.proceed` is read before `verdict.passed`, so a declined pull-request review leaves the loop without revising the implementation and without reporting it as reviewed. The -stage is reached at all only because `start.md` gated it on both -`planning.authorized` and `authorization.proceed`. +stage is reached at all only because `start.md` gated it on +`planning.decision.proceed && planning.verdictPassed` and then on +`authorization.proceed`. + +After the loop, `decision.proceed` true with `verdictPassed` false is what +exhaustion looks like: the user kept approving and the verdict never passed. The +caller's gate rejects that pair, so an exhausted review cannot reach acceptance. +What the workflow should ultimately do about it stays unresolved under #290. ## Approval precedes durable effects diff --git a/workflows/adversarial-implementation/Planning.md b/workflows/adversarial-implementation/Planning.md index 767dd9e6..5fe9d2f3 100644 --- a/workflows/adversarial-implementation/Planning.md +++ b/workflows/adversarial-implementation/Planning.md @@ -20,8 +20,6 @@ returns: verdictPassed: { type: boolean } review: { type: string } revisionPrompt: { type: string } - authorized: { type: boolean } - terminal: { type: string } decision: type: object properties: @@ -213,9 +211,7 @@ resolves factual disagreement, while the user resolves material choices. verdictPassed: verdict.passed, review: verdict.review, revisionPrompt: verdict.revisionPrompt, - decision: planCheckpoint, - authorized: planCheckpoint.proceed && verdict.passed, - terminal: planCheckpoint.proceed ? (verdict.passed ? "converged" : "exhausted") : "declined" + decision: planCheckpoint }} /> ## The stage returns its control state @@ -223,20 +219,26 @@ resolves factual disagreement, while the user resolves material choices. This is a **value component**. A stage that resolves a user decision internally cannot discard it as rendered prose: its caller has to gate on that decision, and prose gives a caller nothing to branch on. So `Planning` declares `returns`, -renders nothing, and hands back the plan, the parsed verdict's fields, the -complete plan-review `UserDecision`, and two derived fields the caller reads -directly: +renders nothing, and hands back the plan, the parsed verdict's fields, and the +complete plan-review `UserDecision`. -- **`authorized`** is `planCheckpoint.proceed && verdict.passed`. Only a plan - that both passed review *and* was approved may reach authorization. Either one - alone is not enough. -- **`terminal`** distinguishes how the loop ended: `converged` (approved and - passing), `declined` (the user stopped it), or `exhausted` (approved but still - failing after `max` rounds). +It returns those sources and nothing derived from them. A field like +`authorized` would be a second copy of `decision.proceed && verdictPassed`, and +a return schema cannot express that the copy must agree with its sources — a +record claiming approval over a decline would validate. The caller reads the two +authoritative fields and computes the gate itself, so there is only ever one +answer. -`authorized` is false for both `declined` and `exhausted`, so neither can -advance the workflow. That is failing closed, not a decision about what an -exhausted loop *should* do. +Three outcomes are distinguishable from those fields alone, which is all a caller +needs: + +| `decision.proceed` | `verdictPassed` | What happened | +| --- | --- | --- | +| `true` | `true` | the review passed and the user approved it | +| `false` | either | the user declined; the loop stopped without revising | +| `true` | `false` | the loop reached `max` still failing — exhaustion | + +Only the first pair advances. The loop is bounded and records why it stopped. `` journals every iteration it enters and one terminal record whose outcome is `break` — a passing @@ -261,10 +263,10 @@ text, raising nothing. **Outstanding gap: the terminal policy for exhaustion.** Reaching `max` completes the loop normally — exhaustion is not a failure and produces no -diagnostic. This stage reports it as `terminal: "exhausted"` with `authorized` -false, so an exhausted loop is distinguishable from a converged one and cannot -advance. That is the minimum needed to keep the workflow safe; it is not the -policy. What the workflow *should* do when five rounds end without a passing +diagnostic. The returned pair identifies it (`decision.proceed` true, +`verdictPassed` false) and the caller's gate refuses it, so an exhausted loop +cannot advance. That is the minimum needed to keep the workflow safe; it is not +the policy. What the workflow *should* do when five rounds end without a passing verdict — return the failing plan, fail the stage, or return to the user — is an unresolved product decision recorded against [issue #290](https://github.com/taras/executable.md/issues/290), whose diff --git a/workflows/adversarial-implementation/artifacts.md b/workflows/adversarial-implementation/artifacts.md index 96914e28..8866848d 100644 --- a/workflows/adversarial-implementation/artifacts.md +++ b/workflows/adversarial-implementation/artifacts.md @@ -138,17 +138,12 @@ declares. `returns`, so their results are validated JSON values bound through `as` rather than text a caller would have to interpret. -- `StageResult` — what `Planning` and `Implementation` return. Each carries its - prose (`plan` or `report`), the parsed verdict's fields, the complete - `UserDecision` that stage resolved internally, and two derived control fields: - `authorized` (`proceed && verdict.passed`) and `terminal` (`converged`, - `declined`, or `exhausted`). `start.md` gates the next transition on - `authorized` and reports `terminal`. A stage that resolved a user decision - cannot return prose alone — its caller has nothing to branch on, and the - authority the checkpoint exercised would be lost at the boundary. -- `UserDecision` — the transition decision a caller gates on, returned directly - by `UserCheckpoint` and embedded in each `StageResult`. Two parts combine - into it. The **decision** is `proceed`, `response`, and `rationale`, validated +- `UserDecision` — the transition decision a caller gates on. `UserCheckpoint` + returns it directly, and `Planning` and `Implementation` each return the one + they resolved internally, alongside their prose and their parsed verdict's + fields. A stage that resolved a user decision cannot return prose alone: its + caller has nothing to branch on, and the authority the checkpoint exercised + would be lost at the boundary. Two parts combine into it. The **decision** is `proceed`, `response`, and `rationale`, validated against one schema on both paths: `` binds it when the assessment reports a material choice, and an explicit `` binds it when there is none. `UserCheckpoint` returns those alongside the assessment fields, so one @@ -158,6 +153,25 @@ than text a caller would have to interpret. artifact ledger (#291) and does not exist. Nothing in the returned value identifies the person who answered. +A stage returns those sources and nothing derived from them. `start.md` computes +its gate as `decision.proceed && verdictPassed` where it uses it, rather than +reading a field the stage precomputed: a return schema can require both fields to +be present but cannot require a derived flag to agree with them, so a record +pairing a declining decision with an approving flag would validate. The same two +fields tell a decline (`proceed` false) from a review that never passed +(`proceed` true, `verdictPassed` false), so no separate outcome label is needed +either. + +Neither stage returns the pull-request handle. `` (#295) resolves a +structured handle carrying the number, URL, head and base identities, state, +reviews, comments, and checks. Nothing in this workflow consumes it, and the +artifact ledger records the effect and its handle independently, so +`Implementation` renders the fields a reader needs into its `report` instead. A +return field typed `string` would be actively harmful: a conforming +`` would perform its durable effects and only then fail the stage's +return validation. If a later caller needs the handle, it is declared with #295's +object schema. + Prompt output used for control flow is JSON parsed against captured draft-07 JSON Schema content. `` exposes the candidate and normalized errors for a visible, bounded correction turn; a final `` prevents invalid data diff --git a/workflows/adversarial-implementation/start.md b/workflows/adversarial-implementation/start.md index 0ae8bcc1..d5b22d00 100644 --- a/workflows/adversarial-implementation/start.md +++ b/workflows/adversarial-implementation/start.md @@ -66,7 +66,7 @@ supplies `request`, `base`, `planner`, and `implementor` (#179). planner={planner} implementor={implementor} as="planning" /> - + ## Implementation plan @@ -89,7 +89,7 @@ supplies `request`, `base`, `planner`, and `implementor` (#179). planner={planner} implementor={implementor} as="implementation" /> - + - + - + # Accepted @@ -123,14 +123,23 @@ supplies `request`, `base`, `planner`, and `implementor` (#179). - # Stopped in implementation: {implementation.terminal} + + # Stopped: the pull-request review never passed - The pull-request review ended `{implementation.terminal}`, so - the change was never offered for acceptance. + The user kept approving and the verdict never passed, so the + change was never offered for acceptance. - {implementation.decision.rationale} + {implementation.review} {implementation.report} + + # Stopped: the pull-request review was declined + + {implementation.decision.rationale} + + {implementation.report} + + @@ -142,14 +151,23 @@ supplies `request`, `base`, `planner`, and `implementor` (#179). - # Stopped in planning: {planning.terminal} + + # Stopped: the plan review never passed - The plan review ended `{planning.terminal}`, so authorization was - never requested. + The user kept approving and the verdict never passed, so + authorization was never requested. - {planning.decision.rationale} + {planning.review} - {planning.plan} + {planning.plan} + + # Stopped: the plan review was declined + + {planning.decision.rationale} + + {planning.plan} + + @@ -177,29 +195,41 @@ component boundary as prose. Two of the gates read a checkpoint this document invoked directly: `handoffCheckpoint.proceed` before `Planning`, and `authorization.proceed` before `Implementation`. The other two read a decision a stage resolved -*internally* and returned: `planning.authorized` and -`implementation.authorized`. Each is `proceed && verdict.passed` — a plan that -was approved but never passed review cannot reach authorization, and one that -passed review but was declined cannot either. +*internally* and returned, together with that stage's verdict: + +``` +planning.decision.proceed && planning.verdictPassed +implementation.decision.proceed && implementation.verdictPassed +``` + +A plan that was approved but never passed review cannot reach authorization, and +one that passed review but was declined cannot either. That second pair is what keeps authority from leaking across a boundary. A stage that asks the user a question and then returns only a report leaves its caller guessing; the caller would ask the next question anyway and could accept a change -whose review the user rejected. Returning `authorized` and `terminal` makes the -internal decision the caller's gate. +whose review the user rejected. Returning the decision itself makes it the +caller's gate. + +The gate is computed here rather than returned by the stage, and that is +deliberate. A returned `authorized` field would be a second copy of the same +answer, and no return schema can require the copy to agree with its sources — a +record pairing a declining decision with an approving flag would validate. One +authoritative pair, read where it is used. -An exhausted loop fails closed. `terminal` distinguishes `converged`, `declined`, -and `exhausted`, and `authorized` is false for the last two, so neither advances. -What an exhausted planning loop *should* do remains an unresolved product -decision under #290 — failing closed is not an answer to it. +The same pair distinguishes the failure modes without a separate label. After a +loop, `decision.proceed` false means the user declined; `decision.proceed` true +with `verdictPassed` false means the loop reached `max` still failing. Neither +passes the gate. What an exhausted loop *should* do remains an unresolved +product decision under #290 — refusing to advance is not an answer to it. A checkpoint that found no material choice still produces an explicit `proceed: true` with its reason, so nothing advances because a decision was absent. -`` reports which gate the run reached, naming the stage's `terminal` -where a stage stopped. A rejected acceptance finishes as rejected — the flow does -not fall into the accepted branch — and a run stopped earlier renders the +`` reports which gate the run reached, telling a decline apart from a +review that never passed. A rejected acceptance finishes as rejected — the flow +does not fall into the accepted branch — and a run stopped earlier renders the artifact it stopped on rather than a value it never produced. **Missing: stopping at the boundary.** Nesting expresses the gate, and it is @@ -285,17 +315,23 @@ manual stages. | `instructions` | `InstructionFiles` (text) | every agent prompt | | `handoff` | `Discovery` (text) | handoff `UserCheckpoint`, `Planning` | | `handoffCheckpoint` | handoff `UserCheckpoint` (decision) | the `Planning` gate, and `Planning` | -| `planning` | `Planning` (structured) | the authorization gate (`.authorized`), the authorization checkpoint (`.plan`, `.review`), and `Implementation` (`.plan`) | +| `planning` | `Planning` (structured) | the authorization gate (`.decision.proceed`, `.verdictPassed`), the authorization checkpoint (`.plan`), and `Implementation` (`.plan`) | | `authorization` | authorization checkpoint (decision) | the `Implementation` gate, and `Implementation` | -| `implementation` | `Implementation` (structured) | the acceptance gate (`.authorized`), the acceptance checkpoint (`.report`) | +| `implementation` | `Implementation` (structured) | the acceptance gate (`.decision.proceed`, `.verdictPassed`), the acceptance checkpoint (`.report`) | | `acceptance` | acceptance checkpoint (decision) | workflow output, terminal record | `instructions` and `handoff` are rendered text. `planning` and `implementation` -are structured stage results carrying `authorized` and `terminal` alongside the -plan or report, the parsed verdict's fields, and the complete `UserDecision` -that stage resolved. The three checkpoints bind decisions. This document renders -the human-readable reports from those returned fields rather than receiving them -pre-rendered. +are structured stage results: the plan or report, the parsed verdict's fields, +and the complete `UserDecision` that stage resolved — the sources a gate reads, +with nothing derived from them. The three checkpoints bind decisions. This +document renders the human-readable reports from those returned fields rather +than receiving them pre-rendered. + +Neither stage returns the pull-request handle. `` (#295) resolves a +structured handle with the number, URL, head and base identities, state, reviews, +comments, and checks; nothing here consumes it, and the artifact ledger (#291) +records it independently. `Implementation` renders the fields a reader needs into +its `report`. ## Details From 5fcc93bfc50f3fd8842bc9386a480c39ab1d7ca7 Mon Sep 17 00:00:00 2001 From: Taras Mankovski <74687+taras@users.noreply.github.com> Date: Tue, 4 Aug 2026 18:22:39 -0400 Subject: [PATCH 6/8] =?UTF-8?q?=F0=9F=93=9D=20Finish=20the=20value-compone?= =?UTF-8?q?nt=20sweep=20and=20give=20the=20reviewer=20the=20whole=20PR?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 `` 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 `` 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 `` 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 `` 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. --- specs/adversarial-implementation-workflow.md | 47 ++++++--- .../Implementation.md | 98 +++++++++++++++---- .../adversarial-implementation/Planning.md | 14 +-- .../UserCheckpoint.md | 5 - .../adversarial-implementation/artifacts.md | 13 +-- .../adversarial-implementation/primitives.md | 15 +-- workflows/adversarial-implementation/start.md | 34 ++++--- 7 files changed, 160 insertions(+), 66 deletions(-) diff --git a/specs/adversarial-implementation-workflow.md b/specs/adversarial-implementation-workflow.md index 6599feb8..f6853c6d 100644 --- a/specs/adversarial-implementation-workflow.md +++ b/specs/adversarial-implementation-workflow.md @@ -194,6 +194,13 @@ The planner reviews the plan against the user intent, repository evidence, instructions, specifications, and observable validation. A failed review returns a focused prompt that the implementor can apply directly. +A pull-request review needs the pull request's complete current state, and the +planner cannot fetch it: the supervised exercise denies agent network access. So +the review prompt renders every category the handle resolves — existing reviews, +comments, and check results — rather than a subset, and names the revision under +review: the diff at the head identity against the base. A verdict describes that +head alone, and a moved head requires a fresh review. + Factual disagreement calls for more evidence. When evidence leaves more than one viable choice, the agents present the options, consequences, and recommendations to the user. Convergence means that the final plan reflects a shared @@ -244,9 +251,10 @@ A stage that only produces material for the next prompt is a text component: `InstructionFiles` and `Discovery` render their result and a caller's `as` binds that text. A stage that resolves a user decision inside itself declares `returns` instead. `Planning` and `Implementation` each run a review loop that -asks the user a question, so each returns its prose, its parsed verdict, the -complete `UserDecision` it resolved, and the derived `authorized` and `terminal` -its caller gates on. `` does the same for a single decision. +asks the user a question, so each returns its prose, its parsed verdict's fields, +and the complete `UserDecision` it resolved — the sources its caller gates on, +and nothing derived from them. `` does the same for a single +decision. The rule is about where authority lives. A controller that resolves a decision and returns only a rendering of it has discarded the thing its caller needs: the @@ -256,15 +264,27 @@ boundary as data or it does not cross at all. ### A stage fails rather than returning a half-record -A stage's `` region runs under the `output` error mode; everything -outside it is documentation and runs under `throw`. An undecided error at either -position fails the run rather than producing a result a caller would bind. The -two modes differ in what a printing boundary may do about it: a `` -region can print an `output` decision instead of failing, and `throw` is the one -mode it cannot replace — a printed error in documentation is one nobody can -read. Neither rescues a stage here, because every stage's parsing sits in -documentation. That is what makes each stage's final `` a gate: malformed -agent output cannot reach control flow or a deterministic effect. +The two component kinds fail differently, and neither can hand a caller a +half-record. + +A **text component** is split by its `` boundary: the region inside runs +under the `output` error mode, everything outside is documentation and runs +under `throw`. `InstructionFiles` and `Discovery` are the two here, and each +puts its work in documentation, so an undecided error ends the run rather than +printing into a region nobody reads. + +A **value component** declares `returns`, so it renders nothing and cannot +contain `` at all — that would be a structural error. Its entire body +runs fail-fast, and a failure binds nothing: there is no partially validated +return for a caller to gate on. `Planning`, `Implementation`, and +`UserCheckpoint` are value components. + +The two error modes differ in what a printing boundary may do about a failure: a +`` region can print an `output` decision instead of failing, and +`throw` is the one mode it cannot replace — a printed error in documentation is +one nobody can read. Neither rescues a stage here. That is what makes each +stage's final `` a gate: malformed agent output cannot reach control flow +or a deterministic effect. A failing region keeps only what it had already rendered. That partial text reaches the output stream; nothing after the failure does. Continuing after a @@ -305,7 +325,8 @@ Two are not implemented: establish is itself shipped and already inherited by files, globs, processes, and agents. - `` creates or resolves the pull request for a branch and returns - its identity and state (#295). + a structured handle: number, URL, head and base identities, state, reviews, + comments, and checks (#295). Each environmental operation declares its inputs and preconditions, reconciles existing state, returns a structured handle, and records its observed effects. A diff --git a/workflows/adversarial-implementation/Implementation.md b/workflows/adversarial-implementation/Implementation.md index 67d9308c..08f9c80c 100644 --- a/workflows/adversarial-implementation/Implementation.md +++ b/workflows/adversarial-implementation/Implementation.md @@ -220,14 +220,38 @@ remote effects. #{pullRequest.number} ({pullRequest.state}) {pullRequest.url} head {pullRequest.headSha} onto base {pullRequest.baseSha} + Reviews: + + + - {review.author} ({review.state}) on {review.headSha}: {review.body} + + + Comments: + + + - {comment.author} on {comment.path}: {comment.body} + + + Checks: + + + - {check.name}: {check.status} / {check.conclusion} — {check.url} + + Result contract: {pullRequestVerdictSchema} - Review the pull request against the authorized plan and instruction - content above. Classify every finding and include a focused revision - prompt when the review fails. Return only JSON matching the supplied - result contract. + Review the diff at {pullRequest.headSha} against {pullRequest.baseSha}, + together with the authorized plan and instruction content above. The + reviews, comments, and checks listed here are the complete current + state; you have no network access, so do not attempt to fetch more. + + A verdict is about one head. If the head moves, this verdict no longer + describes the pull request and a fresh review is required. + + Classify every finding and include a focused revision prompt when the + review fails. Return only JSON matching the supplied result contract. @@ -278,6 +302,24 @@ remote effects. #{pullRequest.number} ({pullRequest.state}) {pullRequest.url} head {pullRequest.headSha} onto base {pullRequest.baseSha} + ### Reviews + + + - {review.author} ({review.state}) on {review.headSha} + + + ### Comments + + + - {comment.author} on {comment.path}: {comment.body} + + + ### Checks + + + - {check.name}: {check.status} / {check.conclusion} + + ## Planner review Passed: {verdict.passed} @@ -351,19 +393,41 @@ and nothing derived from them. The caller reads `decision.proceed && verdictPassed` directly, so there is no second copy of that answer to disagree with the first. -The pull-request handle is **not** returned. `` (#295) resolves a -structured handle carrying the number, URL, head and base identities, state, -reviews, comments, and checks, and `start.md` consumes none of it — the artifact -ledger (#291) records the effect and its handle independently. The `report` -above renders the specific fields a reader needs. A return field typed `string` -would be worse than useless here: a conforming `` would perform its -durable effects and only then fail this component's return validation. If a -later caller genuinely needs the handle, it is added with #295's object schema, -never a placeholder. - -The agent, parsing, and control-flow syntax runs today. A value component's body -runs fail-fast, so the final `` in each repair loop ends the stage rather -than passing malformed data to a durable effect. +## The reviewer sees the complete pull request + +`` (#295) resolves a structured handle carrying the number, URL, +head and base identities, state, reviews, comments, and checks. This stage +consumes all of it, because the planner cannot recover any of it itself: agent +network access is denied for the supervised exercise, so whatever the prompt does +not render is invisible to the review. Every category is rendered explicitly — +each collection iterated with ``, never stringified as an object — into +both the planner prompt and the checkpoint material the user reads. A review that +cannot see a failing check or an existing objection is not adversarial, it is +uninformed. + +The prompt names the revision under review: the planner reviews the diff at +`headSha` against `baseSha`, and a verdict describes that head only. A moved head +invalidates it and a fresh review is required — the same rule #295 states for a +stored verdict. + +The **member field names** used above — a review's `author`, `state`, `headSha` +and `body`, a comment's `author`, `path` and `body`, a check's `name`, `status`, +`conclusion` and `url` — are #295's to settle. This document depends on that +schema rather than defining a competing one; what is settled here is that the +planner receives the complete snapshot, not what each member is called. + +The handle stays internal: it is not part of this component's declared return, +because `start.md` gates on the verdict and decision rather than on pull-request +state, and the artifact ledger (#291) records the effect and its handle +independently. A return field typed `string` would be worse than useless — a +conforming `` would perform its durable effects and only then fail +this component's return validation. If a later caller genuinely needs the handle, +it is declared with #295's object schema, never a placeholder. + +The agent, parsing, and control-flow syntax runs today. This component declares +`returns`, so it contains no `` and its whole body runs fail-fast: the +final `` in each repair loop ends the stage rather than passing malformed +data to a durable effect, and a failure binds nothing at all. The user's decision outranks the verdict here too. `reviewCheckpoint.proceed` is read before `verdict.passed`, so a declined pull-request review leaves the loop diff --git a/workflows/adversarial-implementation/Planning.md b/workflows/adversarial-implementation/Planning.md index 5fe9d2f3..5dc620a4 100644 --- a/workflows/adversarial-implementation/Planning.md +++ b/workflows/adversarial-implementation/Planning.md @@ -254,12 +254,14 @@ presenting it as reviewed — a rejection is neither a revision request nor an acceptance. `` works from that nested position, so the two conditions compose without a flag binding between them. -A value component's body runs fail-fast, which is what makes the bounded repair -turns a real gate: `` absorbs a malformed verdict so the document can -show the correction prompt, and the final `` ends the stage if the -candidate is still invalid. `throwOnError` on each `` is required for the -same reason — a failed prompt without it records its failure and returns its -text, raising nothing. +This component declares `returns`, so it contains no `` and there is no +documentation split: the whole body runs fail-fast and binds nothing if it +fails. That is what makes the bounded repair turns a real gate — `` +absorbs a malformed verdict so the document can show the correction prompt, and +the final `` ends the stage if the candidate is still invalid, rather than +returning something half-formed for a caller to branch on. `throwOnError` on each +`` is required for the same reason: a failed prompt without it records +its failure and returns its text, raising nothing. **Outstanding gap: the terminal policy for exhaustion.** Reaching `max` completes the loop normally — exhaustion is not a failure and produces no diff --git a/workflows/adversarial-implementation/UserCheckpoint.md b/workflows/adversarial-implementation/UserCheckpoint.md index afd2ba26..23930f7d 100644 --- a/workflows/adversarial-implementation/UserCheckpoint.md +++ b/workflows/adversarial-implementation/UserCheckpoint.md @@ -218,11 +218,6 @@ from continuing after the bounded repair loop with malformed output. The schema is ordinary captured document content rather than a registry entry or `` prop. -A value component's body is not documentation in the ordinary sense — a value -root and a value component both run fail-fast, so the assessment, the repair -loop, and the elicitation all end the stage rather than binding something a -caller would mistake for a decision. - `` reads the bare binding because that is the expression- prop spelling current main supports, while `{props.purpose}` in the prompt body is text interpolation; #305 unifies the two. diff --git a/workflows/adversarial-implementation/artifacts.md b/workflows/adversarial-implementation/artifacts.md index 8866848d..5ab3b76b 100644 --- a/workflows/adversarial-implementation/artifacts.md +++ b/workflows/adversarial-implementation/artifacts.md @@ -86,12 +86,13 @@ else renders nothing — not an error, a value with no destination. ### What a failing stage returns Nothing partial. A text component's `` region runs under the `output` -error mode and everything outside it under `throw`; a value component's whole -body runs fail-fast. Either way an undecided error fails the run rather than -producing a result the caller would bind, and a value component that fails binds -nothing at all — there is no half-validated return. A failing `` region -keeps only the text it had already rendered, and that text reaches the output -stream; nothing after the failure does. A failed run is still a complete +error mode and everything outside it under `throw`. A value component has no such +split — it renders nothing, so `` inside one is a structural error — and +its whole body runs fail-fast. Either way an undecided error fails the run rather +than producing a result the caller would bind, and a value component that fails +binds nothing at all: there is no half-validated return. A failing `` +region keeps only the text it had already rendered, and that text reaches the +output stream; nothing after the failure does. A failed run is still a complete record — replay restores its output and its failure without re-executing anything. diff --git a/workflows/adversarial-implementation/primitives.md b/workflows/adversarial-implementation/primitives.md index ed1cd531..20a32e31 100644 --- a/workflows/adversarial-implementation/primitives.md +++ b/workflows/adversarial-implementation/primitives.md @@ -67,12 +67,15 @@ structure and read where an error is raised: A failing region keeps what it had already rendered: that text reaches the output stream, and nothing after the failure does. -A component body is split by its `` boundary: the region inside runs -under `output`, everything outside is documentation and runs under `throw`. -Every stage in this workflow puts its prompts, parsing, and control flow outside -``, so a stage returns a complete validated result or it fails — it -never returns a half-record. `` and `` would let a document -handle a failure instead of ending on it; both are defined and unbuilt. +A **text component**'s body is split by its `` boundary: the region +inside runs under `output`, everything outside is documentation and runs under +`throw`. A **value component** declares `returns` and renders nothing, so it +cannot contain `` — that is a structural error — and its entire body +runs fail-fast, binding nothing when it fails. + +Either way a component returns a complete validated result or it fails; neither +kind can hand a caller a half-record. `` and `` would let a +document handle a failure instead of ending on it; both are defined and unbuilt. **Missing: printing an `output` decision.** The `output` row above is the settled contract, and the engine does not meet it yet — an outer diff --git a/workflows/adversarial-implementation/start.md b/workflows/adversarial-implementation/start.md index d5b22d00..ff4e258f 100644 --- a/workflows/adversarial-implementation/start.md +++ b/workflows/adversarial-implementation/start.md @@ -257,13 +257,14 @@ use the bare spelling in expression props, and #305 migrates them. ## Error modes in a stage -Every stage component is split by its `` boundary, and the two halves -fail differently: +How a component fails depends on which kind it is. + +A **text component** — `InstructionFiles` and `Discovery` — is split by its +`` boundary: - Everything **outside** `` is documentation and runs under the `throw` error mode. The first error stops the body and fails the run, and no - `` region can print it instead — which is what makes each - stage's final `` a real gate rather than a formality. + `` region can print it instead. - The **`` region** runs under the `output` error mode: an undecided error there fails the run too, though a `` region may print it instead. Either way the region keeps what it had already rendered — that @@ -271,10 +272,15 @@ fail differently: Printing an `output` decision is the contract; the engine does not do it yet (#327). -So a stage either returns a complete, schema-validated result or it fails. It -never returns a half-record. The `throwOnError` on each `` is -load-bearing for the same reason: a failed prompt without it records its -failure and returns its text, raising nothing for the error mode to decide. +A **value component** — `UserCheckpoint`, `Planning`, `Implementation` — has no +such split. Declaring `returns` means it renders nothing, so `` inside +one is a structural error; its whole body runs fail-fast and a failure binds +nothing at all. There is no partially validated return for a caller to gate on. + +Either way a stage returns a complete, schema-validated result or it fails, and +never a half-record. The `throwOnError` on each `` is load-bearing for +the same reason: a failed prompt without it records its failure and returns its +text, raising nothing for the error mode to decide. ## What runs today @@ -327,11 +333,13 @@ with nothing derived from them. The three checkpoints bind decisions. This document renders the human-readable reports from those returned fields rather than receiving them pre-rendered. -Neither stage returns the pull-request handle. `` (#295) resolves a -structured handle with the number, URL, head and base identities, state, reviews, -comments, and checks; nothing here consumes it, and the artifact ledger (#291) -records it independently. `Implementation` renders the fields a reader needs into -its `report`. +Neither stage returns the pull-request handle, but `Implementation` consumes it +in full. `` (#295) resolves the number, URL, head and base +identities, state, reviews, comments, and checks; the planner has no network +access, so `Implementation` renders every category explicitly into the review +prompt and into the checkpoint material, and the artifact ledger (#291) records +the effect independently. What `start.md` gates on is the verdict and the +decision, so the handle itself never crosses the stage boundary. ## Details From ef9bcbabdd4114e5781ad90d62166e60331a6231 Mon Sep 17 00:00:00 2001 From: Taras Mankovski <74687+taras@users.noreply.github.com> Date: Tue, 4 Aug 2026 18:41:55 -0400 Subject: [PATCH 7/8] =?UTF-8?q?=F0=9F=93=9D=20State=20the=20handle=20bound?= =?UTF-8?q?ary,=20the=20two=20text=20components,=20and=20the=20objection?= =?UTF-8?q?=20text?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 `` and `` reads inside ``, and probing that case corrected a second assumption in the same sentence: `` 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. --- specs/adversarial-implementation-workflow.md | 25 ++++++++++++++++--- .../Implementation.md | 17 ++++++++----- .../adversarial-implementation/artifacts.md | 16 +++++++----- workflows/adversarial-implementation/start.md | 19 +++++++++----- 4 files changed, 56 insertions(+), 21 deletions(-) diff --git a/specs/adversarial-implementation-workflow.md b/specs/adversarial-implementation-workflow.md index f6853c6d..7da30e06 100644 --- a/specs/adversarial-implementation-workflow.md +++ b/specs/adversarial-implementation-workflow.md @@ -201,6 +201,11 @@ comments, and check results — rather than a subset, and names the revision und review: the diff at the head identity against the base. A verdict describes that head alone, and a moved head requires a fresh review. +The user's checkpoint carries the same evidence. An existing objection reaches +the person approving the change in its own words, not only as the planner +summarized it — a reviewer's own text is what a later reader needs to judge +whether the objection was answered. + Factual disagreement calls for more evidence. When evidence leaves more than one viable choice, the agents present the options, consequences, and recommendations to the user. Convergence means that the final plan reflects a shared @@ -269,9 +274,23 @@ half-record. A **text component** is split by its `` boundary: the region inside runs under the `output` error mode, everything outside is documentation and runs -under `throw`. `InstructionFiles` and `Discovery` are the two here, and each -puts its work in documentation, so an undecided error ends the run rather than -printing into a region nobody reads. +under `throw`. The two here sit on opposite sides of that line, and they fail +differently as a result. + +`Discovery` runs its prompt in documentation and renders only the captured +result. A failed prompt is raised there — `throwOnError` is what raises it — and +`throw` ends the run. + +`InstructionFiles` is the other way round: its `` and `` reads *are* +its `` region. `` prints its own failures rather than propagating +them, so an unreadable instruction file becomes a printed error and the region's +`output` mode never decides anything. The run continues, and what stops the +caller from proceeding on nothing is the binding rule: `as` refuses a body +holding a printed error, so `instructions` stays unbound and the error surfaces +at the invocation rather than inside the text a prompt would quote. + +That difference is worth knowing before relying on either: a missing instruction +file is visible and non-fatal, while a failed discovery prompt is fatal. A **value component** declares `returns`, so it renders nothing and cannot contain `` at all — that would be a structural error. Its entire body diff --git a/workflows/adversarial-implementation/Implementation.md b/workflows/adversarial-implementation/Implementation.md index 08f9c80c..c0a846ff 100644 --- a/workflows/adversarial-implementation/Implementation.md +++ b/workflows/adversarial-implementation/Implementation.md @@ -305,7 +305,7 @@ remote effects. ### Reviews - - {review.author} ({review.state}) on {review.headSha} + - {review.author} ({review.state}) on {review.headSha}: {review.body} ### Comments @@ -399,11 +399,16 @@ answer to disagree with the first. head and base identities, state, reviews, comments, and checks. This stage consumes all of it, because the planner cannot recover any of it itself: agent network access is denied for the supervised exercise, so whatever the prompt does -not render is invisible to the review. Every category is rendered explicitly — -each collection iterated with ``, never stringified as an object — into -both the planner prompt and the checkpoint material the user reads. A review that -cannot see a failing check or an existing objection is not adversarial, it is -uninformed. +not render is invisible to the review. Every category — reviews, comments, and +checks — is rendered explicitly into both the planner prompt and the checkpoint +material the user reads, each collection iterated with `` and never +stringified as an object. A review that cannot see a failing check or an existing +objection is not adversarial, it is uninformed. + +The two surfaces carry the same categories and the same consequential content, +including each review's body: a user approving the change reads the original +objections rather than the planner's summary of them. The checkpoint omits only a +check's link, which is a way to find evidence rather than evidence itself. The prompt names the revision under review: the planner reviews the diff at `headSha` against `baseSha`, and a verdict describes that head only. A moved head diff --git a/workflows/adversarial-implementation/artifacts.md b/workflows/adversarial-implementation/artifacts.md index 5ab3b76b..2f0077e7 100644 --- a/workflows/adversarial-implementation/artifacts.md +++ b/workflows/adversarial-implementation/artifacts.md @@ -163,12 +163,16 @@ fields tell a decline (`proceed` false) from a review that never passed (`proceed` true, `verdictPassed` false), so no separate outcome label is needed either. -Neither stage returns the pull-request handle. `` (#295) resolves a -structured handle carrying the number, URL, head and base identities, state, -reviews, comments, and checks. Nothing in this workflow consumes it, and the -artifact ledger records the effect and its handle independently, so -`Implementation` renders the fields a reader needs into its `report` instead. A -return field typed `string` would be actively harmful: a conforming +Neither stage *returns* the pull-request handle, and the boundary is worth +stating exactly. `` (#295) resolves a structured handle carrying the +number, URL, head and base identities, state, reviews, comments, and checks. +`Implementation` **consumes all of it**, internally: the planner has no network +access, so the stage renders every category into the review prompt and the +checkpoint material. `start.md` never receives it — what crosses the stage +boundary is the verdict and the decision it gates on. The artifact ledger records +the effect and its handle independently (#291). + +A return field typed `string` would be actively harmful: a conforming `` would perform its durable effects and only then fail the stage's return validation. If a later caller needs the handle, it is declared with #295's object schema. diff --git a/workflows/adversarial-implementation/start.md b/workflows/adversarial-implementation/start.md index ff4e258f..933dcdb7 100644 --- a/workflows/adversarial-implementation/start.md +++ b/workflows/adversarial-implementation/start.md @@ -265,19 +265,25 @@ A **text component** — `InstructionFiles` and `Discovery` — is split by its - Everything **outside** `` is documentation and runs under the `throw` error mode. The first error stops the body and fails the run, and no `` region can print it instead. -- The **`` region** runs under the `output` error mode: an undecided +- The **`` region** runs under the `output` error mode: an *undecided* error there fails the run too, though a `` region may print it instead. Either way the region keeps what it had already rendered — that partial text reaches the output stream, and nothing after the failure does. Printing an `output` decision is the contract; the engine does not do it yet (#327). +"Undecided" is the operative word. `InstructionFiles` puts its `` reads in +its `` region, and `` prints its own failures, so an unreadable +instruction file is already decided as a printed error and the region's mode +never sees it. The run continues; what stops the caller is that `as` refuses a +body holding a printed error, so `instructions` stays unbound. + A **value component** — `UserCheckpoint`, `Planning`, `Implementation` — has no such split. Declaring `returns` means it renders nothing, so `` inside one is a structural error; its whole body runs fail-fast and a failure binds nothing at all. There is no partially validated return for a caller to gate on. -Either way a stage returns a complete, schema-validated result or it fails, and +By one route or another a caller receives a complete result or nothing — never a half-record. The `throwOnError` on each `` is load-bearing for the same reason: a failed prompt without it records its failure and returns its text, raising nothing for the error mode to decide. @@ -336,10 +342,11 @@ than receiving them pre-rendered. Neither stage returns the pull-request handle, but `Implementation` consumes it in full. `` (#295) resolves the number, URL, head and base identities, state, reviews, comments, and checks; the planner has no network -access, so `Implementation` renders every category explicitly into the review -prompt and into the checkpoint material, and the artifact ledger (#291) records -the effect independently. What `start.md` gates on is the verdict and the -decision, so the handle itself never crosses the stage boundary. +access, so `Implementation` renders every category — reviews with their bodies, +comments, and checks — explicitly into the review prompt and into the checkpoint +material, and the artifact ledger (#291) records the effect independently. What +`start.md` gates on is the verdict and the decision, so the handle itself never +crosses the stage boundary. ## Details From 382d11500fd403a15f18c2da926101870724d156 Mon Sep 17 00:00:00 2001 From: Taras Mankovski <74687+taras@users.noreply.github.com> Date: Tue, 4 Aug 2026 18:57:43 -0400 Subject: [PATCH 8/8] =?UTF-8?q?=F0=9F=93=9D=20Show=20the=20user=20every=20?= =?UTF-8?q?effect=20their=20approval=20performs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `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 ``, 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 ``. 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 `` 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. --- specs/adversarial-implementation-workflow.md | 17 +++++--- .../Implementation.md | 40 ++++++++++++++++--- 2 files changed, 46 insertions(+), 11 deletions(-) diff --git a/specs/adversarial-implementation-workflow.md b/specs/adversarial-implementation-workflow.md index 7da30e06..228f9bc7 100644 --- a/specs/adversarial-implementation-workflow.md +++ b/specs/adversarial-implementation-workflow.md @@ -60,11 +60,18 @@ should ultimately do with an exhausted loop is an unresolved product decision under #290, and refusing to advance is not an answer to it. `proceed: true` authorizes the exact transition and effects the checkpoint -assessed, and nothing more. Free-text fields in a decision record the user's -reasoning; nothing reads them to amend what runs, because an effect that has -already executed cannot be amended by prose. This is why durable effects follow -their approval rather than preceding it — the deferred issues a review proposes -are created inside the approved path, after the checkpoint. +assessed, and nothing more. The rule binds the material as much as the decision: +whatever an approval sets in motion has to appear in what the user assessed, in +its original form. A review checkpoint therefore shows the revision prompt that +would reach the implementor and the evidence attached to every finding that would +become an issue — not a summary of either, since approving instructions nobody +read is the same authority leak as not asking. + +Free-text fields in a decision record the user's reasoning; nothing reads them to +amend what runs, because an effect that has already executed cannot be amended by +prose. This is why durable effects follow their approval rather than preceding +it — the deferred issues a review proposes are created inside the approved path, +after the checkpoint. A checkpoint that found no material choice produces an explicit `proceed: true` recording why, so a transition never advances because a decision was missing. diff --git a/workflows/adversarial-implementation/Implementation.md b/workflows/adversarial-implementation/Implementation.md index c0a846ff..cc599bc8 100644 --- a/workflows/adversarial-implementation/Implementation.md +++ b/workflows/adversarial-implementation/Implementation.md @@ -326,12 +326,27 @@ remote effects. {verdict.review} + ## What approval performs + + Revision prompt sent to the implementor when the verdict has not passed: + + {verdict.revisionPrompt} + + A finding whose disposition is `defer` becomes an issue, with the evidence + shown beneath it. + ### {finding.title} Disposition: {finding.disposition} {finding.description} + + Evidence: + + + - {item} + ` for every finding -the verdict marked `defer`. It is not an invitation to amend them. The free-text -`response` and `rationale` are a record of the user's reasoning, and nothing -reads them to change which effects run: an effect that already executed cannot -be silently amended by prose. A user who wants different effects declines, and -`proceed: false` performs none of them — no issue, no revision turn, no +in the material the checkpoint assessed. That rule only means something if the +material actually shows them, so `checkpointMaterial` carries every value an +approval sets in motion, unchanged and unsummarized: + +- the **revision prompt** that goes to the implementor when the verdict has not + passed — the literal `verdict.revisionPrompt`, not a description of it; and +- each finding's **evidence**, because `` receives the complete finding + and a `defer` disposition turns it into a durable GitHub object. + +Approving instructions or evidence the user never read would be the same +authority leak as not asking at all. It is not an invitation to amend them +either: the free-text `response` and `rationale` record the user's reasoning, and +nothing reads them to change which effects run — an effect that already executed +cannot be silently amended by prose. A user who wants different effects declines, +and `proceed: false` performs none of them — no issue, no revision turn, no acceptance. `` (#294), `` (#295), and `` (#296) do not exist,