diff --git a/specs/adversarial-implementation-workflow.md b/specs/adversarial-implementation-workflow.md new file mode 100644 index 00000000..988a16d3 --- /dev/null +++ b/specs/adversarial-implementation-workflow.md @@ -0,0 +1,820 @@ +# 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 executable sketch is organized around a compact +[entry document](../workflows/adversarial-implementation/start.md), with stage and +runtime details in adjacent Markdown files. `architecture.md` and the +[workflow Workspace specification](./workflow-workspace-spec.md) own the +implementation invariants and the observable `xmd workflow` contract; this +document owns the roles, the authority boundaries, and the review and deferral +contracts the workflow is built to satisfy. + +The dependency map is [#218](https://github.com/taras/executable.md/issues/218), +which orders every capability this target still needs. + +## 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, + proposes a concrete implementation plan, and then proposes the change itself + as executable source rather than performing it. +- 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. + +That authority controls execution rather than describing it. A checkpoint binds +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. 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. 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. + +Stopping *at* the boundary is a durable suspension rather than a construct. A +checkpoint that reaches a person records its pending request and the Workspace +frontier, releases the executor, and reports the run ID and stop reason; +`xmd workflow resume` continues there once the answer is available. Neither the +process nor the Agent sessions need to stay alive in between. That lifecycle is +#366 for foreground start and resume and #367 for suspension, ownership, and +stop-reason inspection, and neither is built. `` is not the answer: it was +rejected as architecture, because the root document is the workflow and a durable +run may continue through several document executions without inventing +subdivisions between them (#298, closed). + +## 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 proposes the change as XMD; XMD performs it + → XMD stages, commits, and pushes, then opens the pull request + → 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. + +The implementor never writes to the repository itself. Agents are read-only +under a workflow run, so the change reaches the Workspace as constrained +generated XMD, and staging, committing, pushing, and pull-request creation are +separate deterministic effects the document performs. **Agents inspect; XMD +mutates.** + +### Two execution environments + +The command selects the environment; the document describes the procedure. + +`xmd run` executes against the caller's current environment and promises no +restoration. `xmd workflow start` creates a workflow run with one implicit root +Workspace that supplies the filesystem, repository, process, and +working-directory capabilities the stages use, retains the filtered journal, and +returns when execution completes, suspends, fails, is cancelled, or is +interrupted. `xmd workflow resume ` continues from the journal frontier +under the retained definition and props. The same declarative components work in +both; durability comes from the host rather than from a second spelling of +`` or `` (#366, unbuilt). + +Until that command exists, the first exercise runs one stage at a time under +user control in one document execution and one existing working directory. The +user inspects each result and manually starts the next stage. That manual +boundary lets the exercise test the interview, handoff, plan, review, decision, +and revision contracts before the retained lifecycle exists. + +The automated form treats 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 durable signal the run is waiting on arrives. +3. The user provides direct input. + +Stop arbitration is not implemented (#300), and premature watcher semantics are +deliberately excluded from it. 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. Under +`xmd workflow` the same question becomes a durable suspension that releases the +executor and resumes later. Until start and resume exist, the user still +supplies decisions between manually invoked stages whenever the work stopped at a +boundary rather than at a question inside one document execution. + +### 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. + +A pull-request review needs the pull request's complete current state, and the +planner cannot fetch it: agent network access is denied. Whatever the prompt does +not render is invisible to the review. The prompt therefore names the revision +under review — the change at the head identity against the base — and renders the +source that performed it, the implementor's report, and the pull request's +identity. A verdict describes that head alone, and a moved head requires a fresh +review. + +``'s creation result is deliberately minimal: stable provider +identity, number, URL, state, head SHA and base SHA (#295). Existing reviews, +comments and check results are separate reads rather than fields on a creation +result that would otherwise claim to stay fresh against a remote that keeps +changing. **That read is not defined by any open issue.** The requirement it must +satisfy is unchanged: the reviewer receives every existing review with its body, +every comment, and every check result, iterated rather than stringified — a +review that cannot see a failing check or an existing objection is not +adversarial, it is uninformed. + +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 +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. + +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'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 +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 + +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`. 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 document execution. + +`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. Execution 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 +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 +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 document execution 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. + +## 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. + +The rest are not implemented, and they are explicit composition rather than one +implicit workspace: + +- `` authorizes a Git locator, resolves an optional base once, pins + that commit, and creates the named primary checkout inside the run's Workspace. + `` adds a named linked checkout on its own branch. Both install + contextual cwd while rendering their children and bind their Workspace-relative + path through `as` (#293). Names are stable component identity, not lookup keys + into hidden configuration: a locator and a base are ordinary root props or + expressions. Two repositories are two `` elements, and no + transaction spans them. +- `` is lexical cwd and nothing else. Making a directory readable by an + Agent is `` (#302), a separate operation on purpose. +- ``, `` and staged-only `` operate on the + contextual checkout as Workspace-local durable effects (#294). `` is + explicit and separate (#370), and `` requires that pushed head + rather than performing a hidden push (#295). +- `` reconciles an approved deferred finding (#296), over the shared + external reconciliation of #297. + +Each environmental operation declares its inputs and preconditions, reconciles +existing state, returns a structured result, and records its observed effects. A +result is a structure, never a string standing in for one: `` +resolves stable provider identity, number, URL, state, head SHA and base SHA, 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, through the +effect's natural key as well as its stable identity (#297). + +`` journals reconciliation evidence — repository and worktree +identity, branch, commit and parent SHAs, tree SHA, staged paths, message +evidence — rather than Git object contents. Replaying a completed commit creates +no second commit, and replaying a completed push performs no remote mutation. +The first local provider is expected to use native Git for the most faithful +checkout, worktree and object-cache behavior; a browser provider may implement +the same contract differently, including emulating named worktrees as separate +retained checkouts, without changing what a document writes (#293, #410). +Initial checkout speed is provider-owned acceleration: a host-local mirror or +object cache may change how fast a clone is, never what the run records or how it +replays. + +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. + +### Retained run state + +Retention exists. A workflow run owns one SQLite database holding its filtered +journal and effect results, versioned Workspace roots and content, Repository and +Worktree metadata, and Agent-session mappings, and it is found by public run ID +alone (#291, closed). Every Workspace-local expansion publishes its mutation, the +resulting logical Workspace root, and its filtered journal result in one +transaction — all three commit or none does (#365, closed). Alongside the journal +the run retains its immutable identity, one of six statuses, a nullable stop +reason, replaceable retrieval metadata, and one document-execution record per +start and per resume. + +There is no sidecar Git history. Earlier revisions of this document put artifact +versions in Git objects under `refs/xmd/runs`; run state lives in the run's own +database instead, which is why a run survives independently of any repository it +touched and why deleting a run never claims to undo a push. + +Co-location does not make arbitrary filesystem content journal or training data. +Journal events reach storage already filtered by the pre-persistence secret gate, +and storage adds no second policy: a rejected gate leaves nothing behind. Making +that gate default-on for execution, with its CLI opt-out and warning, is #199. + +`JournalProvenance` is what makes retained history evidence rather than merely +storage. It is a non-operational, equality-only witness that a live publication +stream descends from the exact journal backend a provider selected for one +workflow run. It grants no append, read, execution, publication, or reconciliation +capability, and it is meaningful only because the provider retains the witness it +established and later requires exact equality. The generic pre-persistence guard +preserves nothing; the trusted secret-filter wrapping site preserves provenance +explicitly, so a filtered journal — including one wrapped more than once — still +carries the witness its source carried. Another run's journal, an in-memory +stream, a copied property, an ordinary guard, or a look-alike is refused before +any mutation or publication. For a review workflow that is the difference between +"the history says the pull request was created" and "this history is the one this +run wrote." + +Every committed journal event references the logical Workspace root current when +it was written, and only committed event boundaries are checkpoints — which is +what makes an event selectable for a compatible history fork later (#368). + +Most of what a stage produces is already journaled. `` records every +iteration it enters and one terminal `break`, `exhausted`, or `error` outcome, +and refuses a replay whose stored outcome or iteration count disagrees with what +this execution reached — there is no `cancelled` loop outcome, because run-level +cancellation and stop reasons are retained run state rather than loop state. Each +`` is one durable operation carrying its identity, input, agent and +session, terminal status, text, and structured failure. `` journals 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. + +`xmd workflow history` is how a person reads that back: stable public event IDs +with each event's operation, source location, normalized evaluated arguments, +result or normalized error, Workspace version, and forkability reason. It is +read-only — it attaches no Workspace, Agent, or external provider and cannot +advance a run — and it exposes no value the security policy has not already seen. +That surface, with `status`, `list`, cancellation and deletion, is #367 and is +unbuilt. + +**Still missing: who answered.** The journal records the validated decision, the +question fingerprint, and the document execution it belongs to. It does not +record the actor identity behind an elicitation response, so a decision is +attributable to a run and an expansion but not to a person. No open issue owns +that yet. + +Replay is what makes a resumed stage possible: a document execution that failed +is still a complete record, and replaying it restores the output and the failure +without re-executing anything. Completed durable effects restore their recorded +results; ephemeral operations run again only to rebuild live structure. Replay +never asks current state to prove a past effect, and a completed root result +returns without expanding the document or attaching any provider — so replaying a +finished run does not reclone, recommit, or repush. Missing or corrupt +authoritative Workspace state fails explicitly rather than being silently +recreated. + +Prompts therefore receive their content from restored values rather than from a +file an agent was asked to locate and read. A generated file is an optional +export, not canonical run state, and reading one does not inherently save tokens +because its contents still enter model context. + +### Agent authority and generated XMD + +Under `xmd workflow` an Agent is mandatorily read-only. Enforcement has three +layers: the provider permission bridge allows only read and search operations, +the provider runs in its native read-only sandbox, and registered Workspace paths +are presented as read-only filesystem views. ``, repository `.codex` +or `.claude` configuration, and prompt content cannot exceed that host ceiling, +and a provider that cannot enforce it fails before Prompt execution (#302). + +The ceiling is therefore the host's, not the document's. There is no `` +component and no document prop that grants an Agent write access; earlier +revisions of this document declared readable roots, writable roots, environment, +process, and network policy as markup, and that authority moved to where a +document cannot widen it. What the document still declares is *access*: +`` registers a read-only Workspace path with its enclosing Agent +session, in document order, with no special first directory. + +An Agent that cannot write proposes changes instead. It returns an XMD fragment, +and a constrained evaluator parses the complete fragment before its first effect, +resolves an allowlist to pinned component identities supplied by the trusted +parent definition, refuses everything outside that set — eval and exec blocks, +imports, native execution, arbitrary JavaScript expressions — and only then +expands what it admitted (#369). Rejected syntax produces no partial effect. The +allowlist is authority rather than prompting guidance: generated source cannot +grant itself push, pull-request, or secret access by naming a component. The exact +filtered generated source is retained, so replay expands the same fragment without +asking the Agent again, and a reviewer and the user read the literal source that +performed the change. + +Every file the workflow writes is consequently an ordinary durable XMD effect +with its own expansion identity, journal result, and Workspace transaction — +never a side effect of an agent process. + +Agent network access is denied, which is why a review prompt must render +everything the reviewer has to judge rather than pointing at it. + +### Experiment isolation + +The run pins its Repository base once and composes named checkouts from it. +Planner discovery, handoff validation, implementor planning, implementation, and +review use the same pinned revision even if the base branch moves, and replay +does not re-query a moving branch. + +A worktree isolates experimental Git state from the user's current checkout. It +is composition and isolation, not a security boundary — the security boundary is +the host's Agent ceiling and the Workspace the document's file operations resolve +inside. + +`` and `` (#293) and `` (#302) are not +implemented. `API.Files` and its host provider are: every document file operation +routes through that provider, which confines document paths to `Env.cwd` while the +host namespace is stable. That claim is about traversal rather than about the +filesystem being stable — a directory that is real when it is read could be +replaced afterwards — and the transaction-bound provider that resolves the same +paths inside a run-owned Workspace, where a document path never becomes a host +path at all, is #227's second layer and is unbuilt. + +Scope cleanup releases live attachments; it does not delete run-owned state. +Every run status is retained until an explicit `xmd workflow delete`, which +reports what it removed and never claims to rewind a push, a pull request, or an +issue (#367). + +### 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. + +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. + +Cleanup releases live attachments; it does not delete the run's Workspace. Every +run status is retained by default, so a failed, cancelled, or interrupted run +keeps its checkouts and its journal for inspection and for an eligible history +fork. Deletion or explicit cleanup reports retained dirty, unpushed, or +conflicting work and never discards it implicitly (#293, #367). + +Durable published effects such as commits, issues, and pull requests remain +after temporary execution resources close, and no deletion claims to undo them. + +## 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. Required content is rendered directly into +later prompts from restored values, and generated artifacts do not appear in a +checkout unless the user explicitly exports them. + +Until `xmd workflow start` exists, the exercise runs in one document execution +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 +retained environment around it and the durable environmental effects inside it. + +The exercise succeeds when: + +1. Each manually invoked stage declares its props, publishes explicit results, + 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 composes a named Repository and Worktree from its pinned base before + discovery; the change reaches that worktree as admitted generated XMD rather + than as agent writes, and validation evidence is recorded. +8. ``, ``, and an explicit `` precede + ``, and none of them is implied by another. +9. The planner reviews the resulting pull request, and implementation and + review repeat when the verdict fails. +10. The user decides whether to accept the completed change. +11. The participants record every hidden-state dependency or optional file + export encountered during the exercise. +12. The run's filtered journal, Workspace roots, and effect results remain + readable through the retained run rather than through any repository ref. +13. 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 document execution + rather than binding malformed data; a failing region keeps only what it had + already rendered. +9. **How does a document read a prop?** Under the `props` namespace, in + expression props and in text alike: `agent={props.planner}` and + `{props.instructions}`. Declaring a prop creates no bare binding (#305). + Authored bindings — from `as`, ``, ``, ``, `` — + stay bare. +10. **Where may a component body project its caller's content?** Anywhere it + writes ``, including nested inside another invocation such as a + `` (#328). The stages here still take their material as declared + props, because a stage's inputs are part of its contract and a schema + validates them. +11. **Where does a stage's durable environment live?** In the workflow run's own + retained store, addressed by public run ID: filtered journal and effect + results, versioned Workspace roots and content, Repository and Worktree + metadata, Agent-session mappings (#291). One Workspace-local expansion + publishes its mutation, logical root, and journal result in a single + transaction (#365). +12. **Which construct owns a stage boundary?** None. The root document is the + workflow, and a durable run continues across document executions through + suspension and `resume` rather than through a `` construct (#298, + closed as superseded). + +### Open + +The exercise must resolve enough of these to implement one vertical slice: + +1. How does `` choose an idempotent identity, branch name, and cleanup + policy from the Repository base the run already pinned, and what does a + provider without native worktree semantics present instead (#293)? +2. What does a read-only Agent receive, and how is the ceiling proven for each + provider before a Prompt runs (#302)? +3. What is the public spelling and response schema of the constrained + generated-XMD evaluator, and which components does the first allowlist admit + (#369)? +4. What state makes external effects safe to repeat or resume after + interruption, and how does a revision iteration reach the *same* pull request + rather than a second one — expansion identity alone cannot answer it, so the + effect's natural key has to, and a head that deliberately advanced on the same + branch must be distinguishable from a conflicting one (#295, #297)? +5. Which forge read returns a pull request's existing reviews, comments, and + check results to a network-denied reviewer? ``'s creation result + is deliberately minimal, and no open issue owns that read. +6. Which containment does the transaction-bound Files provider give a document + path inside a run-owned Workspace, and what still needs the host provider's + weaker traversal claim (#227)? +7. How does a later invocation select and resume the same workflow run without + hidden transcript state, and which durable signals may schedule that + resumption (#366, #300)? +8. 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. +9. What records who answered an elicitation? The journal retains the validated + decision and its question fingerprint but no actor identity, so a decision is + attributable to a run and an expansion but not to a person. No issue owns it. + +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..74bf5bf4 100644 --- a/specs/markdown-agents-vision.md +++ b/specs/markdown-agents-vision.md @@ -178,6 +178,90 @@ 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. The command supplies the +rest: `xmd workflow start` creates a workflow run with one retained Workspace +(#366), and named `` and `` composition (#293), +deterministic local Git effects (#294), explicit `` (#370), +`` (#295), and `` (#296) describe the effects inside it. None +of them is built. Together they cover the work that should not depend on model +judgment: + +- retain each handoff, plan, review, and decision as a filtered journal event + bound to the Workspace root current when it was written; +- create or resolve a named repository, worktree, branch, file, or pull request + only when that environmental asset is needed; +- establish the working directory inherited by child operations; +- read and write exact content through the contextual filesystem boundary; +- return paths, commit identities, pull-request numbers, and URLs as workflow + data; +- reconcile existing external state when an execution resumes; and +- record the inputs, observed state, effects, and outputs of each operation. + +Agent calls analyze evidence and propose changes; they do not perform them. Under +a workflow run an Agent is read-only, and a proposal reaches the Workspace as +generated XMD that a constrained evaluator preflights and expands as ordinary +durable effects (#302, #369). 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. + +Live run state is scoped to the operation that owns it: created inside the +operation it describes, provided contextually, and torn down with it. Nothing +accumulates runs in a module-scoped registry, so concurrent runs cannot observe +each other. What outlives the process is retained deliberately, in the run's own +store, addressed by a public run ID. + +Resources clean up with their enclosing execution by default. Agent sessions, +processes, streams, and other ongoing effects always stop. Cleanup releases live +attachments without deleting run-owned state: every run status is retained until +an explicit deletion, so a failed or cancelled execution keeps its checkouts and +its journal, 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 +306,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..3d58cc36 --- /dev/null +++ b/workflows/adversarial-implementation/Discovery.md @@ -0,0 +1,76 @@ +--- +required: [instructions, planner, request, worktree] + +props: + instructions: { type: string } + planner: { type: string } + request: { type: string } + worktree: { 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: + + {props.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, and props are namespaced in expression props and text alike (#305). +Its caller supplies the request as the `request` prop 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. + +`` is what gives the planner read access to the checkout, and it +is the only thing that does. Lexical cwd — established here by the enclosing +`` — governs where XMD's own file operations resolve; it registers +nothing with an Agent. The two are separate operations +([#302](https://github.com/taras/executable.md/issues/302), unbuilt), and the +planner's access is read-only either way: the workflow host enforces that +ceiling and no prop in this document can raise it. Registration belongs to the +enclosing Agent session, which is why it sits inside ``; the exact +placement rule is #302's to settle. + +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. + +Everything here except `` runs today. diff --git a/workflows/adversarial-implementation/Implementation.md b/workflows/adversarial-implementation/Implementation.md new file mode 100644 index 00000000..9332d3c9 --- /dev/null +++ b/workflows/adversarial-implementation/Implementation.md @@ -0,0 +1,601 @@ +--- +required: [plan, authorization, instructions, planner, implementor, worktree] + +props: + plan: { 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 } + worktree: { type: string } + +returns: + report: { type: string } + verdictPassed: { type: boolean } + review: { type: string } + revisionPrompt: { 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 + +Implementation begins only after the user authorizes the converged plan. + +The implementor does not edit files. Under `xmd workflow` an Agent is read-only, +enforced by the host rather than by anything this document writes, so the +implementor inspects the checkout and returns an XMD fragment describing the +change it proposes. A constrained evaluator preflights that fragment and expands +the components it admits, and those expansions are what write files — each one an +ordinary durable effect with its own expansion identity, journal result, and +Workspace transaction. Staging, committing, pushing, and opening the pull request +are separate deterministic effects the document performs afterwards. + +That is the whole shape of the stage: **agents inspect; XMD mutates.** + +## Target shape + + +```json +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "changes": { "type": "string", "minLength": 1 }, + "title": { "type": "string", "minLength": 1 }, + "commitMessage": { "type": "string", "minLength": 1 }, + "report": { "type": "string" } + }, + "required": ["changes", "title", "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.assessment} + User response: {props.authorization.response} + Rationale: {props.authorization.rationale} + + Result contract: + + {proposalSchema} + + Implement the authorized plan by returning Executable.md source that + performs it. You have read-only access to the registered checkout and + cannot modify anything directly; the `changes` field is the only way + your work takes effect. + + `changes` may use only ``, ``, and ``. It may not + contain eval or exec blocks, imports, or any other component. Report + validation and newly discovered scope in `report`. Return only JSON + matching the supplied result contract. + + + + + {proposalCandidate} + + + + + + + Correct your previous response without changing its meaning. + Do not use tools or perform additional analysis. + + Previous response: + + {proposalCandidate} + + Validation errors: + + + - {error.instancePath}: {error.message} + + + Result contract: + + {proposalSchema} + + Return only corrected JSON. + + + + + + + {proposalCandidate} + + + + + + + + {proposal.report} + + + + + + + + Repository instructions: + + {props.instructions} + + Authorized plan: + + {props.plan} + + Authorization record: + + {props.authorization.assessment} + User response: {props.authorization.response} + Rationale: {props.authorization.rationale} + + Proposed change, as the source that performed it: + + {proposal.changes} + + Implementor report: + + {proposal.report} + + Pull request: + + #{pullRequest.number} ({pullRequest.state}) {pullRequest.url} + head {pullRequest.headSha} onto base {pullRequest.baseSha} + commit {commit} + + Result contract: + + {pullRequestVerdictSchema} + + Review the change at {pullRequest.headSha} against + {pullRequest.baseSha}, together with the authorized plan, the + instruction content above, and the registered checkout. You have no + network access: everything you may judge is either rendered here or + readable in the checkout. + + 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. + + + + + {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} + + ## Change performed + + Commit {commit} on the workflow branch, pushed before the pull request + was opened. + + {proposal.changes} + + ## Pull request + + #{pullRequest.number} ({pullRequest.state}) {pullRequest.url} + head {pullRequest.headSha} onto base {pullRequest.baseSha} + + ## Planner review + + Passed: {verdict.passed} + + {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} + + + + + + + + + + + + + + + Revise the implementation using this review: + + {verdict.review} + + Focused revision prompt: + + {verdict.revisionPrompt} + + User involvement record: + + {reviewCheckpoint.assessment} + User response: {reviewCheckpoint.response} + Rationale: {reviewCheckpoint.rationale} + + + + + + + + + + + + + +## Agents inspect; XMD mutates + +The implementor's proposal is XMD source, and `` is what runs it (#369; +the public component name is still open, and the workflow Workspace +specification uses this spelling as a placeholder). The evaluator parses the +complete fragment before its first effect, resolves the allowlist to pinned +component identities supplied by this document, refuses everything outside that +set — eval and exec blocks, imports, native execution, arbitrary JavaScript +expressions — and only then expands what it admitted. Rejected syntax produces +no partial effect. + +The allowlist is authority, not prompting guidance. It admits ``, ``, +and `` and nothing else, so a fragment cannot stage, commit, push, +open a pull request, or reach a secret by naming a component. `` in +particular stays out: what gets staged and committed is this document's decision, +not the proposal's. + +The exact filtered generated source is retained, so a replay expands the same +fragment without asking the implementor again — and so the source is available to +the reviewer and to the user as the literal description of what happened. + +Being lexically inside `` selects an agent for nested prompts and grants +that agent nothing. ``, ``, ``, ``, and +`` are XMD's own effects against the authoritative Workspace; the +agent process never sees them and could not perform them. + +`` registers the checkout with each session that has to read it. +The implementor's registration sits outside the loop so it happens once; +the planner's sits inside, so it repeats per iteration. Re-registering a path a +session already holds has to be idempotent for that to be safe — the ordered +directory set must stay the same set — which is a requirement on #302 rather +than something this document can guarantee. + +## Every mutation is one effect, and pushing is separate + +Each step is one expansion, one effect, and one transaction: + +```text + → each admitted publishes its mutation, the resulting + logical Workspace root, and its journal result together + → stages explicit paths; "." is written explicitly because + omission never means all paths + → commits only the staged index, fails when nothing is staged, + and journals reconciliation evidence: repository and worktree + identity, branch, commit and parent SHAs, tree SHA, staged + paths, message evidence — not Git object contents + → an external effect; the remote ref cannot join the local + transaction, so it observes remote state and adopts, performs, + or fails, and never force-pushes + → requires that pushed head; it never pushes on its own +``` + +Push is explicit precisely so that neither `` nor `` +hides remote mutation (#370). Replaying a completed commit creates no second +commit and replaying a completed push performs no remote mutation; resuming +after uncertain completion reconciles against retained state rather than +repeating the effect (#294, #297). + +**Open question for #295 and #297.** A revision iteration commits again, pushes +the same branch again, and reaches `` again under a new expansion +identity. The reconciliation that keeps that from opening a second pull request +is the effect-specific natural key — repository, head branch, base — rather than +expansion identity alone. A head that advanced on the same branch is the normal +shape of a revision, not a conflict, and #295's "conflicting head is diagnosed" +needs to distinguish the two. + +## The reviewer sees what it judges + +The planner has no network access, so whatever the prompt does not render is +invisible to the review. What the prompt renders is the proposal source that +performed the change, the implementor's report, and the pull request's identity — +number, URL, state, head SHA, base SHA — together with the checkout the planner +can read directly. + +``'s creation result is deliberately minimal (#295): stable provider +identity, number, URL, state, head SHA and base SHA. Reviews, comments and check +results are *not* fields on it, because a creation result that pretended to stay +fresh would be lying about a remote that keeps changing. They are separate reads. + +**Missing: that read.** No component or issue defines the forge observation that +returns existing reviews, comments and check results for a pull request. The +requirement is unchanged and worth stating exactly, because it is what makes the +review adversarial rather than uninformed: + +- the reviewer must receive every existing review with its body, every comment, + and every check result, iterated rather than stringified; and +- the user's checkpoint must carry the same consequential content, so a person + approving the change reads the original objections in their own words rather + than the planner's summary of them. + +Until that read exists, this stage reviews the change it just made against the +plan and the checkout, and an objection raised by anyone other than the planner +does not reach either surface. + +The verdict names one head. The prompt reviews the change at `headSha` against +`baseSha`, and a moved head requires a fresh review — the same rule #295 states +for a stored verdict. + +## 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. 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 stays internal. `start.md` gates on the verdict and the +decision rather than on forge state, and the filtered journal records the +external effect independently. A return field typed `string` would be worse than +useless — a conforming `` would perform its external effect 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. + +There is no `changedFiles` field in the proposal either, and for the same reason +the stage returns no `authorized` flag: the fragment already says what it writes, +and a second list is one no schema could hold in agreement with the first. What +was actually staged and committed is ``'s journaled evidence. + +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 +without revising the implementation and without reporting it as reviewed. The +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 + +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 forge 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. 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 **change itself**, as the source that performed it, rather than a + description of it; +- the **revision prompt** that goes to the implementor when the verdict has not + passed — the literal `verdict.revisionPrompt`; and +- each finding's **evidence**, because `` receives the complete finding + and a `defer` disposition turns it into a durable forge 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. + +The commit, push, and pull request are the one asymmetry, and it is deliberate: +they happen *before* the review checkpoint because the review is a review of a +pull request. What the user authorized earlier, at the authorization checkpoint, +was implementing the plan — which is what those effects carry out. The review +checkpoint then authorizes what comes after the review: the deferred issues, the +revision turn, or acceptance. + +## What does not exist yet + +| Written above | Supplied by | Status | +| --- | --- | --- | +| `` and the read-only ceiling | #302 | unbuilt | +| `` | #369 | unbuilt; public name open | +| ``, `` | #294 | unbuilt | +| `` | #370 | unbuilt | +| `` | #295 | unbuilt | +| `` | #296 | unbuilt | +| shared forge reconciliation behind push, pull request and issue | #297 | unbuilt | +| the forge read that returns reviews, comments and checks | — | unowned | + +The agent, parsing, capture, and control-flow syntax runs today; the loop body +above does not expand, because those names resolve to nothing. Until they land, +the effects they stand for remain explicit user-run steps between manual stages. + +Props are namespaced throughout (#305). `proposal`, `commit`, `pullRequest`, +`verdict`, `checkpointMaterial`, and `reviewCheckpoint` are authored bindings and +stay bare. + +## 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..7ce93d7c --- /dev/null +++ b/workflows/adversarial-implementation/InstructionFiles.md @@ -0,0 +1,33 @@ +--- +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. Props are +namespaced, so the `in` prop reads `props.paths` (#305). + +Under a workflow run the `` reads resolve inside the run-owned Workspace, +in whatever checkout the enclosing `` or `` established as +cwd. The component does not change to say so — that is the point of the +contextual `API.Files` boundary. + + + + +## `{instructionPath}` + + + + + diff --git a/workflows/adversarial-implementation/Planning.md b/workflows/adversarial-implementation/Planning.md new file mode 100644 index 00000000..c5bf393d --- /dev/null +++ b/workflows/adversarial-implementation/Planning.md @@ -0,0 +1,314 @@ +--- +required: [handoff, handoffCheckpoint, instructions, planner, implementor, worktree] + +props: + handoff: { 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 } + worktree: { type: string } + +returns: + plan: { type: string } + verdictPassed: { type: boolean } + review: { type: string } + revisionPrompt: { 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 + +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. + +Both agents are read-only here, and that is not a restriction this stage has to +impose: planning produces a plan, not a change. What the stage does have to do +is give each agent the checkout to reason about, which is ``. + +## 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.assessment} + User response: {props.handoffCheckpoint.response} + Rationale: {props.handoffCheckpoint.rationale} + + Investigate the registered checkout. Confirm, refute, or amend the + implementation theory with evidence. You cannot modify anything, and + this stage produces no change. 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.assessment} + User response: {props.handoffCheckpoint.response} + Rationale: {props.handoffCheckpoint.rationale} + + 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.assessment} + User response: {planCheckpoint.response} + Rationale: {planCheckpoint.rationale} + + + + + + + + + + + + + +## Two agents, two sessions, one checkout + +The implementor's `` and `` wrap the whole loop, so the plan +prompt and the revision prompt reach the same conversation without repeating +`agent` and `session` props on each one. The planner's `` nests inside +for the review and gives that session its own registration; leaving it restores +the implementor's for the revision turn. + +`` registers a read-only Workspace path with its enclosing Agent +session (#302). Registration is what grants an agent access — the lexical cwd the +enclosing `` established governs where XMD's own file operations +resolve and registers nothing. Both agents read the same checkout because both +are reasoning about the same revision, and neither can write to it under +`xmd workflow`. `` does not exist yet, and its exact placement +relative to `` is #302's to settle; everything else in this body runs +today. + +## 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, and the +complete plan-review `UserDecision`. + +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. + +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 +verdict or a declined checkpoint — `exhausted`, or `error`, and it refuses a +replay whose stored outcome or iteration count disagrees with what this +execution reached. `` opens no binding scope, so `plan`, `verdict`, and +`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 +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. + +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 +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 +acceptance pins the behavior. This synchronization slice does not choose it. + +Every `plan`, `verdict`, and `planCheckpoint` is already durable: each `` +is one durable operation, each `` answer is journaled against its +question fingerprint, and under `xmd workflow` those filtered events are retained +with the run and readable through `xmd workflow history` (#291 closed, #367 +open). There is no `cancelled` loop outcome — run-level cancellation and stop +reasons are retained run state rather than loop state. The component creates no +handoff files either way. + +Props are namespaced throughout: `agent={props.implementor}` and +`{props.instructions}` use one spelling in an expression prop and in text (#305). +`plan`, `verdict`, `verdictSchema`, `checkpointMaterial`, and `planCheckpoint` +are authored bindings and stay bare. diff --git a/workflows/adversarial-implementation/UserCheckpoint.md b/workflows/adversarial-implementation/UserCheckpoint.md new file mode 100644 index 00000000..d94bf453 --- /dev/null +++ b/workflows/adversarial-implementation/UserCheckpoint.md @@ -0,0 +1,234 @@ +--- +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 + +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 + + + +```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 +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "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: + + {props.material} + + 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} + + + + {"proceed": true, "response": "continue", "rationale": "The assessing agent found no material choice, so this transition needs no user decision."} + + + + + + +## Continuation is represented, never inferred + +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 passes the complete material to assess as the +`material` prop rather than asking the agent to locate or read it. + +This checkpoint registers no directory with its agent. It assesses supplied +material and needs no repository access, so no `` appears here — +a read-only agent with nothing registered is the narrowest thing this workflow +can ask for. + +`material` could equally be projected content: `` is substituted +wherever a body writes it, including nested inside a `` +([#328](https://github.com/taras/executable.md/issues/328), fixed). It stays a +declared prop because a stage's inputs are part of its contract and the schema +validates them, and because every caller here already holds the material as a +binding rather than writing it as prose. + +`` 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 — `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 execution 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. + +Under `xmd workflow` the same question becomes a durable wait. The elicitation +records its pending request and the Workspace frontier, the executor is +released, and `xmd workflow resume ` continues once the answer is +available — the process, the Workspace attachment, and the agent processes need +not stay alive in between. That lifecycle is #366 for start and resume and #367 +for suspension and ownership, and neither is built. Under `xmd run` the question +is answered inside one document execution or not at all. + +`` 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. + +Props are namespaced: `` and `{props.purpose}` use the +same spelling, in an expression prop and in text (#305). The bare bindings here — +`assessmentSchema`, `candidate`, `parsed`, `assessment`, `decision` — are +authored captures and parses, which stay bare. + +This component runs today. diff --git a/workflows/adversarial-implementation/artifacts.md b/workflows/adversarial-implementation/artifacts.md new file mode 100644 index 00000000..0cfb4d11 --- /dev/null +++ b/workflows/adversarial-implementation/artifacts.md @@ -0,0 +1,294 @@ +# Retained Run State 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. + +## What the run retains + +The retained store exists. A workflow run owns one SQLite database that is the +physical retention boundary for logically separate data +([#291](https://github.com/taras/executable.md/issues/291), closed): + +```text +WorkflowRun SQLite +├── filtered journal events and effect results +├── versioned Workspace roots and content +├── Repository and Worktree metadata +└── Agent-session mappings +``` + +Alongside the journal the run retains its immutable identity — run ID, workflow +definition, definition base, normalized props — one of six statuses, a nullable +stop reason, replaceable retrieval metadata, and one document-execution record +per start and per resume. A run is found by its public run ID alone: discovery +is arithmetic on that ID and no second registry can disagree with what is +stored. Damaged or incompatible storage is described and left exactly as found; +nothing migrates, truncates, or replaces it. + +There is no sidecar Git history. Earlier drafts of this document put artifact +versions in Git objects under `refs/xmd/runs`; run state lives in the run's own +database instead, which is why a run survives a repository the workflow never +pushed to. + +## One expansion, one effect, one transaction + +Every Workspace-local expansion publishes atomically +([#365](https://github.com/taras/executable.md/issues/365), closed): + +```text +BEGIN + apply the filesystem, Git or metadata mutation + publish the resulting logical Workspace root + append the filtered journal result +COMMIT +``` + +All three commit or none does. A host killed mid-effect publishes nothing: the +next connection recovers the last committed state, from which no recorded effect +is performed again. Nested effects finish their transactions before their +parent's effect begins. + +That boundary is what a stage's writes actually get. A `` the constrained +evaluator expands, a ``, and a `` are each one expansion, +one effect, and one transaction — never a bundle the enclosing commit rolls back +together. + +External effects cannot join it. ``, ``, ``, and +`` derive a stable identity from the workflow run and the expansion, ask +the provider to perform or reconcile that identity, and then append one local +result transaction. Replay adopts proven compatible completion, performs proven +absence, and refuses conflict or permanent ambiguity rather than duplicating an +effect whose completion is unknown (#297). + +## The journal is evidence, and the evidence is witnessed + +Journal events reach storage already filtered, through the pre-persistence +secret gate. Storage adds no second policy: a rejected gate leaves nothing +behind, and a record is retained exactly as the protocol produced it and parsed +back through the matching reader. Making that gate default-on for execution, with +its CLI opt-out and warning, is +[#199](https://github.com/taras/executable.md/issues/199). + +Filtering is also where a subtle authority question lives, and it is answered +explicitly. `JournalProvenance` is a non-operational, equality-only witness that +a live publication stream descends from the exact journal backend a provider +selected for one workflow run. It grants no append, read, execution, publication, +or reconciliation capability; it is meaningful only because the provider retains +the witness it established and later requires exact equality. The generic +pre-persistence guard preserves nothing, while the trusted secret-filter wrapping +site preserves provenance explicitly — so a filtered journal, including one +wrapped more than once, still carries the witness its source carried. An +in-memory stream, another run's journal, a copied property, an ordinary guard, or +a look-alike is refused before any mutation or publication. + +For this workflow that is the difference between "the history says the pull +request was created" and "this history is the one this run wrote." + +Every committed journal event also references the logical Workspace root current +when it was written. Only committed event boundaries are checkpoints, which is +what makes an event selectable for a history fork later (#368). + +## Reading the history + +`xmd workflow history ` exposes stable public event IDs with each +event's operation, source location, normalized evaluated arguments, result or +normalized error, Workspace version, and forkability reason: + +```text +EVENT OPERATION ARGUMENTS RESULT WORKSPACE FORKABLE +E14 File path="docs/notes.md" completed V7 yes +E15 Agent.Prompt prompt= V7 no: provider has no checkpoint +E16 Git.Add paths=["docs/notes.md"] completed V8 yes +E17 Git.Commit message="Document the workflow" 6f21a9… V9 yes +``` + +Values come from the retained journal after its security filter, so history +exposes nothing the policy has not already seen, and it never reconstructs a +filtered value from the Workspace or the provider. `status`, `list`, and +`history` are read-only: they attach no Workspace, Agent, or external provider, +and cannot advance a run. That surface is +[#367](https://github.com/taras/executable.md/issues/367) and is unbuilt. + +Part of what it will read is already journaled. `` records every iteration +it enters and one terminal `break`, `exhausted`, or `error` outcome, and refuses +a replay that disagrees with what this execution reached. Each `` is one +durable operation carrying its identity, input, agent and session, terminal +status, text, and structured failure. `` journals its validated answer +keyed by a fingerprint of the compiled schema and the rendered message, so a +resumed execution restores the answer instead of asking twice and refuses one +recorded against a different question. + +**Still missing: who answered.** The journal records the validated decision, the +question fingerprint, and the document execution it belongs to. It does not +record the actor identity behind an elicitation response, so a decision is +attributable to a run and an expansion but not to a person. No open issue owns +that yet. + +## Replay restores; it does not re-perform + +Replay rehydrates the Effection tree. A completed durable effect restores its +recorded result without executing again; ephemeral operations run again only to +rebuild live structure — attach the Workspace, enter lexical working-directory +scopes, attach providers, re-register Agent directories. + +The Workspace stores the current frontier; the journal stores the execution that +reached it. Replay never asks current state to prove a past effect: a file +written and later deleted is absent at the frontier while both completed effects +still restore in order. Nothing infers completion from a filesystem guard. + +A completed root result returns without expanding the document and without +attaching a Workspace, an Agent, or an external provider — which is why replaying +a finished run does not reclone, recommit, or repush. Missing or corrupt +authoritative Workspace state fails explicitly instead of being silently +recreated. + +Prompts therefore receive their content from restored values rather than from a +file an agent was asked to locate and read. Persisting a generated handoff file +does not reduce model tokens either: reading it adds its content to context and +normally adds tool-call overhead. Files remain useful as explicit exports or when +an external tool requires a path; they are derived views, not 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. `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 +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 text component's `` region runs under the `output` +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 document +execution 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 document +execution is still a complete record — replay restores its output and its failure +without re-executing anything. + +### Logical result contracts + +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. Text a stage produces and a later prompt +quotes, because nothing downstream reads an individual field. + +- `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` — 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. + +**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. +- `ImplementationProposal` — `changes`, `title`, `commitMessage`, `report` + ([`Implementation`](./Implementation.md)). `changes` is the XMD fragment the + read-only implementor returns for the constrained evaluator; `title` is the + pull request's; validation and newly discovered scope are reported inside + `report`. There is no separate + `changedFiles` list: the fragment already says what it writes, and a second + copy is one no schema could hold in agreement with the first. The authoritative + set of paths is what `` staged and `` journaled. +- `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)). + +**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. + +- `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 value carries both the gate and + the material a later prompt quotes. + +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, and the boundary is worth +stating exactly. `` (#295) resolves a minimal creation result — +stable provider identity, number, URL, state, head SHA, and base SHA. +`Implementation` consumes it internally, together with the separately observed +review state its prompt needs; `start.md` never receives it. What crosses the +stage boundary is the verdict and the decision it gates on, and the filtered +journal records the external effect independently. + +A return field typed `string` would be actively harmful: a conforming +`` would perform its external effect 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 +from reaching control flow or a durable effect. Prose capture remains +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. 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/primitives.md b/workflows/adversarial-implementation/primitives.md new file mode 100644 index 00000000..101f10a7 --- /dev/null +++ b/workflows/adversarial-implementation/primitives.md @@ -0,0 +1,311 @@ +# Primitive Inventory + +## XMD execution foundation + +XMD already supplies component expansion, root and component props, prompt +capture, agent selection, named sessions, collection iteration, scoped +permission 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 assigns deterministic +expansion identities, journals effects, and observes completion, failure, or +cancellation. Replay restores a recorded outcome without re-executing it, and a +document execution 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 continue. + +Under `xmd workflow` those records are also retained and addressable. That is +the difference between an execution journal and a run someone can come back to, +and it exists now (#291, #365). + +## 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 operation 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; execution continues | the root; `` | +| `output` | fails the document execution; `` can print instead | every `` region | +| `throw` | fails the document execution, 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 **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 +`` around a region that failed under `output` ends the document +execution 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 +these names and a repository file never stands in for it: + +1. `` with an optional nested ``, and `` with ``, provide + visible bounded control flow. `` selects its branch by JavaScript + truthiness. `` 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. Every + operation goes through the contextual `API.Files` provider, and there is no + host default: the four CLI entrypoints install the host provider, which + confines document paths to `Env.cwd` while the host namespace is stable + ([#227](https://github.com/taras/executable.md/issues/227)). The + transaction-bound provider that resolves the same paths inside a run-owned + Workspace is that issue's second layer and is unbuilt. +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 execution 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. It is the + shipped shape of a lexical working directory; under a workflow run that role + belongs to ``, ``, and ``. + +**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. `` is *not* among them; it belongs to the workflow + Agent boundary below. + +**Props are namespaced.** A declared prop is read as `props.name` in text, in +executable-block content, in eval blocks, and in expression props alike: +`agent={props.planner}` and `{props.instructions}` are both correct, and +declaring `planner` creates no bare `{planner}` binding +([#305](https://github.com/taras/executable.md/issues/305), shipped). Authored +bindings — from `as`, ``, ``, ``, `` — stay bare, +which is why `worktree={worktree}` passes a bound path down. + +**Content projects at any depth.** `` is substituted wherever a +component body writes it, including nested inside another invocation such as a +`` ([#328](https://github.com/taras/executable.md/issues/328), fixed). +The stages here still take their material as declared props rather than as +projected content, because a stage's inputs are part of its contract and a +schema validates them; projection is available where a caller genuinely writes +prose into a component. + +The document-level logic in `InstructionFiles`, `Discovery`, `Planning`, and +`UserCheckpoint` therefore uses shipped syntax throughout, except for the +`` registration `Discovery` and `Planning` need to give their +agents the checkout. `UserCheckpoint` registers nothing, because it assesses +supplied material rather than a repository. `Implementation` has no such +exemption: its loop body invokes ``, ``, ``, +``, ``, and ``, which resolve to nothing. + +## What the workflow still needs + +The missing capability is not one component. It is the retained-Workspace +implementation umbrella, [#218](https://github.com/taras/executable.md/issues/218), +whose dependency order this workflow consumes in the same sequence. + +**1. Retention and one filesystem vertical slice.** + +| Capability | Issue | Status | +| --- | --- | --- | +| open and look up one WorkflowRun database; append and replay the filtered journal | #291 | closed | +| commit a Workspace mutation, its logical root, and the journal result atomically | #365 | closed | +| foreground `xmd workflow start` / `resume` with an implicit Workspace and declarative `` effects | #366 | open — the next critical slice | +| `status`, `list`, `history`, suspension, cancellation, deletion, single-executor ownership | #367 | open | +| versioned history checkpoints and compatible forks | #368 | open | + +`xmd workflow start` is what makes this document a workflow rather than a script: +it creates the run, gives it one implicit root Workspace, streams rendered output +in the foreground, and returns when the execution completes, suspends, fails, is +cancelled, or is interrupted. `resume` selects only by run ID and reuses the +retained definition and props — a document path locates a definition and never +selects a previous run. + +**2. Repository and deterministic Git composition.** + +| Capability | Issue | Status | +| --- | --- | --- | +| `` and `` as named Workspace composition, with lexical cwd | #293 | open | +| ``, ``, staged-only `` | #294 | open | +| shared external forge-effect reconciliation | #297 | open | +| explicit `` | #370 | open | +| `` over an explicitly pushed head | #295 | open | +| provenance-linked deferred `` | #296 | open | + +Names are stable component identity, not magic configuration lookup: a +Repository's locator and base are ordinary root props or expressions. The first +local provider is expected to use native Git, because it gives the most faithful +checkout, worktree, and object-cache behavior; a browser provider may implement +the same contract differently, and may emulate named worktrees as separate +retained checkouts, without changing what a document writes (#293, #410). Initial +checkout speed is provider-owned acceleration — a host-local mirror or object +cache may change how fast a clone is, never what the run records or how it +replays. + +`` is a Workspace-local durable effect and joins #365's transaction +boundary. What it journals is reconciliation evidence — repository and worktree +identity, branch, commit SHA, parent SHAs, tree SHA, staged paths, message +evidence — not raw Git object contents. Replaying a completed commit creates no +second commit; resuming after uncertain completion reconciles against retained +Workspace Git state, accepts a matching commit, treats missing retained state as +corruption, and fails on incompatible state without an implicit reset, rebase, or +merge. + +`` stays separate from both `` and `` because +a remote ref cannot join the local transaction. It is an external effect that +observes remote state and adopts, performs, or fails — never force-pushes, never +resolves divergence implicitly. `` depends on that explicit pushed +head rather than performing a hidden push. + +**3. Agent authority and constrained execution.** + +| Capability | Issue | Status | +| --- | --- | --- | +| provider-correct filesystem containment | #227 | open — `API.Files` and the host provider are built | +| mandatory read-only workflow Agents and `` | #302 | open | +| preflight and expand constrained Agent-generated XMD | #369 | open; public component name undecided | +| transactional Worker Shell | #363 | open; containment and transaction POCs complete | + +The read-only ceiling is the host's, not the document's, which is why no +`` appears anywhere in this workflow. An implementor that cannot write +returns XMD instead, and a constrained evaluator parses the complete fragment, +resolves an allowlist to pinned component identities supplied by the trusted +parent definition, refuses everything outside that set — including eval and exec +blocks, imports, native execution, and arbitrary JavaScript expressions — and +only then expands what it admitted. The allowlist is authority, not prompting +guidance: generated source cannot grant itself push, pull-request, or secret +access by naming a component. The exact filtered generated source is retained, so +replay expands the same fragment without asking the Agent again. + +**4. Compose and certify.** + +| Capability | Issue | Status | +| --- | --- | --- | +| namespaced document props | #305 | closed | +| synchronize this living target with settled contracts | #292 | this change | +| prove the shipped planning-document logic | #290 | open | +| compose the supervised workflow | #301 | open | +| resume from explicit durable signals | #300 | open | +| default-on secret rejection before journal persistence | #199 | open — guard and scanner built | +| certify interruption, replay, authority, reconciliation, and cross-runtime behavior | #299 | open | + +``, ``, ``, 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. +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. + +Two authoring features sit deliberately outside this path. +[#412](https://github.com/taras/executable.md/issues/412) document targets and +[#416](https://github.com/taras/executable.md/issues/416) `` are useful, and +nothing here requires them; pulling either in is a separate decision. + +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..189a2257 --- /dev/null +++ b/workflows/adversarial-implementation/runtime.md @@ -0,0 +1,141 @@ +# Runtime and Isolation + +The command selects the environment; the document describes the procedure. +`xmd run` uses the caller's current environment and promises no restoration. +`xmd workflow start` creates a workflow run with one retained root Workspace, +and that Workspace supplies the filesystem, repository, process, and +working-directory capabilities every stage uses (#366, unbuilt). + +The same declarative components work in both. Durability comes from the host, +not from a second spelling of `` or ``. + +## Target shape + +```sh +xmd workflow start \ + --props-request="…" \ + --props-repository=https://github.com/acme/project.git \ + --props-base=main \ + workflows/adversarial-implementation/start.md +``` + + + + + + + +Root props supply the locator and the base. A repository name is stable +component identity inside the Workspace, never a key into hidden configuration, +and `xmd workflow start` uses the same generated `--props-*` arguments as +`xmd run`. + +## What the run owns + +The workflow run owns its Workspace, its repositories and worktrees, its Agent +sessions, and its filtered journal. Nothing required lives only in an Agent +transcript, a host path, a provider handle, or a branch name. + +- `` authorizes the locator, resolves the base once, pins that + commit, and creates the named primary checkout. Resolution happens once; + replay does not re-query a moving branch (#293). +- `` adds a named linked checkout on its own branch inside that + Repository. Its identity is Repository identity plus name, independent of any + attachment-specific absolute path. +- Both install contextual cwd while rendering their children and bind their + Workspace-relative path through `as`. +- `` changes cwd and nothing else. + +A worktree isolates Git state from the user's checkout. It is composition and +isolation, not a security boundary. + +## The Agent ceiling is the host's, not the document's + +Workflow Agents are mandatorily read-only, enforced in three places: the +provider permission bridge allows only read and search operations, the provider +runs in its native read-only sandbox, and registered Workspace paths are +presented as read-only filesystem views. ``, repository `.codex` or +`.claude` configuration, and prompt content cannot raise that ceiling, and a +provider that cannot enforce it fails before Prompt execution (#302). + +There is therefore no `` component in this workflow, and no document +prop that grants an Agent write access. Earlier drafts of this document declared +readable roots, writable roots, environment, process, and network policy as +markup; that authority moved to the host, where a document cannot widen it. + +Directory access is explicit: + + + + Review the change. + + +Registrations are ordered, may occur between prompts, and have no special first +directory. Lexical `` does not register anything with an Agent. + +Native Agent processes inspect disposable read-only materializations of the +current logical Workspace root. Those views have no write-back path and may be +discarded and recreated. An Agent proposes changes by returning XMD, which a +constrained evaluator preflights and expands as ordinary durable effects +against the authoritative Workspace (#369). + +Agent network access is denied for this workflow, which is why a review prompt +must render everything the reviewer has to judge rather than pointing at it. + +## Interruption, suspension, and cancellation + +Three outcomes are distinct, and none of them is a failure of the document: + +- **Suspension.** A checkpoint that reaches a person records its pending + request and the Workspace frontier, releases the executor, and returns with a + run ID and stop reason on standard error. `resume` continues when the answer + is available. +- **Interruption.** Losing the executor outside an authored wait, including + Ctrl-C, leaves the run `interrupted` and resumable at the journal frontier. +- **Cancellation.** `xmd workflow cancel ` asks an active executor to + stop and makes the run terminal. It retains the journal and Workspace for + inspection and does not undo completed local or external effects. + +An uncaught failure escaping the root is terminal too. Reusing that run's ID +replays the retained failure rather than silently retrying it; a corrected +document starts a new run. + +All of that is #366 for start/resume and #367 for status, list, history, +cancellation, and deletion. Neither is built. + +## Cleanup follows the invocation + +Resources clean up with their execution. Agent sessions, processes, and streams +stop before their enclosing scope closes, and content a caller projects keeps +the caller's bindings while its live effects belong to the invocation that +projected it and stop before that invocation cleans up its own (#203, shipped). +That ordering is what makes a workspace safe to tear down: a process a stage +started stops before the directory it ran in goes away. + +Scope cleanup releases live attachments. It does not delete run-owned state: +every run status is retained until an explicit `xmd workflow delete`, and +deletion never claims to undo a push, a pull request, or an issue (#367). + +## Loop interruption + +An automated iteration stops on the first applicable signal: + +1. The iteration reaches its defined completion. +2. A durable signal the run is waiting on arrives. +3. The user provides runtime input. + +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. Under `xmd workflow` the same question becomes a durable +suspension. Arbitration between signals is not implemented +([#300](https://github.com/taras/executable.md/issues/300)), and premature +watcher semantics are deliberately excluded from it. + +`` already records part of this: 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 execution reached. There is no `cancelled` loop outcome — run-level +cancellation and stop reasons are retained run state, not loop state. + +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..bfc41c53 --- /dev/null +++ b/workflows/adversarial-implementation/start.md @@ -0,0 +1,425 @@ +--- +required: [request, repository] + +props: + request: { type: string } + repository: { type: string } + base: { type: string, default: main } + branch: { type: string, default: agent/adversarial-implementation } + planner: { type: string, default: codex } + implementor: { type: string, default: claude } +--- + +# Adversarial Implementation Workflow + +- **Status:** Living end-goal target +- **Command:** `xmd workflow start` (#366), unbuilt + +This entry document is the complete workflow map. The linked files define the +prompts, effects, and authority boundaries used by each stage. Its component +markup is the intended executable form. + +The root document *is* the workflow. There is no `` wrapper and no +`` construct: `xmd workflow start` selects the environment, and a durable +workflow continues across several document executions without subdividing the +document ([#298](https://github.com/taras/executable.md/issues/298), closed as +superseded). What the command supplies — one retained Workspace per workflow +run, restored durable effects, and a read-only Agent ceiling — is described by +the [workflow Workspace specification](../../specs/workflow-workspace-spec.md). +"What runs today" below says exactly which parts exist. + +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. + +## Complete flow + + + + + + + + + + + + ## Implementation plan + + {planning.plan} + + ## Planner review + + {planning.review} + + + + + + + + + + + + + + + + + # Accepted + + {acceptance.rationale} + + {implementation.report} + + # Rejected at acceptance + + The change was completed and reviewed, but the user did not + accept it. + + {acceptance.rationale} + + {implementation.report} + + + + + # Stopped: the pull-request review never passed + + The user kept approving and the verdict never passed, so the + change was never offered for acceptance. + + {implementation.review} + + {implementation.report} + + # Stopped: the pull-request review was declined + + {implementation.decision.rationale} + + {implementation.report} + + + + + + # Stopped: implementation was not authorized + + {authorization.rationale} + + {planning.plan} + + + + + # Stopped: the plan review never passed + + The user kept approving and the verdict never passed, so + authorization was never requested. + + {planning.review} + + {planning.plan} + + # Stopped: the plan review was declined + + {planning.decision.rationale} + + {planning.plan} + + + + + + # Stopped: the handoff was not validated + + {handoffCheckpoint.rationale} + + {handoff} + + + + + + +## The Workspace is composed, not implied + +`xmd workflow start` gives the workflow run one retained root Workspace. That +Workspace is neither a repository nor a checkout: what it holds is named +composition this document writes (#293). + +`` authorizes `props.repository`, resolves +`props.base` once, pins the resulting commit, and creates the named primary +checkout. The name is stable component identity inside the Workspace, not a +lookup key into hidden configuration — the locator and the base are ordinary +validated root props, and a value like `"project"` resolves through no alias +registry. `` adds a linked checkout on its own +branch, so discovery, planning, implementation, and review share one filesystem +without disturbing the primary checkout. Both install contextual cwd while they +render their children, and `as` binds the Workspace-relative path. + +Nothing here is implicit. A second repository is a second `` with +its own name, locator, and base, and no transaction spans the two (#293, and +§7.6 of the workflow Workspace specification). The pinned commit is what keeps +every stage on one source revision even if the base branch moves. + +`` is lexical cwd and nothing else. Making a directory readable by an Agent +is ``, written inside the `` that reads it — which is why +each stage that runs an Agent takes the `worktree` binding as a prop. The two +are separate operations on purpose (#302). + +## Agents inspect; XMD mutates + +Under `xmd workflow` an Agent is read-only, and the host enforces that ceiling +in its permission bridge, its provider-native sandbox, and the filesystem view +it presents. A document cannot raise it, so there is no `` component to +write: the command selects the environment and the ceiling comes with it (#302). + +An implementor that cannot write files proposes changes instead. It returns an +XMD fragment, and a constrained evaluator preflights the whole fragment and +expands only pinned, explicitly allowed component identities (#369). Every file +the workflow writes is therefore an ordinary durable XMD effect with its own +expansion identity, journal result, and Workspace transaction — not a side +effect of an agent process. `Implementation` is where that happens. + +## User authority is a gate, not a report + +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, 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 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. + +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, 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 document execution stopped +earlier renders the artifact it stopped on rather than a value it never +produced. + +## Waiting for the user is a suspension, not a stop + +A checkpoint that reaches a person is a durable wait, and a durable wait is not +a failure. Under `xmd workflow` the elicitation records its pending request and +the Workspace frontier, releases the executor, and returns control with a run ID +and a stop reason on standard error. The process, the Workspace attachment, and +the Agent processes need not stay alive. `xmd workflow resume ` +continues the same workflow run once the answer is available: completed durable +effects restore from the journal, ephemeral attachments rebuild, and partial +replay continues at the retained frontier. + +That is what the earlier drafts of this document were reaching for with a +`` boundary. The construct was rejected — the root document is the +workflow — and the requirement it stood for now belongs to the retained +lifecycle: durable suspension and foreground `start`/`resume` are #366, and +status, history, cancellation, and deletion are #367. Neither is built. Until +they are, gating is expressed by nesting, which prevents the remaining stages +from running but does not stop the document execution: it still expands to +`` and completes. + +## How props are read + +Root and component props are namespaced under `props`, in expression props and +in text alike: `planner={props.planner}` selects the agent, and +`{props.instructions}` interpolates it inside a prompt body. Declaring a prop +creates no bare binding, so `{planner}` stays verbatim +([#305](https://github.com/taras/executable.md/issues/305), shipped). + +Authored bindings are the other half and stay bare. `as="worktree"` on +`` creates the binding `worktree`, and `worktree={worktree}` passes it +down. Bare `{name}` resolves an authored eval, capture, loop, or return binding; +dotted `{props.name}` traverses the validated props namespace. + +## Error modes in a stage + +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 document execution, + and no `` region can print it instead. +- The **`` region** runs under the `output` error mode: an *undecided* + error there fails the document execution 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. Execution 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. + +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. + +## What runs today + +**Expressible now.** The document logic in +[`InstructionFiles`](./InstructionFiles.md), [`Discovery`](./Discovery.md), +[`UserCheckpoint`](./UserCheckpoint.md), and [`Planning`](./Planning.md) is +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. +`InstructionFiles` and `UserCheckpoint` run as written; `Discovery` and +`Planning` each name ``, which does not exist, so their bodies run +only once that registration is supplied or removed. + +**Not expressible.** Everything that composes the Workspace or performs a +durable environmental effect: + +| Written above | Supplied by | Status | +| --- | --- | --- | +| `xmd workflow start` / `resume` | #366 | unbuilt | +| `` / `` | #293 | unbuilt | +| `` and the read-only Agent ceiling | #302 | unbuilt | +| `` for Agent-generated XMD | #369 | unbuilt; public name open | +| `` / `` | #294 | unbuilt | +| `` | #370 | unbuilt | +| `` | #295 | unbuilt | +| `` | #296 | unbuilt | + +So the complete flow is non-executable at two levels: the workflow spine that +selects the retained environment and composes named checkouts, and the +implementation stage's durable Git and forge effects. What can be exercised +today is discovery through plan convergence and the user gates around them, +running in one document execution and one existing working directory, with +commits, pushes, pull requests, and issues performed as explicit user-run steps +between manual stages. Proving that shipped subset is #290. + +The foundation underneath is built. Retained WorkflowRuns and filtered journals +are stored and looked up by public run ID +([#291](https://github.com/taras/executable.md/issues/291), closed), and one +Workspace mutation, its logical root, and its journal result publish in a single +transaction ([#365](https://github.com/taras/executable.md/issues/365), closed). +What is missing above them is public reachability, not durability. + +## Rendered data flow + +| Captured value | Produced by | Consumed by | +| ---------------------- | -------------------------- | --------------------------------------------------- | +| `worktree` | `Worktree` (Workspace-relative path) | every stage that registers a directory with an Agent | +| `instructionPaths` | `Glob` (`string[]`) | `InstructionFiles` | +| `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 (`.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 (`.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: 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. `Implementation` creates the pull +request, reviews it, and keeps the handle internal, because `start.md` gates on +the verdict and the decision rather than on forge state, and the filtered +journal records the effect independently (#291). A return field typed `string` +would let a conforming `` perform its external effect and only then +fail the stage's return validation. + +## Details + +- [Runtime and isolation](./runtime.md) +- [Instruction materialization](./InstructionFiles.md) +- [Discovery](./Discovery.md) +- [User checkpoint](./UserCheckpoint.md) +- [Planning](./Planning.md) +- [Implementation](./Implementation.md) +- [Retained run state](./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).