diff --git a/AGENTS.md b/AGENTS.md index 1be3b2f02..c900139bd 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -405,7 +405,9 @@ the corpus or enabling in-shard concurrency is not an answer to a miss. ## MUST READ - https://github.com/thefrontside/effection/blob/v4/AGENTS.md -- `architecture.md` — read fully +- [`architecture.md`](architecture.md) — read fully. Its [reading map](architecture.md#reading-this-architecture) + locates current invariants and their feature-interface owners; it does not + replace the full reading. - `specs/executable-mdx-spec.md` — read the sections your change touches ## Code Rules diff --git a/README.md b/README.md index 086c29400..b32222bf5 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,21 @@ Three documents own the rest: - [`specs/executable-mdx-spec.md`](specs/executable-mdx-spec.md) — what the product does, with the conformance matrix that pins it. +Follow a question into the [architecture reading map](architecture.md#reading-this-architecture): + +- [Execution and diagnostic journals](architecture.md#a-document-running): what + runs, what a Plan produces, and which journal can continue a workflow. +- [Partial and completed replay](architecture.md#replay-and-effect-transactions): + why a historical file read survives a later deletion. +- [Native Agent ownership](architecture.md#native-agent-sessions): handoff, + release and executable compatibility. +- [Installed structural regions](architecture.md#the-installed-structural-syntax-boundary): + demand, output and cancellation. +- [Plan recovery](architecture.md#typed-candidate-recovery-around-evaluate): why + cleanup failure stops a candidate retry. +- [Current rules and historical decisions](specs/decisions.md#finding-the-current-rule): + where the architecture, feature contracts and vision differ in authority. + Locate the package that owns the behavior you are about to change: - `packages/core/` — the engine: the boundary scanner, component expansion, @@ -33,7 +48,7 @@ Locate the package that owns the behavior you are about to change: - `packages/durable-streams/` — the journal protocol, replay and divergence. - `packages/workflow/` — workflow runs, run storage, and the Workspace. - `packages/git/` — repository collaboration: ``, ``, - ``, the `Git.*` operations, pull requests and issues, and the GitHub + the `Git.*` operations, pull requests and issues, and the GitHub adapter behind them. It is the one Plugin `xmd` bundles, active by default for the run-profile commands — `run`, `plan`, `syntax`, and `workflow start`, `resume` and `fork`. The `xmd test` root is deliberately not one of them, so a diff --git a/architecture.md b/architecture.md index 700d0b27c..a347fd4e6 100644 --- a/architecture.md +++ b/architecture.md @@ -1,151 +1,63 @@ # Architecture guidelines -Rules the engine's core mechanics must satisfy. The spec says what the -language does; this file binds what any implementation may do. Rules are -invariants; the inventory at the end says what is built. +## Reading this architecture -## Terminology +Read this document to understand the rules every engine implementation preserves. +Start with a document running, then follow its execution, failures and retained +state. The detailed boundaries explain what an implementation may do; the +terminology and construct inventory are references after those ideas are clear. -Terms are locked here. Two rules: +The documents have different jobs: -1. Only use concepts and words described here or already defined in the spec - and code. -2. If a concept or term is missing, ask for permission before using it; using - it requires adding it here first. +- This architecture owns implementation invariants and resource lifetimes. +- [The language specification](specs/executable-mdx-spec.md) owns authored syntax, + observable behavior and conformance. Feature specifications refine their own + interfaces; the route table below points to them. +- [The vision](specs/markdown-agents-vision.md) explains product direction and is + non-normative. [Decisions](specs/decisions.md) preserve history and consequential + rationale, including changes to earlier decisions. +- [The contribution guide](AGENTS.md) owns preparation, verification and review. -Existing documents and code get aligned to this section retroactively. +| Question | Start here | Interface and evidence | +| --- | --- | --- | +| What runs, and can a diagnostic journal continue it? | [A document running](#a-document-running) | [Plan diagnostic journal](specs/plan-command-spec.md#the---journal-file) and [workflow continuation](specs/workflow-workspace-spec.md#9-replay-and-continuation) | +| What does partial or completed replay restore after a write, read and delete? | [Replay and effect transactions](#replay-and-effect-transactions) | [Workflow replay](specs/workflow-workspace-spec.md#9-replay-and-continuation) | +| Who owns a native Agent handoff, and what if its executable changes? | [Native Agent sessions](#native-agent-sessions) | [Native ownership](specs/native-agent-session-launch-spec.md#ownership-and-concurrency) and [build binding](specs/native-agent-session-launch-spec.md#executable-build-binding) | +| When does an installed structural region run, and who cancels it? | [Installed structural syntax](#the-installed-structural-syntax-boundary) | [Structural interfaces](specs/executable-mdx-spec.md#declared-structural-syntax) and [SR conformance](specs/executable-mdx-spec.md#tier-sr--expanding-installed-structural-syntax-53) | +| Why can Plan retry a refused candidate but stop on cleanup failure? | [Typed candidate recovery](#typed-candidate-recovery-around-evaluate) | [Plan failures](specs/plan-command-spec.md#failures) | +| Which rules are current, and which explain earlier choices? | [Changing these rules](#changing-these-rules) | [Decisions](specs/decisions.md) and [vision](specs/markdown-agents-vision.md) | -"Authority" is not one of these terms. Each boundary it once covered is named -for what it actually does: canonical resolution, admission, configured -permission, ownership, coordination, publication, execution, or definitive -settlement. Two serialized spellings keep the older word because their bytes are -a compatibility contract, not a description: the durable `LaunchFailureClass` -value `"directory-authority"`, which a retained launch record is parsed against -by exact match, and the Files invariant category `"authority"`, which crosses -loaded copies and is matched the same way. Renaming either would break replay -and categorization rather than clarify them, so both stay exactly as written. +## A document running -| Term | Meaning | -| --- | --- | -| behavioral predictability | a plan as a program: ~80% deterministic, ~20% probabilistic | -| replay determinism | the journal doesn't lose the execution chain: replaying arrives at the same state, where execution can continue | -| replay | using the durable journal to restore the recorded portion of a document execution without repeating durable effects; after the recorded entries are consumed, execution may continue live | -| divergence | reserved for replay: a replay that departs from the recorded journal | -| REPL history | the immutable ordered execution records the REPL reads from the durable journal and projects into `ReplModel`; the History surface presents this read model and is not itself a journal | -| middleware | applied by the lexical structure, used by runtime execution | -| workflow run | a workflow being carried out with its progress and outcome recorded durably; document executions perform its work, while ongoing effects remain scoped to the document execution in which they run | -| document execution | one evaluation of a root document initiated through `execute()`, producing one output stream and one completion result while reading and appending a durable journal; its ongoing effects belong to the Effection scope in which the evaluation runs | -| workflow definition | what a workflow run is a run of: a closed versioned descriptor, in one of two versions. Version 1 names an immutable Git object — its format and object ID — together with the repository-relative path of the root document inside it. Version 2 is a **source bundle**: the exact bytes themselves, addressed by logical paths and retained with the run. Both optionally carry the exact canonical document target and the component bundle the root declares. A repository locator is not part of either, and neither is distinct from every Repository created inside the run's Workspace | -| source bundle | the workflow definition: an entrypoint, a canonically ordered manifest of logical paths with each source's SHA-256 and byte length, and a bundle hash over that manifest. The bytes behind it are retained with the run, so a file outside a repository, an untracked file and a file edited since its last commit are all ordinary starts, and editing, moving or deleting the original afterwards changes nothing about the run | -| logical path | a portable identity inside a source bundle, never a host filesystem path: NFC Unicode scalar values, `/`-separated, without leading or trailing separator, NUL, C0 control, DEL, backslash or `#`, and without an empty, `.` or `..` segment, compared case-sensitively by its UTF-8 bytes. The containing directory, the absolute path and the invocation working directory are retrieval facts and never identity | -| workflow component bundle | the closed set of authored Markdown components a workflow root declares. A bundle maps each name onto a logical path the same source bundle retains. It is both definition identity and canonical resolution: the names it declares resolve to its exact retained sources and to nothing else | -| declared Markdown component | exact first-party Markdown a trusted host declares to one document execution, read once and held by the invocation — schemas copied, not referenced — before any installation runs, as immutable data on an `ExecutionInstallation`: the public name, the reported origin, the bytes, the SHA-256 of those bytes, the accepted forms, the contract parsed from them, and any private component closure only those bytes may write. The host reserves the name rather than offering a default for it, so it resolves ahead of a repository file, a workflow component bundle and every registration; a second reservation on one name is a configuration failure. It is a composition mechanism and not a policy loader: no caller-facing option selects one, adds one, or names its source | -| private component closure | the components one declared Markdown component carries that only its own bytes may write. Each is minted like any other invocation-identity component and registered nowhere, so it resolves while canonical core is expanding that declaration's body and nowhere else — not from the caller's root, the content the caller projected through it, a sibling declaration, an imported component, middleware, or an implementation kept past teardown. Eligibility is the authored occurrence rather than the name: what an import may invoke is what canonical core produced inside that exact ask; an implementation the closure built is refused for every other name, copy, later site and later execution, because a run that has ended admits nothing; and a private name written anywhere else resolves to nothing before any other tier can answer. It is lexical availability, not permission: every private component still takes its operation from the invocation that carries it | -| retrieval metadata | replaceable, credential-free information about where a workflow definition can be fetched from now; it takes no part in run identity and is reauthorized by the host before use | -| stop reason | why a workflow run or a document execution stopped: a categorical host code, or a reference to an already-filtered journal event | -| run ID | an opaque stable public identifier generated by the host or selected by an authorized caller; it associates the run's durable records and effects, remains unchanged for the life of the run, and has no semantics beyond equality and lifecycle addressing | -| Repository base | the optional Git revision from which one named Repository initializes its primary checkout | -| Repository selection | plain structural composition data naming the repository one component invocation acts on: an opaque provider-minted selection identifier, the display name, the credential-free repository identity, and the selected checkout path. It carries no credential, provider handle, lock, database, run ID or permission — the installed provider authenticates every selection against private state before it touches Git or a service, so a copied, replaced or rebuilt one can misname a target and be refused but can never reach one | -| ambient Repository | the repository an ordinary `xmd run` was started inside, discovered when the first element asks for one — never at installation — from the invocation's starting directory, and remembered for the rest of the execution including when there is none. Its identity is the canonical common Git directory and its selected checkout is the canonical checkout root, so starting in a linked worktree names the same repository as starting in the primary checkout while Git operations still act on the worktree. A workflow run has none | -| managed checkout | a Repository or Worktree an ordinary `xmd run` created under the host root `~/.xmd/repositories`, addressed by a digest of its whole identity, described by a closed version 1 sidecar written beside it, and held for one document execution by an exclusive non-blocking advisory lock. It survives every execution: nothing deletes, resets, cleans, fetches or repairs one | -| ordinary Git identity | the invoking user's effective Git author and committer name and email, read from the trusted host's own environment and configuration by the first `` that needs one — never at installation — and kept in the provider's closure for the rest of the execution, including when the host can name none. A run that commits nothing never asks. It is used for an ordinary `` and nothing else, because that commit lands in the caller's own checkout; a workflow run keeps its one fixed identity, whose whole purpose is that retained Git state does not depend on whose machine made it. It is not a prop, a Context value, a component result or a middleware answer, and nothing else about the environment is borrowed with it — hooks, file-system monitors, signing programs and repository-supplied credential helpers stay disabled by the same fixed command-line configuration. A host where Git can name no identity refuses `` with an actionable sentence rather than substituting the workflow one; every other component is unaffected | -| ordinary invocation identity | a fresh opaque random value an ordinary document execution's repository provider mints for itself and keeps in its own closure. It is not a prop, a Context value, a component result, a middleware answer, a lifecycle ID or a retained record, and it is neither addressable nor reusable; live Issue and pull-request idempotency and reconciliation keys are derived from it together with the engine's own expansion identity | -| pinned commit | the commit obtained by resolving a base once; it remains the workflow run's starting repository state even as the run creates descendant commits | -| document target | an addressable static heading in a root document's own Markdown flow, named by the canonical path of heading labels that reaches it; selecting one executes the preamble, each ancestor's own content, and that heading's complete subtree | -| Prompt | a person's original request, in ordinary natural language. `xmd plan` takes exactly one | -| Plan | the executable program produced from a Prompt: an Executable Markdown document combining readable prose that expresses the Prompt's intent with the components that carry it out, each placed beside the prose describing what it does. It begins with one descriptive level-one heading. A Plan is what `xmd plan` approves and then delivers as source: printed to stdout by default, or written to an `--output` file. Plan produces a program; Run executes a program from the host or CLI; composition decides whether and when a planned program runs. It is not a synonym for a workflow, a policy document or any executable Markdown file | -| plan command document | the one exact checked-in first-party Markdown value root `xmd plan` executes. It is the command's adapter and nothing else: it projects the request into ``, supplies the session, and returns the approved source. Its body is those two elements and no prose, because its rendered transcript is the progress the command writes to stderr. It is not itself a Plan. Internal: no command-line option selects another one, and no repository component search can answer for it | -| packaged `` Component | the one exact checked-in first-party Markdown text component that converts a prompt into a Plan, `packages/cli/src/documents/Plan.md`, declared to every ordinary run as the public ``. It owns and implements the Plan authorship workflow — the Prompt wording, the draft and repair loops, the `` branches, human review, revision, approval, stopping, exhaustion and the automatic final explanation turn — and produces the exact approved Plan source as what it renders: written bare it emits those bytes, and `as` is ordinary text capture that binds them and emits nothing. Neither form evaluates the source. Every Plan-producing turn in it states the complete Plan requirements for itself, so a replacement may add or correct a title rather than only carry one forward. Both surfaces expand these exact bytes under one origin and one digest; there is no generated TypeScript copy and no second Markdown implementation. Its four phase components are private to it, and it is not itself a Plan | -| Plan writer profile | the trusted-host assembly the packaged `` Component runs its authored turns under, installed by its own `` inside the invocation that owns it rather than around an execution — which is what makes it the same frame whether `xmd plan`, an ordinary document, or a configured `` run child asked. Which Agent context goes under it is a trusted-host capability the declaration carries — the production ACPX one built from the run's Agent stack, or the deterministic one a canonical `` child declaration produced — and a host that supplies none states the sentence a `` written there is refused with. The fixed policy is installed in one place for both, so a second provider cannot bring a weaker one: its fixed inputs, a constrained Agent provider, Elicitation, the fixed first-party components and the host-declared ``. It uses no repository component search and exposes no custom root, and the policy it installs is not readable from the command line. Its working directory is one host-owned directory dedicated to the logical session, keyed by the digest of that name, created empty and required to be empty on the way in. An explicitly named session's directory is durable, because continuation derives the same session identity from it; an invocation-unique default session's is scope-owned, registered for cleanup before it is created, and exactly one cleanup is attempted after profile teardown and before admission on every ending — the leaf removed non-recursively when it is still the empty directory that was handed over, and left as found with the command failing terminally when it has gained content or vanished. Where those directories live is a host dependency no caller or document selects | -| upgrade command document | the one exact checked-in first-party Markdown streaming text root `xmd upgrade` executes to select and install a published release. It owns the exact-tag grammar, release selection, semantic-version comparison, consent, the status, already-current and installation branches, and the wording of every refusal and report; its rendered body is the command's output rather than a value it returns. Internal: no command-line option selects another one, and no repository component search can answer for it | -| upgrade assembly | what one runtime-named entrypoint states about the `xmd` that is running: its provenance, reported version, invoked executable path, platform, architecture, release target when the release publishes one, and — for an eligible compiled macOS or Linux host alone — the factory for the four phases an installation needs. It describes how this `xmd` is running, never how its files arrived | -| release identity | the invocation-local opaque identifier `` mints for each release it admits. Holding one is what authorizes downloading that release, and nothing outside that one invocation's private admission map can read, extend or forge it. A download mints an upgrade candidate identity in the same way, and that candidate advances `downloaded → verified → committed` exactly once | -| expansion | one logical evaluation of an authored executable element within a document execution | -| expansion ID | a deterministic identifier for one logical expansion; restoring or retrying that expansion preserves the ID, while a distinct evaluation requested by the document receives another | -| Git capability | the contextual interface through which `@executablemd/git` queries the Git repository associated with the current working directory | -| Git host | an external service that owns remote Git repositories and associated collaboration objects such as branches and pull requests. GitHub is one Git-host adapter. A Git host is distinct from the local Git capability and the trusted workflow host, and from an Issue provider | -| Issue provider | an external service that owns a collection of issues. GitHub and Atlassian Cloud are Issue-provider adapters. It is a separate boundary from a Git host because an Issue provider need not own a Git repository, and one that does not cannot truthfully persist as a Git-host effect | -| Issue tracker | what a lexical `` names: a credential-free URL for the container new issues are created in, together with an optional explicit provider discriminator. An upsert requires one and takes its discriminator only from it; a read needs none, because the URL it was given is the identity. It is replaceable composition data; it requests a destination and grants nothing | -| canonical target URL | the normalized form of an Issue tracker's URL. One destination has one spelling, which is what durable identity, the natural key and every provider comparison use | -| provider discriminator | the stable lower-case name of an Issue provider. Stated on a tracker, it names the only provider allowed to handle the request; omitted, each installed provider matches the URLs it recognizes | -| printed error | an error printed into the document (`ErrorSegment`); data, not control flow | -| failure | an error that propagates (`ComponentFailure`, `DocumentationError`) | -| error mode | what an undecided error in a region becomes: `print` (printed into the document), `output` (fails the document execution; `` can print instead), `throw` (fails the document execution) | -| attempt | one execution of a retried operation or region | -| raise | where an error no middleware converted is decided: printed or failed, per the error mode; observed exactly once, where raised | -| blocker | execution needs input (auth, a human answer); not a failure | -| suspension | a durable wait: a crash restarts into the same wait | -| suspension ID | an opaque stable identifier derived by the trusted workflow execution for one logical suspension request; replay preserves it and only equality and input correlation are public | -| delivery | retaining one externally supplied value for an exact durable suspension request, without executing the workflow run or advancing its lifecycle: it takes no executor lock, begins no document execution, appends no journal event and changes no run status. It is distinct from resume, which is the execution that later reaches the wait; from answer publication, which is what that execution appends; and from answer consumption, which is what spends the retained value | -| answer publication | appending the durable answer event that records what ended one durable wait; performed by the resuming document execution, never by a delivery | -| answer consumption | marking a retained delivery spent; it commits in the same transaction as the answer publication it belongs to, so neither can be observed without the other | -| workflow executor | the live host invocation that advances or settles one workflow run | -| executor lock | the scope-owned, non-blocking exclusive host lock that permits at most one workflow executor for a run | -| stale execution | an unfinished retained document execution found after acquiring the executor lock proves its previous workflow executor is gone | -| inspection recovery coordination | provider-private cross-process exclusion between copying a crashed run's database-and-journal pair for inspection and opening the retained database write-capably for SQLite recovery; it grants no executor or lifecycle ownership | -| recovered inspection snapshot | an immutable lifecycle snapshot read from a private scratch copy after SQLite rolls that copy's hot journal back to the retained store's last committed state; the retained database and journal are not changed | -| XMD artifact | one immutable portable evidence file with the `.xmd` extension, containing one workflow run's committed retained state at one artifact frontier together with its workflow definition source closure; it is neither a live run store nor permission to continue the source run | -| artifact frontier | the exact committed lifecycle snapshot, journal boundary and current Workspace root an XMD artifact records | -| workflow definition source closure | the exact root Markdown source and every source in its declared workflow component bundle, the retained bytes re-derived from the run's own store. Retrieval metadata, provenance and a repository checkout are not part of either | -| artifact manifest | the canonical versioned inventory of every semantic record and retained byte in one XMD artifact, excluding its own derived identity and physical container layout | -| XMD artifact identity | the lowercase SHA-256 of an XMD artifact's artifact manifest; it identifies the evidence independently of its host path and physical encoding and is retained as the source identity of a history fork made from that artifact | -| Agent session portability evidence | what one XMD artifact states about every logical workflow Agent session that contributed a retained Prompt: either complete portable evidence — the session and its compatibility attributes, the provider, the bundle kind and compatibility identifier, the source and bundled provider-session identities, the identity-allocation mode, the ordered provider checkpoint tokens, and the Agent session bundle's length, hash and bytes — or an explicit unavailable marker naming why. A session that retained no Prompt has none. It is detached retained evidence about a conversation, never a live provider session and never control over the host that produced it | -| provider checkpoint token | one opaque value a provider issued for one completed Agent Prompt, retained in Agent session portability evidence beside the event it belongs to. It says where a conversation reached; it is not a credential, is never derived from event position, transcript text, another token or a provider head, and no ordinary presentation renders it | -| Agent session bundle | the opaque bytes a provider captured one Agent session as, retained under that session's key in an XMD artifact. Nothing scans or scrubs them: a bundle may hold transcript text, tool state, secrets a conversation repeated and the exporting host's own paths, and it carries the confidentiality of the whole retained Workspace | -| Workspace | the provider-neutral, run-owned environment that supplies retained filesystem, repository, process and working-directory capabilities to a workflow | -| document filesystem | the files a document names in its own text, reached only through `API.Files`; distinct from the host paths the engine's own control plane reads | -| Files provider | the installed implementation of `API.Files` for a document execution: the host provider under `xmd run`, a transaction-bound provider under a workflow run. There is no default, and no provider falls back to another | -| stable host namespace | the condition the host Files provider's containment is stated under: no other process replaces a directory, symlink, junction or reparse point between the moment a path is observed and the moment it is used | -| Files infrastructure failure | a Files provider that is absent, that refuses an operation, or that broke its own contract; fatal like a durability failure, and never a printed error | -| ephemeral | a replay classification for an operation, context or attachment that runs again to reconstruct live execution; its result is not substituted from the journal and it owns no durable workflow state | -| live binding | an execution-owned value reconstructed ephemerally for the current document execution; it is visible only to constructs that explicitly consume the live binding overlay and never enters interpolation or the journal | -| attached service | a scoped host process that publishes its authenticated loopback endpoint through the XMD service handshake protocol and remains supervised for the lifetime of its service attachment | -| effect transaction | the single atomic SQLite transaction that publishes one Workspace-local mutation together with that effect's journal result | -| external effect | an effect whose provider-owned outcome cannot participate in the Workspace SQLite transaction and therefore requires a stable identity and provider reconciliation | -| XMD-mediated HTTP read | one bounded, non-mutating HTTP request an authored document performs through the contextual Fetch capability, whose detached response is retained as an ordinary durable observation. It is distinct from an Agent's own network permission, which it does not grant, and from a reconciled mutating external effect: it performs no reconciliation, so an interruption before its record commits may repeat the read | -| Git-host evidence read | one bounded, non-mutating read of a collection a Git host already holds about the pull request a canonical URL names — its reviews, its comments, or the checks on its head. It needs no Repository in scope: the URL is the identity, and the selected provider parses the repository and number out of it. It is authenticated with the host's own credential, bounded by the operator's `allowed` list, and paginated by the adapter, and it is *not* a reconciled external effect: it has no natural key, no pre-state and nothing to adopt, so it is an ordinary durable read whose interrupted attempt may repeat, whose completed record replays without contacting a provider, and which a fork inherits. It answers completely or not at all — an empty collection and a collection nobody could finish reading are different answers | -| object-source attachment | adapter-private composition data the trusted host builds for exactly one external-effect reconciliation, giving the selected provider authorized live access to local state the frozen request cannot describe — the Git objects a push publishes, and the retained locator it is authorized to reach. It is authenticated against the durable identity it belongs to and proven contained before its first use — the local state behind one can name further state, so the graph a provider could traverse through it is validated rather than assumed — held in that provider's own closure, disposed with the invocation, and is never durable, never part of a natural key and never visible to routing middleware | -| checkpoint | a completed journal boundary associated with the logical Workspace root visible after that effect | -| history fork | a new workflow run that replays a compatible journal prefix and continues from its checkpoint and Workspace root under a new immutable document definition | -| loaded copy | one independently evaluated instance of a package, such as the copy bundled into the binary or a separately installed dependency | -| authoritative behavior | behavior that decides what an execution or effect *is* — whether it happens, what it may replay from, and what it settles to. Non-authoritative behavior may inspect, narrow, refuse or add a failure, but cannot bring an execution into being, substitute one, or rescue one | -| testing harness | the testing-only construct through which one canonical `` invocation runs another document as a real document execution under a selected host profile; it is not component composition and is unavailable to any element core did not recognize as its own `` | -| host profile | a named production assembly a trusted host offers a testing harness — `run` today — reused after argument parsing rather than restated; a host that cannot offer one refuses rather than approximating it | -| execution outcome | what one nested document execution ended as, as the harness publishes it: a settlement carrying the exact completion `Result`, or, for a workflow attempt, a durable suspension carrying the run and suspension identities. A suspension is neither success nor failure | -| trusted host | the code that decides what an execution is for — a CLI entrypoint or a workflow runner — as distinct from the document, the components it expands, and the middleware packages composed around it | -| native session launch | preparing one durable coding-agent session from a document and then handing its provider's own interactive UI the terminal for that exact session. It is session construction followed by an ownership handoff, not a chat frontend: XMD proxies no native turn and journals no native conversation, and takes no model turn of its own beyond the one materialization turn an adapter may require to make a freshly created conversation resumable | -| provider-native session identity | the identity an agent asserts for its own durable session state, distinct from the logical XMD session key and from the ACP/ACPX record identity. Only an asserted one crosses a handoff; XMD never infers that a string a native CLI would accept is one | -| pending session placement | an exact provider-issued `Session` capability carrying where a logical session will live and which agent it is compatible with, and nothing more: no construction route, no provider handle, no resumable identity. A fresh `` produces one, and the first consuming operation chooses how the session is constructed | -| session materialization | the transition that makes a placement's chosen route and its backend history resumable. ACP-first materialization happens only when the backend reports that it accepted the session's first turn; client-native materialization is the native launch's existing retained construction. Nothing else promotes a placement — not a returning ensure, a first output, a terminal result, a checkpoint token, an error code or a diagnostic | -| established session | a placement whose immutable construction route and durable provider or native identity both already exist, and which is therefore validated eagerly: reattached, compared against its retained history, and refused when either is missing or names another conversation | -| instruction layer | the provider-native session, system or developer instructions a launch installs before the native UI accepts its first user turn. It is not a user message, and it is not conversation history | -| foreground-terminal lease | the one exclusive hold on a document execution's foreground experience. A root native launch holds it for one inherited terminal. A host with no terminal refuses it, and no second root launch can hold it concurrently | -| native launcher | the host-owned seam that reserves the foreground terminal, flushes what that terminal has pending, starts one native UI there, and reports its terminal status and nothing else. It is not `exec`, whose children are piped, captured and journaled | -| launch request | the frozen, one-use value public launch middleware routes. It carries the facts of one launch and `with()`, and nothing that can settle one. Identity is object identity: a rebuilt look-alike describes the same ask and is admitted nowhere | -| agent launch coordinator | what core delivers to the provider factory it installs, as an argument that factory closes over. It validates the routed request, runs each absent phase once, cross-checks and retains what comes back, and derives the result. There is no reader for one, no context holding one, and no request member carrying one | -| session coordinator | the host-built capability that answers who owns one logical agent session right now, across processes. It is passed directly into the provider that needs it and is deliberately not contextual: a decision document middleware could replace is not one. Acquisition never waits | -| construction route | how one logical agent session was first constructed — through ACP, or under an identity XMD chose — as strict create-once durable state beside the coordinator's own records. It answers a different question from ownership and grants no right to act: the coordinator remains the one live owner. A route never converts, and a session ACP already established is recorded as ACP-first rather than reclassified. Two schemas are readable: the released `session-route.v1`, and `session-route.v2`, which exists only for `client-native` and adds a required executable build binding. New client-native construction publishes V2; a V1 client-native route stays legacy-unbound, is never upgraded in place, and admits native resume only | -| executable build binding | which build of a provider executable accepted a client-allocated identity, as a schema, an adapter-canonical version and the lowercase SHA-256 of the canonical executable target. Equality is exact over all three, so a matching build reached at another path is the same build and a changed build at the same path is not. It contains no path: a path says where a build was, which stops being true, and names host layout besides | -| executable observer | the host-built capability that resolves a launcher command through the host's real execution environment, canonicalizes the target, requires an executable regular file, hashes that target and asks that same file its version. Passed directly into the provider like the coordinator beside it, and deliberately not contextual: executable validation decides which retained history may be accepted, and a resolver document middleware could replace could point the observation at one binary while the run spawns another | -| client-native attachment | joining a conversation a native process constructed, through ACP, under the identity the route already names. It is attachment, never conversion: the route stays client-native, `resumeSessionId` receives that exact identity, and the provider's canonical assertion must equal it before the first turn. Advertised separately from native launch, because handing a session to a native UI and later joining it prove different things | -| identity provenance | who chose a provider-native session identity: the provider returned it, or the client allocated it before the provider existed. Retained rather than inferred, because after the fact both are just a string in the record. A released record written before the distinction existed reads as provider-returned, which is the only compatibility inference | -| quiescence | the one-use acknowledgement an ownership body gives. It does not mean "I finished" — it means nothing this owner started can still act on the session, which is why a handoff that could not release its handle never gives it | -| recovery tombstone | an ownership record left active because its owner never proved it stopped. A crash releases the kernel lock and not this, and no pid, elapsed time, released lock or empty transcript clears it | -| provider partition | one complete, independently owned agent-provider state — runtime, store, managed sessions, queues, coordinator, teardown — selected by the one installed factory at each dispatch. Production is the single-partition case of the same path; holding a partition grants work, never permission | -| `JournalProvenance` | 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 is meaningful only because the provider retains the witness it established and later requires exact equality | -| result object | the value a component binds instead of failing: `{ok: true, value}` or `{ok: false, …}`, whose failure members the component declares | -| syntax symbols | the complete, versioned description of what a document may write in one directory under one host profile: every structural construct the engine reserves, and the one implementation selection chooses for every other name. It is observation — describing an environment installs no operational state, runs nothing and journals nothing — and it is produced once per request and projected, never rediscovered per format. `xmd syntax` prints one for an environment nobody is running; canonical `` renders one for the site an element was written at, from the same construction and the same Markdown renderer | -| execution environment | the private bundle of services and records one execution supplies while its document expands: what resolves a name, what routes it to a retained implementation, what the execution declared, and the records it keeps about its own identities, forms, vocabulary and source output. The execution builds it from what it captured and admitted, owns it, and passes it by value through canonical expansion — never through a context, because a context resolves by name and a name is not a secret. An expansion driven directly is handed none at all, which is what "nothing is installed here" means; there is no partial one. Its shape is an architecture and product boundary, which is why the resolution an execution performs for one *import* is not on it: that terminal is handed to expansion by hand, as an internal argument beside this bundle, named by no entry point and reachable only by having been given it | -| syntax reference | the engine-owned lexical answer to "what may a document write here", carried by value on canonical core's execution environment beside what resolves an import. The execution builds one at its root from the selection inputs it captured before any installation, middleware or document code ran, or from the one set of symbols a trusted host stated for its profile; a trusted canonical evaluation boundary replaces it for the subtree it evaluates. It answers with text and decides nothing: a component the symbols name is not a component anything may run | -| run profile declarations | the component registrations a first-party package makes, held as plain values apart from the middleware, providers, activation and launchers its installer also arranges. The installer registers exactly those values and inspection reads exactly those values, so what a run installs and what the symbols report cannot drift | -| Plugin | trusted code installed once before an execution imports a root document. XMD bundles exactly one — `@executablemd/git` — and activates it for the run-profile commands: `run`, `plan`, `syntax`, and `workflow start`, `resume` and `fork`. The `xmd test` root is not one of them, because a harness that quietly gained the repository vocabulary would be reserving names its children are entitled to shadow; a nested `` child assembles the profile for itself rather than inheriting what its parent declined. Everything else an operator selects with `--plugin`, and a package that happens to be installed stays inert until it is named. A plain structural value carrying a name and one optional `install`; what it contributes is the existing `ExecutionInstallation` members, and what it composes through are the three stable contextual APIs below. Selecting one runs its module, and selected Plugin code can execute whatever the surrounding runtime permits: it is trusted, and it is not sandboxed | -| document envelope | the Markdown the `Document` Api composes for one run: the canonical `` placeholder wrapped by each Plugin's middleware, first Plugin outermost. Canonical core scans it once and splices the root's already parsed, already target-selected segments in at each placeholder, at their authored positions — so an unwrapped run is the run it always was, and an authored `` is an ordinary element that projects nothing | -| active Plugins | the frozen ordered list of Plugin values installed for one command — the bundled Git Plugin first where the command's profile carries it, then the operator's own in the order they wrote them — published before the first `install()` runs so that every Plugin and every later consumer read the same list. It is a description of what is installed and grants nothing | -| command declarations | everything one command declares, in one order and under one discriminant: the exact Markdown components *and* the structural syntax the selected Plugins declared, then whatever the calling surface declares for itself. Both arms, because both are what a name means here — a catalog holding only the Markdown half would let `xmd syntax` and ``'s validation describe a language a run does not have. Built once per command so that an ordinary run, a nested `host="run"` child, inspection and ``'s validation cannot describe four different vocabularies | -| origin-only | the inspectability of a component whose contract could only be learned by loading it: a repository TypeScript module, whose schemas live on its exports and whose top level would run. Such an entry carries name, category, origin and source kind, and no contract field at all — an absent contract is stated, never rendered as an empty one | -| definition-owned return state | which value body a `` selects for: one ephemeral state per execution of one value root or Markdown value component. Structural directives keep the ambient one, a component invocation hides it from the invoked body, a nested value body installs its own, and caller-projected content restores the caller's. It travels down the expansion call stack as a local rather than through a context, and no exported function accepts another body's, so nothing a document can read, replace, or import acts on a live one. The first reservation on it is atomic, so a second executed return fails the body rather than replacing its value, and it appends no durable event | - -## Three axes +Consider a document that writes a file, reads its contents, then deletes it. +Each step has an observable result. At the end the file is absent, but the read +still describes the contents observed between the write and deletion. + +An ordinary `xmd run` uses the caller's environment. Its `--journal` is a new +diagnostic trace, not input to a CLI continuation: another invocation starts a +new execution. A Plan is source for a program; producing or approving it does +not run it. The caller composes it with a run when execution is wanted. + +A workflow instead retains the definition bytes, props, journal and filesystem +state under a run ID. Partial replay rebuilds the live resources needed to continue and restores +completed effects from their records, then performs the first unrecorded work. +It does not write or delete again merely because those steps are reached again. +A completed root returns its retained output and result without expanding the +document or attaching operational providers. + +The journal is historical; the Workspace is the current filesystem frontier. +After the three steps above, replay restores the old read even though the file +is now absent. It never refreshes an observation against today's filesystem to +prove that the earlier effect happened. These distinctions govern +[effect transactions](#replay-and-effect-transactions), +[workflow lifecycle ownership](#workflow-lifecycle-ownership) and +[ordinary command retention](#foreground-commands). + +## Shared rules + +Start with the properties every execution shares: predictable behavior, scoped ownership and composition across independently loaded package copies. + +### Three axes The UX has 3 axes: determinism, resilience and debuggability. If we pull too far in one direction, the others suffer. Errors are inevitable in a system @@ -159,2905 +71,2727 @@ Determinism is two properties: replaying the journal arrives at the same state, where execution can continue. -## Workflow runs - -Core retains one document-execution entry point: `execute()`. A host that needs -workflow-run metadata passes an `ExecutionInstallation` to `executeInstalled()` -for the one document execution it is preparing. The run is created or -restored by that installation's preparation, which canonical core invokes -inside the durable root — so the journal is active before the workflow run -exists, and the run exists before any document policy observes it and before the -root document is imported. -A later document execution, including one that continues the same workflow -run, gets a new child scope and a new middleware installation. Workflow -metadata is durable; the middleware and every ongoing effect remain -scope-owned. +### State ownership -Installing the middleware alone creates no workflow run. On the first live -document execution, the first durable operation installed by the middleware -uses the host-authorized requested run ID or allocates one using cryptographic -randomness and durably records one value: +All state is scoped to the operation that owns it, so it is torn down when +the operation is torn down: created inside the operation it describes, +provided via context. No module-scoped registries — not as collections, not +hidden inside library objects that accumulate. One exception: metadata an +author declares at module evaluation, about a value the author owns, may live +on that value. -```ts -interface WorkflowRun { - readonly runId: string; - readonly bundleHash: string; - readonly targetPath?: string; -} -``` +A callback registered with something outside the process — a listener on a +socket, a child process, a stream or a DOM target — is state of exactly this +kind, and the source it is attached to knows nothing about the operation that +attached it. The event arriving is not teardown: a cancelled wait is precisely +the case where it never arrives, and a losing race arm is one that will never +be told. So the owner detaches its handlers, on completion, failure, halt and +race loss alike, before it is considered closed, and an event delivered after +that reaches nothing and changes nothing. `local/require-scope-bound-event-registration` +holds source to it. -The workflow run exists once that value is durably recorded. A document failure -or cancellation after that point does not erase it. Failure before that point -creates no workflow run and the root document does not expand. +A Repository selection is composition data and is therefore replaceable: a +document may bind one, render one, hand one to a child, and construct one that +looks exactly like it. Nothing a repository provider does is authorized by the +value it was handed. What stays provider-owned, in the provider's own closure +for one document execution, is everything a selection is *not*: the advisory +locks on managed checkouts, the canonical Git identity each selection resolves +to, an ordinary run's live Push evidence, its invocation identity, and every +reconciliation key derived from them. A selection that the provider did not +mint, or one whose name, checkout path or identity was edited after it did, is +refused before Git or a service is touched. -`WorkflowRun` is one closed shape carrying `runId`, `bundleHash` and the exact -target when it has one. It has no base and no pinned commit: a run starts from -exact retained bytes rather than from a repository state, so a member naming one -would be a statement about a repository the run never had. It neither creates nor -seeds a Workspace Repository. Each `` inside the definition resolves -and pins its own optional Repository base, so one workflow may compose -repositories unrelated to the definition repository or to one another. +### State across loaded copies -Read-only Git questions go through the contextual Git capability, which the -workflow run itself never asks: +The binary bundles its own package code while components and integrations may +load another copy of the same package. Those loaded copies must be able to +compose: one can provide shared state that another reads. -```ts -interface GitQueryApi { - resolve(revision: string): Operation; -} -``` +Shared composition has two replaceable forms: -`GitQuery.resolve(revision)` has the semantics of -`git rev-parse --verify --end-of-options ` in the contextual working -directory. Its default provider invokes the Git CLI; another provider may -replace it lexically. It lives in `@executablemd/git` with the rest of the -capability, and the workflow package calls none of it. Ordinary `execute()` -remains Git-independent, and so does starting, resuming, forking or exporting a -run. +- Composition data uses an Effection Context with a stable, namespaced string + name and a plain structural value. Independently constructed descriptors with + the same name intentionally address the same binding. +- Contextual operations use a contextual Api with stable, namespaced operation + names. Lexical middleware replacement is part of their contract. -**The workflow package imports no Git feature, and a retained run reaches no -repository either.** The CLI establishes a candidate from filesystem bytes, and -the retained lifecycle re-derives the source from the run's own store, so a run -genuinely needs none: there is no definition whose Markdown lives anywhere but -in the run that retained it, and therefore no source capability for a host to -supply. +Neither form is a permission boundary. Security enforcement, durable-effect +identity, and reconciliation never trust a same-named binding merely because +its value has the expected structure. They derive from execution-owned state +established before untrusted work begins or from parsed durable records. A +contextual observation may describe an execution; it cannot authorize it or +name its durable effects. -Replay restores the recorded `WorkflowRun` without allocating another run ID or -invoking Git. The supplied bundle hash must equal the recorded one. +A private context name is not an exception to this. A descriptor built under a +name nobody else knows — a module-local one, a name carrying a fresh identifier +— is still a context, and reaching a capability through one is still reading +replaceable state to decide it. The same holds for a public answer that carries +an unreadable value: a registry record whose contents a handler cannot read is +still a record that handler can keep and hand back somewhere else, so what +cannot be read can still be transferred. A capability that must not move is held by +the execution and delivered as an argument, never looked up. -A host that has already created the run's storage record passes -`retainedWorkflowInstallation(run)` instead, with the exact frozen value. -Nothing is left for the execution to decide: it records that value through the -same `workflow_run` durable operation, allocates no identifier and resolves no -base, and every journal state — live, truncated and completed — requires the -recorded run to agree with the supplied one in every member of its own version: -run ID, base and pinned commit for version 1; run ID, bundle hash and exact -target for version 2. A journal that disagrees in any of them, or that records -the other version, is not this run's journal, and the refusal names the fields -rather than their values. +Metadata covered by State ownership's module-evaluation exception uses a +stable, namespaced string property when another loaded copy must read it. A +module-local symbol is unreachable to another loaded copy. A registry symbol is +reachable but unowned, and offers nothing a namespaced string does not. -`getWorkflowRun()` returns the frozen `WorkflowRun` for the current document -execution. Every call in one live execution returns the same object. It throws -outside a document execution a workflow installation was passed to, and it -exposes no journal, Git, workspace or continuation capability. Replay preserves -the field values, not JavaScript object identity. +A descendant may replace a same-named Context binding for its own descendants +under ordinary lexical rules. Private fields, constructors, symbols, +module-local identity, and private sentinels remain valid when no second loaded +copy needs to read the value. String names and metadata keys that cross loaded +copies are compatibility contracts; changing one is a breaking change. -The `@executablemd/workflow` package owns `WorkflowRun`, -`retainedWorkflowInstallation()`, `getWorkflowRun()` and the generic run, -storage, replay and Workspace boundaries. It owns no Git: repository -composition, the Git capability, issue and pull-request contracts and the -Git-host engine are `@executablemd/git`'s, and that package imports Workflow's -published extension boundaries rather than the other way round. It depends on -`@executablemd/core`, `@executablemd/durable-streams` and -`@executablemd/runtime`; core never imports workflow or Git. `xmd workflow -start` and `xmd workflow resume` are the CLI lifecycle, and they resume through -the run storage below. Ordinary `xmd run` carries the bundled Git Plugin, so -the repository vocabulary is available there without an operator naming it. +Unreachable to another loaded copy and unreachable to a holder of the object are +different properties, and only the first is what a module-local symbol gives. +A symbol-keyed property is an ordinary property: `Reflect.ownKeys()` returns it, +a descriptor read returns its value, and `Object.getOwnPropertyDescriptors()` +copies it onto a look-alike — non-enumerable and frozen change none of that. So +a symbol names state safely and hides none. State a value must carry but its +holder must not reach belongs in a private field, or in membership of a set the +owner keeps; both are absent from every key list, descriptor, and copy. A +capability boundary that a handler holding the object could read across is not +one, whichever of the two properties it was reasoned from. -Git publishes on three subpaths, and which one a consumer imports from says -what kind of thing it is reaching for. `@executablemd/git/api` is the only -route to the contextual Apis — `Repository`, `RepositoryContext`, `Git`, -`GitQuery`, `PullRequestAPI`, `IssueApi`, `IssueTrackerContext` and `GitHost` -— together with their named interfaces, the identity each was minted under, -the base refusal each falls back to, the direct operations and accessors -consumers call, and the types those interfaces are written in. Those are the -seams: a consumer reaches one to call an operation and replaces one to answer -it. The root publishes the Plugin and its profile predicate, the component -registrations and definitions, the workflow installation, the durable effect -identifiers, the errors and the record parsers — what was said, rather than -who answers. `@executablemd/git/deno` publishes the host adapters that -implement the seams, and `@executablemd/git/credential-helper` is the -standalone program Git spawns as itself. An Api is published from exactly one -of these, not re-exported from the others, because a seam reachable two ways -is two contracts. +## Executing a document -**Each Api is named for the operations it answers, and every identity is under -one `executablemd.git` prefix.** A consumer writes the vocabulary of the thing -it is doing rather than of the machinery behind it: +Execution first acquires and selects source, then expands it under lexical bindings and ownership. The rules here distinguish authored identity from runtime scheduling. -| Api | Identity | Operations | -| --- | --- | --- | -| `Repository` | `executablemd.git.repository` | `select`, `worktree`, `ambient` | -| `RepositoryContext` | `executablemd.git.repository.current` | `current` | -| `Git` | `executablemd.git` | `switch`, `add`, `commit`, `push` | -| `GitQuery` | `executablemd.git.query` | `resolve`, `root`, `format`, `read` | -| `PullRequestAPI` | `executablemd.git.pull-request` | `read`, `upsert` | -| `IssueApi` | `executablemd.git.issue` | `read`, `upsert` | -| `IssueTrackerContext` | `executablemd.git.issue-tracker.current` | `current` | -| `GitHost` | `executablemd.git.host` | `route` | +### The root document a run is given -`Git` and `GitQuery` are two seams and never one. `Git` performs the four -authored durable transitions a document writes as ``, -``, `` and ``, and what lifecycle they have is -the installed provider's. `GitQuery` asks read-only questions of the checkout -containing the contextual working directory and mutates nothing, so replacing -it answers questions rather than becoming a route to a transition. +An `xmd run` starts from exactly one root document, and it comes from one of +three places: a path, standard input, or one `--eval` value. Which one is fixed +grammar — decided from the command form the caller wrote, before anything is +read — so a run never discovers its source part-way through preparation. -These names and identities are a break from `@executablemd/workflow@0.12.1`, -taken deliberately and without compatibility aliases. A provider compiled -against an old identity intercepts nothing, and an import of an old name does -not resolve: two vocabularies for one seam is the outcome an alias would -guarantee, and a silent miss is what an old identity would produce. The -package-internal names that still read `Composition` — the provider -installers, the error classes and the serialized refusal names retained -history holds — are not this layer and did not move. +The command form is what separates the three, because a path and standard input +can be written the same way. `-` is the one filename the option grammar leaves +unwritable, so it reaches the reference grammar through the parser's own +unconsumed tokens: bare `xmd -` names that file, `xmd run -#Section` selects a +section of it, and only an invocation that explicitly names `run` with a +document argument of exactly `-` means standard input instead. -## Workflow run storage +Standard input is the one of the three the shared CLI cannot reach for itself. A +process's own stdin is host-specific state, so each runtime-named entrypoint +supplies the operation that reads it, as a value passed into the CLI beside the +service installer, the upgrade assembly and the repository installer. It is not +a Context, a contextual Api, a public export, a syntax entry, a component or a +document capability: nothing an authored document can name reaches it, replaces +it, or asks it for a second read. The CLI calls it at most once per invocation, +and only for the one command form that selected it. -A workflow run recorded only in the journal of the document execution that -created it can be found only by whoever already holds that journal. Retained -storage is what makes a run addressable: another process opens it by public run -ID and continues from durable data. +The parsed path and recovered reference remain separate until the grammar is +settled. Naming two roots refuses in either order before either candidate is +inspected or standard input is read. A `-` consumed as another option's value is +not a document argument; another command's `-` keeps that command's meaning, +and `--eval -` keeps its refusal. -Shared code reaches storage through a contextual Api that names no provider, so -the lifecycle stays host-neutral. The Deno host installs its own from a -runtime-named entrypoint, and that entrypoint is the only place SQLite, run-ID -hashing, filesystem paths and host filesystem behavior appear. No shared module -imports them or detects the active runtime. The absence of a provider is -reported rather than answered with an empty store: a run that appears to start -and retains nothing has not started. +Core represents the supplied source as `retainedSource("", source)`. -A storage handle is a lease owned by the scope that opened it. Lease teardown -makes that handle unusable without closing the run's physical connection or -invalidating another handle. The Deno provider owns the authoritative -SQLite/DOFS connection for each canonical workflow-run database path and closes -it at provider-scope teardown after its child scopes finish. The public handle -contains no raw connection association; the provider owns an exact-object lease -registration and refuses foreign, fabricated, closed, or stale handles before -they can reach SQLite. +What comes back is admitted through the existing supplied-source protocol rather +than a form of its own. The origin `` and the exact text travel together, +which is what source positions, diagnostics, the root binding and the durable +root import all carry — so replay restores the retained source without reading +anything, and two different programs are two different roots without a second +identity field. `` is an origin and never a path: no file of that name is +read or created, and every relative operation still resolves from the contextual +working directory. -### Identity is separate from retrieval +Acquisition is the first thing the run does. The complete input is in hand +before document inspection, target and property preparation, Agent-stack or +provider setup, the secret-detection announcement, journal creation, root +admission and execution — so a host that cannot deliver a whole document reports +one fixed sentence, exits nonzero and has reached none of them. It happens +inside the run's existing deadline rather than opening a lifecycle of its own, +and a waiting read is cancellable: cancellation tears the reader down and stays +cancellation, never a read failure. -Identity is the run ID, the whole workflow definition, and the normalized props. -Values are compared canonically, so props differing only in key order are the -same props. The comparison covers the bundle hash, the entrypoint, the whole -canonical source manifest, the component mapping and the exact target. The -descriptor carries no version and no kind: one shape is the only shape, so there -is nothing to dispatch on. -Everything a run accumulates is excluded from that comparison: status, stop -reason, retrieval metadata, timestamps, document executions and journal -records. A completed run asked for again is found, not refused. +### Document targets -Creation is therefore also lookup. A request describing the stored run answers -with it; a request differing in any immutable field is a different run wearing -the same ID and is refused. That is what makes a caller-selected run ID usable -twice without a separate idempotency concept. A refusal names the fields that -differ and never the values behind them. +A root document addresses its own sections. The outline is discovered from the +document's static Markdown alone: only headings in the root flow are targets, +and a heading is addressable only when its text is statically rendered — a +heading generated inside a component, or one carrying an interpolation, has no +stable address, and neither does anything beneath it. Discovery therefore +parses a copy in which the scanner's top-level component spans are blanked, +because a Markdown parser reading raw XMD cannot tell a component's children +from the root flow. -Where a definition can be fetched from is retrieval metadata, not identity, so -a run that moves between hosts is the same run. +A document's preamble is what precedes its outline — the source before the first +root-flow heading, whatever its depth. A document may open at a deeper level than +the one supplying its title, and an earlier section is a target in its own right +rather than preamble every other target carries. -### Discovery has one source of truth +Selection resolves exactly once, before the document expands and before any +authored effect runs. A selector may glob, but it must name exactly one catalog +entry: naming none and naming several are both failures, and two sections that +canonicalize to the same path stay two entries so the ambiguity is reported +rather than resolved arbitrarily. -A run's location is derived from its run ID, so discovery is arithmetic and no -second registry exists that could disagree with what is stored. The storage -root and the location it produces are host arrangement rather than durable -identity, which is why the run ID is also stored inside the run and checked -against the one requested. Storage holding a different run than its location -implies is a collision or tampering: it is reported as its own condition and -left unchanged. +The selector and the target it resolves to are different things, and only one of +them is identity. -### What a run retains +**The exact canonical target is definition identity.** It is what ran, so it is +what a document execution records durably, what a targeted workflow definition +carries, and what a resumed run is checked against. -A run retains its immutable identity, one of six statuses — `running`, -`suspended`, `interrupted`, `completed`, `failed`, `cancelled` — a nullable -stop reason, replaceable retrieval metadata, one document-execution record per -start and per resume, and the filtered journal. Document-execution records are -not attempts: an attempt is one execution of a retried operation or region and -remains in the journal. +**A caller's glob is non-authoritative invocation metadata.** It describes what +a caller asked for — two callers may spell one request differently — and it +never substitutes for the exact target: it does not occupy the recorded +exact-target field, it never enters a workflow definition, and it is never +re-resolved against a newer checkout to decide what a resumed run means. A glob +is retained in exactly one place, a failed selection's structural record, and +only so that an ordinary failed execution can be reproduced. -A stop reason is a categorical host code or a reference to an already-filtered -journal event. Arbitrary exception text is never duplicated outside the journal -that filtered it. +The command line is the first consumer of that split. `xmd run` resolves a +selector while inspecting the document, then asks execution for the exact target +that resolved — so a file replaced between the two reads fails on the target the +run chose, rather than silently running whatever the glob would name now. -WorkflowRun schema version 1 is complete in place. It contains the run records, -filtered journal, pinned Cloudflare DOFS version-5 structure, immutable -Workspace-root tables, current-root state, a non-null Workspace-root -association on every journal event, and the Repository and Worktree creation -records #293 added to it. A fresh run starts with one -content-addressed root manifest describing only the root directory, empty -retained manifest and blob reference sets, and the corresponding root-only DOFS -frontier. +`xmd workflow start` is the second, and it is stricter because its answer is +retained. It resolves the selector once, against the exact bytes it is about to +retain, and stores only what core answered. A resume re-enters that section +without resolving anything again: the glob is gone, and the bytes it was +resolved against are the run's. -`workflow_run` has **no `base` column**, and two tables hold the run's own -source: content keyed by its SHA-256 with its byte length, and one manifest row -per descriptor entry mapping a logical path to that hash. Content is retained as -BLOB bytes rather than as text or re-encoded JSON, and two paths holding -identical bytes reference one blob. +The workflow definition is the second. It optionally carries that exact target, +compares it with the rest of the descriptor, and validates it through core's own +canonical-target predicate rather than a rule the workflow package restates — +identity two packages define separately is identity they can disagree about. A +run of one section, a run of another, and a run of the whole document are three +different runs. -A new run is written at `user_version = 1`, and initialization never upgrades an -existing database. Recognition reads the application ID first, then the schema -version, and holds the whole inventory to its one declaration. Version 0 under -that inventory is corruption, any other version is unsupported, and an altered -or extra object is corruption rather than a migration candidate — there is no -earlier shape for a file to be recognized as. +A resumed run re-resolves the current selector against the *recorded* content +and refuses to continue unless the outcome is the one recorded. A failed +selection is an outcome too, and is recorded and compared as one — otherwise a +journal left by a selector that matched nothing would answer a later request for +a section that does exist. -Before a record is exposed, its retained source is re-derived in full: -the manifest equals the descriptor's `sources`, every referenced blob exists, -both retained lengths equal the content's own, recomputing each blob's hash -produces its key, no blob is unreferenced, and recomputing the bundle hash from -the retained manifest and mapping produces `bundleHash`. No reader returns a -partial bundle, and nothing falls back to the original file or the provenance. +Validating that identity is **execution-owned**. Replay guards are composable +policy — a handler installed further out may decline to delegate — so identity, +which must not be negotiable, is not decided there. The execution owns the +journal it replays through and validates the recorded selection inside the read +itself, ahead of public guard policy, of a retained terminal result being +reused, of authored work, and of any append. The retained history it owns covers +every event that takes part in that decision, a recorded completion included, so +no event can present one identity to the validation and another to the run. -A Workspace root is a complete, immutable filesystem checkpoint. Its canonical -format-1 JSON includes `/` and every reachable absolute POSIX path in UTF-8 byte -order, with kind, mode, observable mtime, file size and DOFS manifest identity, -symbolic-link target, and deterministic file-hardlink groups. The root ID is -the lowercase SHA-256 of `xmd-workspace-root\0v1\0` followed by those exact -canonical bytes. Mutable inode numbers, revisions, tombstones and cache state -do not participate in the identity. +Three views of that history stay distinct. The **authoritative** one is what +admission validated and replay consumes, and it is immutable to policy. +Replaceable public policy reads an **isolated observation** of it, so a handler +composes freely without acquiring the enforcement the gate exists to withhold. +What a document receives is a **fresh mutable copy**, because a resumed binding +is ordinary data its own continuation writes to. Reusing a terminal result also +requires exactly one recognizable root import belonging to the coroutine whose +result is being reused. -The root tables are authoritative checkpoints; the DOFS node, dirent and chunk -tables are the live materialization of the current root. Files remain solely in -DOFS content-addressed blobs. Normalized root-to-manifest and root-to-blob rows -equal the root's transitive content exactly and prevent retained content from -being deleted. Opening a run validates every root and referenced manifest and -blob, then snapshots the live frontier read-only and requires it to equal -`current_root`. The schema structure, retained content, live/current comparison -and run row are read through one explicit SQLite snapshot. This recognition -transaction is not a caller-owned Workspace transaction and enables no DOFS -savepoints. +Every public path validates in one order: parse the source, resolve the target, +compile schemas, then build the projected definition. A caller who named a +section the document does not offer hears that, rather than a complaint about a +schema they never reached. -Recognition also requires every file entry in every retained root, including a -historical root, to declare the size held by its referenced DOFS manifest. A -root whose XMD identity and reference rows are internally consistent but whose -file size disagrees with DOFS is corrupt and not restorable. +A recorded selection is a closed protocol, and a record that does not satisfy it +is refused rather than delegated. "This event is not the root import" and "the +root import, malformed" are different answers: one continues, the other fails +before the recorded terminal result can be reused, without executing authored +work or appending history. The record carries the content it was taken from, so +the selection is verified against that content rather than merely parsed, and +reading it is total — every way journal-controlled data can refuse to be read is +the same refusal, carrying nothing the record supplied. -Which status transitions are legal, and what a caller may do to a run in each -of them, is lifecycle policy applied above storage. +### The ordinary component prop boundary -### The journal is stored, not re-encoded +An ordinary component's props are JSON, and one resolver decides that for both +Markdown and function components — before validation, and before anything +durable is written. -Journal events reach storage already filtered, through the existing -pre-persistence gate. Storage adds no second filtering policy: a rejected or -cancelled gate leaves nothing behind. A record is retained in the -representation the protocol produced and parsed back through the matching -reader, so replay reads what was written rather than a re-encoding of it. -Events replay in append order. An event's opaque identity is retained -separately from its physical position; the position orders replay and is not a -public identifier. +- **A successful `undefined` means the prop is absent.** The resolver leaves it + out rather than giving it a value, so the schema answers it: an optional prop + stays unset, a declared default is supplied, and a required one fails as a + missing property with no body run. That is how one authored invocation can + carry an optional identity a preceding result supplies later. +- **`null` is a value the author wrote.** It crosses the boundary as itself, and + only a schema that accepts it accepts it. Absence is never widened to it, and + never widened to a root a JSON round trip cannot describe: a function, a + symbol and a serialization that threw stay the failure they were, as do an + expression that threw and a name that is not bound. +- **The rule stops at the root.** Everything under it is the same native + `JSON.stringify` round trip as before — an `undefined` object member is + dropped, an `undefined` array entry becomes `null` — and no recursive + normalizer of ours replaces it. +- **Deliberate bypasses stay bypasses.** A capture is separated before this + resolver runs and receives the exact operand, `undefined` included; `` binds by reference; a construct that evaluates an arbitrary operand + keeps its own contract. +- **Absence is not durable state.** Prop resolution records nothing of its own. + No resolved prop, validated prop, retained event or replay-restored prop holds + `undefined`, and a replay reconstructs the same validated prop set through + ordinary expansion. -### One database at a time, and a transaction a caller can hold +### Expansion identity -Operations on one run's storage are serialized, and each runs inside a -transaction. The Deno provider's per-path entry owns the one physical SQLite -connection, the one Cloudflare DOFS wrapper and Workspace filesystem, the -cooperative connection queue, and the savepoint allocator. Turns belong to that -entry rather than to a handle, so two leases for one run take turns instead of -contending — a host whose storage is reached synchronously would otherwise stop -while a second handle waited for a transaction the first one cannot resume to -finish. Different workflow-run paths have independent entries. +Core describes the executable element currently being expanded: -Each entry has an opaque generation identity created with the physical -connection. Reopening the same path after provider teardown creates a different -generation. Each top-level transaction has its own opaque identity and exact -active record, including its path, generation, authorized lease and public -transaction handle. Finishing invalidates that record before commit or rollback -can expose the connection to later work. Retained transaction handles and -tokens stay invalid across a later transaction on the same connection and -across a later provider generation. +```ts +interface Expansion { + readonly id: string; + readonly name: string; + readonly position?: Readonly; +} +``` -A caller that must publish several changes together holds the transaction -itself and receives a handle for taking part in it. Enlistment travels with -that handle rather than with the storage, so work that never received one -cannot join by accident: an unrelated append waits for its turn and commits on -its own rather than being rolled back with a failure it had nothing to do with. +`getExpansion()` returns the same frozen snapshot throughout one live +expansion. `name` is the authored tag name, independent of which repository, +registered or built-in component resolves it. `position` is the opening tag's +source position, including its workspace-relative path when known; dynamically +rendered markup may have no position. A nested expansion temporarily replaces +the contextual expansion and returning restores the enclosing one. Calling the +operation outside an executable element expansion throws. The snapshot exposes +no props, bindings, projected content, selected component definition or live +scope. -A transaction commits only once the work inside it has finished, including work -that is still unwinding when the body returns. Cleanup belonging to that work -appends through the same transaction, and a commit that happened first would -leave those appends to publish themselves outside it. Failure and cancellation -roll back everything the transaction did. Adapter-private Workspace work also -waits for its supplied scope to finish teardown before it performs the final -live/current validation. +An expansion ID is derived from the root document and structural expansion +path. It uses no process-global counter, time, randomness or scheduling order. +Replay, continuation, retry attempts and restoration of the same loop +iteration preserve it. Different authored elements, loop iterations, +projections, component expansions and root documents receive different IDs. +The ID is opaque and supports equality only. Two workflow runs may contain the +same expansion ID; a durable effect that needs workflow-wide identity uses the +run ID and expansion ID together. Replay preserves the snapshot's fields, not +JavaScript object identity. -A transaction opened inside another on the same storage is refused rather than -nested, as is an ordinary operation called from inside a transaction body. -The active-path context chain only detects and refuses that accidental nesting; -it never authorizes transaction or savepoint use. Authorization comes from the -provider-owned identities behind an adapter-private contextual operation, which -validates exact handle possession each time. +`Expansion` and `getExpansion()` belong to `@executablemd/core`, so ordinary +document execution receives expansion identity without installing workflow +middleware. -One monotonically unique allocator serves both synchronous Cloudflare DOFS -savepoints and Effection operation savepoints. The operation form receives an -`Operation`, runs it in a child scope, waits for every child and resource to -finish teardown, and only then releases. Ordinary failure rolls back to and -releases that savepoint while leaving the outer transaction usable. Synchronous -cleanup rolls an open savepoint back on cancellation or halt. A failure to -create, roll back, or release poisons the active transaction, so its top-level -owner cannot commit it. +Selecting a document target does not disturb any of this. Projection retains +the original source ranges and scans each one under its own origin, so a +retained element keeps the offset and line it was authored at, and with them +its expansion ID. Two runs of the same document under different targets +therefore agree on the ID of every element they both retain, and may share IDs +without sharing effects — run identity and workflow-definition identity are +what tell those runs apart. -Serialization is not a single-executor policy. Which executor may advance a run -is decided above storage. +### Definition-owned return state -### Damaged storage is described, never replaced +A value body — a value root, or a Markdown component that declares `returns` — +selects its value with a `` its own flow reaches, rather than with a +direct top-level child. What decides whose declaration an element satisfies is +the body it was written in, which is the ownership `` already establishes +for the loop it exits. -Storage is parsed, never trusted, and every condition a caller can act on -differently is reported as itself: absent, conflicting, holding another run, -not this kind of storage at all, a schema version this build does not -implement, damaged, holding a record that does not parse, asked for with a -value that describes nothing, or unable to transact. Collapsing them would -leave a host guessing whether to create, refuse, or report damage. +One state exists per execution of one value body. ``, ``, ``, +`` and `` keep the ambient one; a component invocation hides the caller's from the +invoked body, so a component's own `` satisfies its own declaration; a +`` passes none either, and the shared rule refuses the `` that +would have reached for one, so concurrent children cannot race to reserve a value +owned outside their ``; a +nested value body installs a separate one, and both executions stand; and +content the caller projected restores the caller's, through a Markdown +`` and a function component's `content()` alike, with every +projection of one authored element restoring the same state. Dynamically scanned +Markdown and a foreign body receive none, so a `` there stays reserved +and never resolves a component named `Return`. -Recognizing storage means holding its structure to the one this build writes, -not reading the names of its parts. Storage whose own header reports a version -it is not shaped like is damage rather than a version this build has not -learned. Storage is initialized only when it is pristine: something that merely -looks unused is not. +The first executed `` reserves the state before evaluating its expression, +so two returns racing through one body cannot both believe they were first. The +second fails the body, evaluates nothing, and the first value is not published — +there is no first-wins or last-wins fallback. A body that executes none settles +as a missing selection: the body ran and its flow reached no ``, which is +a structural outcome and carries nothing about what the author concluded. A +document that deliberately aborts, or that exhausted a bounded search, expresses +that as authored control flow with `` and the sentence its author wrote. -Complete version 1 is the first XMD schema. A database carrying the XMD -application identity with schema version zero is a partial initialization and -therefore damage, not a supported historical version. +**Selection is not a decision a document makes about itself.** A document's own +eval block reaches further than it looks: a named context by name, because +`createContext(name)` names the slot the engine would publish on, and an internal +module by file URL, because an eval block's imports are hoisted and resolved like +any other module's. Anything published contextually is therefore readable, and +any exported function acting on what was published is callable — a sealed carrier +in a context plus an exported `claim(carrier)` is forgeable however carefully the +carrier itself is sealed, because the document calls the engine's own helper with +the engine's own carrier. -Records are held to what they mean and not only to the types they are stored -in. A retained timestamp is an instant, a retained identity is not empty, and -normalized props are an object. +So selection uses neither. The body that owns a value is a plain object whose +reservation, selection and settlement are closures over state nothing else holds, and +it reaches the `` that reserves it as an ordinary parameter of expansion — +down the call stack, which document code cannot read or replace. Ownership is +explicit at every site rather than ambient: a component invocation passes none, +so an invoked body is isolated; caller projection passes the caller's, so +projected content still satisfies the declaration its author could see; and +dynamically scanned markdown and generated fragments pass none. Both settlement +sites read the body their own expansion created and held as a local. Importing +the module that defines one gains a document nothing — it gets a body of its own, +connected to no execution — so a value still crosses its declared schema on the +way out. -Nothing initializes, migrates, truncates, deletes or replaces incompatible or -damaged storage, and a lookup that finds nothing creates nothing. Messages -describe what failed without repeating retained props or journal payloads — -including their member *names*, which can carry a credential as readily as a -member value can. +Selection is not a permission and not durable state: it appends no journal event, a +partial replay reconstructs it while completed effects replay, and a completed +document replay reuses the retained result without expanding the body. `` +does not end the body, so later documentation runs and a later failure, +cancellation or teardown failure keeps its existing precedence. -## The workflow lifecycle +### Structural branch selection -`xmd workflow start [--id=] [--props-*=…] ` and -`xmd workflow resume ` are the two commands. Both run in the foreground, -stream the document's own output to standard output, and report identity and -outcome on standard error as two stable lines — `workflow run: ` once -the run has been created or found, and `workflow status: ` once the -execution settles. Only a completed run exits zero: failed exits 1, suspended 2, -cancelled 3 and interrupted 130, so shell automation cannot mistake an -incomplete workflow for a finished one. A request the command refuses — bad -grammar, a missing run, an incompatible reuse, damaged storage, an unsupported -host — exits 1. +`` and `` are the engine's own syntax, not components, so +core owns what they mean and no repository file, registration or bundle can +supply either name. -`start` establishes an immutable definition from **the bytes the caller named**. -Its argument is a document reference — a path, optionally followed by `#` and one -target selector — so the reference is taken apart before anything is resolved as -a path. It reads the supplied file once, derives the logical entrypoint from that -file's own NFC-normalized final segment, decodes it strictly, parses it, resolves -any selector through core against those exact bytes to the one canonical target, -reads the declared bundle from the root's own directory, and builds and verifies -version 2 of the descriptor — all before storage exists. +A switch decides its whole structure from source before it evaluates anything. +Which props were written, that every substantive direct child is a paired +``, that each case carries exactly one of a matcher or the bare `default` +word, and that at most one default is written and written last — all of it is one +shared rule that expansion and non-executing validation read. A malformed branch +therefore stops the selector too, including a branch no comparison would have +reached. -**The bytes that execute are the bytes the file held**, so a file outside a -repository starts, an untracked file starts, and a file edited since its last -commit runs what it says. Where the file sat is never identity: the containing -directory, the absolute path and the invocation working directory are retrieval -facts. Git is optional provenance, recorded as replaceable metadata when it is -cheaply available; failing to obtain it cannot fail a start, and nothing reads it -back to find the source. +Only then does selection run. The selector is evaluated once, each non-default +matcher at most once in source order, and the first `===` match wins. Operands +are arbitrary JavaScript values evaluated through the expression evaluator rather +than through prop resolution, so `undefined` stays `undefined`, `NaN` matches no +case including one written `NaN`, `0` and `-0` are one value, and two objects +match only when they are the same object. With no match and no default, the +switch renders nothing and reports nothing. -`resume` executes the closure the lifecycle authenticated from the run's own -store and never rereads the candidate path. A file that is unreadable, not -well-formed UTF-8, not Markdown, whose selector resolves to nothing, or that -declares a component this command cannot read fails explicitly and before -storage exists; none of them creates a replacement run or an empty definition. A -workflow definition is one closed set of retained bytes, so the component search -path is empty and a repository component fails to resolve rather than resolving -to content beside the definition in a mutable checkout. +The selected case is transparent. It expands inline into the region that holds +the switch, opening no binding scope, no observation boundary, no error mode and +no loop boundary: a `` it creates stays available after ``, a +`` reserves the value body that owns the switch, a `` exits the +loop that lexically encloses it, and an error inside it is observed exactly where +inline content would be. Fallthrough does not exist, because expansion enters one +child list once. -A root may close itself over a **component bundle** by declaring one in its own -frontmatter — `workflow.components`, a non-empty mapping from a component name -to a relative POSIX Markdown path beside the root. `start` normalizes that -declaration against the root's own directory, reads every member from beside the -root as exact bytes, takes each source's own domain-separated content hash as -its source hash, -parses each component before the run exists, and stores the bundle on the -definition as a `components` array in canonical order — so each declared path is -both the logical path the run retains and the file this host happened to find it -at. A root that declares nothing writes no `components` member at all, which -means "closed over no components"; an explicitly empty declaration is refused -rather than becoming a second spelling of it. A declaration may not declare structural syntax, a component the -engine supplies, or a name the host reserved, and the refusal names a component -only once the name has passed the grammar a document writes. +The selected `` contributes its own expansion frame even though the +`` consumed it, so corresponding children of two branches cannot share +one identity. Neither construct is durable state: they append no journal event, a +partial replay rebuilds the selection through ordinary expansion while completed +effects in the chosen branch replay from their own records, and a completed +document replay reuses the retained result without expanding anything. -The bundle is identity, so compatible reuse compares the whole array: a changed -name, canonical path, source hash, or component set conflicts as `definition`, -and a run closed over a bundle is never the same run as one closed over none. -`resume` obtains the bundle under the executor lock and before the lifecycle -advances. It re-derives the bundle from the run's own retained BLOBs, -recomputing every source hash and the bundle hash from the bytes it holds; -nothing is read from a repository or from the files the run was started beside. -A component that changed, went missing, or became unreachable leaves the run's -records exactly as they are. +### Concurrent spawned children -The bundle is also canonical resolution. It reaches canonical core as plain -immutable data on the `ExecutionInstallation` that already carries the run's -admission and preparation, captured by value before any installation, middleware -or document code exists. Resolution places it between reserved registrations and -ordinary registered defaults, so core's own components stay available and no -name they own can be taken back. Every selected member records one -`import_component` result of exactly `{ kind: "workflow", path, sourceHash, -content }`, and the installation contributes a journal admission that holds every -retained component import to that exact bundle before public document policy or -the root import. While a bundle is installed, `Component.importComponent` -middleware may observe, delegate, and refuse an import but cannot answer one: -canonical core keeps its own copy of the definition it produced and invokes that -copy, then compares the returned answer against it by own property descriptor — -never by serialization — so a synthetic answer, a replacement, and a mutation -each fail before the component is invoked, including one masked by a `toJSON()` -or by an accessor that answers twice. +`` and `` are the engine's own syntax, not components, so core owns +what they mean and no repository file, registration, bundle or Plugin can supply +either name. `` runs its direct `` children at the same time and +waits for all of them. It takes no props, produces no value, and requires at +least two spawns — one alone is the sequential document it was already written +as. -Every actual execution opens or creates the run's database, begins a -document-execution record, installs the exact retained WorkflowRun, installs the -service denial before the root is imported, and — for a live or partial -execution — installs the logical working directory `/`, the transaction-bound -Files provider and that database's Workspace effect coordinator. It executes -against the database's own journal with the retained props and secret detection, -then finishes the execution record and publishes the run's status. A completed -run still replays, so its retained output and result are emitted, but it -attaches no Workspace provider or coordinator and performs no filesystem -mutation. +The whole structure is decided from source before a child is constructed: the +paired forms, the absent props, the two-spawn minimum, that only blank text sits +between the spawns, that no `` is written outside its direct ``, and +that no `` or `` inside a spawn reaches for an owner outside it. +One shared rule states all of it, and expansion, non-executing validation and +the generated-fragment preflight read the same result — so a malformed construct +starts no child anywhere, including in a fragment an Agent generated. -A failure retains `failed` and uses a journal stop reason when a retained event -identifies it, and a categorical host code otherwise; no exception text is -retained beside the journal that filtered it. Graceful foreground interruption -finishes the execution `interrupted` and exits 130. `suspended` is resumable; -`failed` and `cancelled` are refused by `resume`, and `completed` replays under -either command. +**Concurrent expansion shares immutable inputs and nothing else.** Every child +gets a binding environment and live overlay snapshotted from the one that +reached the ``, an eval scope of its own, a fresh block counter, a private +output buffer, its own checked-failure ledger, a copied hide set and an +expansion path extended by its authored ordinal. Nothing merges back, so a name +one child binds or rebinds is invisible to its siblings and to the work after +``, and a resource it retains is released when it ends. The outer +`` and `` owners are cleared at the boundary; a loop or value +component created wholly inside a child establishes its own. -The initial host is Deno-local. The Deno entrypoints — source and the compiled -binary — install the local run store, beneath `~/.xmd/runs` unless -`XMD_WORKFLOW_RUNS` names another absolute directory. Node and Bun expose the -same grammar and refuse before creating or executing anything. The shared CLI -module imports no SQLite, no DOFS and no runtime detection: it asks a host -adapter to open storage and to attach a run's Workspace, and the entrypoints -decide which adapter exists. +**Durable identity comes from the source, never from the schedule.** The +existing durable structured-concurrency substrate is the implementation: in +source order, before any child starts, each `` receives one child +coroutine of the one that reached the ``, and nesting produces the existing +hierarchical identity. Effects inside a child keep their ordinary descriptions +under that coroutine, so two children may derive the same local block id without +colliding, and work after `` continues on the parent counter it would have +had without any child. Each successful child closes with the ordered `{ text, exact }` runs it +rendered. Exact presentation is a provenance recorded against the segment +objects expansion marked, and those segments do not cross the join — so the runs +are what carries it, live and on replay alike, and a retained close that does +not read as that shape fails the run instead of rebuilding part of a document. -The start/resume slice predated single-executor enforcement. Issue #367 -replaced its opportunistic orphan closure with the lifecycle ownership below, -and no supported host advances a retained run without it. +**Output is authored order; the journal is completion order.** A child emits +into its private buffer, and `` appends the renderings to its caller's +region only after every child has succeeded. Which child finished first may +decide when its records append and never what the document renders. A complete +replay reads the child closes and runs no spawned work; a partial replay +restores the children that closed and resumes only the unrecorded ones under the +same identities. Inserting, removing or reordering spawns is a definition change +and takes the existing divergence path rather than being reassigned by position +or completion time. -## Workflow lifecycle ownership +**Failure is the existing fail-fast join.** The first child failure fails the +``, cancels every unfinished sibling and does not return until all of them +have finished tearing down. No private buffer is emitted, records acknowledged +before the failure stay durable, and interrupted work is never recorded as +complete. Cancelling the parent takes the same ownership path, and no task, +subscription, retained resource or buffer survives its spawn. No scheduler, no +new record family and no document-visible concurrency control is introduced. -Storage serialization decides who uses one connection next. It does not decide -who may advance a workflow run. A workflow executor must acquire one executor -lock before stale recovery, a document-execution begin, Workspace attachment, -journal replay, root import or authored work. +## Errors, output and commands -The provider-neutral lifecycle API routes acquisition and management requests -to a host. Routing through a contextual Api selects a provider and grants no -permission. Acquisition gives the workflow executor an opaque exact -`ExecutorLock` object registered by that provider for one run and one -acquisition. The provider checks the same object, its open scope, its run and -the expected retained lifecycle state inside every mutating transaction. A run -ID, database handle, structural look-alike, same-named context value or retained -token is admitted nowhere. Releasing or tearing down the executor lock -invalidates that object before another acquisition can mutate the run. +A document decides recovery through its structure. Output routing, retained evidence and process failure remain separate decisions. -The executor lock is not a time lease. It has no duration, expiry, renewal, -heartbeat, watcher, generation record or liveness poll. The operating system -releases the local lock when the workflow executor exits. +### Two layers -The shared surface names outcomes and retained data, not a transport: +Error handling has two layers: -```ts -interface ExecutorLock { - readonly runId: string; -} +1. **Lexical structure** — the document applies context, in two forms: + context values (`` sets the `output` error mode) and middleware + (`` installs retry middleware). `` uses both: it sets + `print` for its region and installs printing middleware. +2. **Runtime execution** — execution runs under the context applied by the + enclosing structure. -type ExecutorAcquisition = - | { readonly kind: "acquired"; readonly lock: ExecutorLock } - | { readonly kind: "already-running" }; +Code inside a region doesn't see the context that governs it. A block inside +`` doesn't know it's being retried. Behavior reads from the enclosing +structure, not from the affected code. -interface WorkflowForkLineage { - readonly sourceRunId: string; - readonly checkpointEventId: string; - readonly checkpointWorkspaceRootId: string; -} +### Probabilistic behavior is visible in the structure -interface WorkflowInspectionSnapshot { - readonly record: WorkflowRunRecord; - readonly executions: readonly DocumentExecutionRecord[]; - readonly journalFrontier?: { - readonly eventId: string; - readonly workspaceRootId: string; - }; - readonly currentWorkspaceRootId: string; - readonly lineage?: WorkflowForkLineage; -} +Every source of probabilistic behavior is visible in the document's structure +(`sample`, `exec`, ``) — at the construct that applies it, not at the +code it affects. Everything else is deterministic: same inputs, same behavior. -interface WorkflowLifecycleSnapshot extends WorkflowInspectionSnapshot { - readonly retrieval?: DefinitionRetrieval; -} +Known exception, to close: eval can reach `Date`, `Math.random` and the +network directly. -interface WorkflowDeletion { - readonly removed: readonly ("run-storage" | "provider-sessions")[]; -} +### Error propagation -interface XmdArtifactFrontier { - readonly sourceRunId: string; - readonly finalEventId?: string; - readonly currentWorkspaceRootId: string; -} +An error, once decided, is one of two things: -interface WorkflowArtifactIdentity { - readonly identity: string; - readonly frontier: XmdArtifactFrontier; -} +- **printed error** — printed into the document; data +- **failure** — propagates until middleware handles it or the document + execution fails -interface WorkflowArtifactSnapshot extends WorkflowInspectionSnapshot { - readonly artifact: WorkflowArtifactIdentity; -} +#### 1. Middleware wraps the work -interface WorkflowArtifactHistory { - readonly artifact: WorkflowArtifactIdentity; - readonly entries: readonly WorkflowHistoryEntry[]; -} +Error handling, retry and suspend behavior is controlled by middleware applied +by the lexical structure. Middleware holds the operation, so it can run it +again. When the operation errors, middleware may: -interface WorkflowLifecycleApi { - acquireExecutor(runId: string): Operation>; - inspect(runId: string): Operation>; - list(): Operation>; - history(runId: string): Operation>; - inspectArtifact(path: string): Operation>; - historyArtifact(path: string): Operation>; - cancel(runId: string): Operation>; - delete(runId: string): Operation>; - export(request: WorkflowExportRequest): Operation>; -} +- retry it — including after fixing what failed (a new token, a changed input) +- suspend, wait for input, then retry +- bind it — `` converts the failure into a bound `{ok: false, error}` +- decline — the error emerges at raise + +Middleware converts an error into success and prevents the raise: the output +shows no error at all, and the attempts live in the journal. The set of +actions is closed so every one is journalable. + +```md + + + + + ``` -The two artifact operations are siblings of the retained-run pair rather than a -widening of it. A run ID names live lifecycle ownership in this host's storage; -a path names immutable evidence somebody is holding, and no run has to exist for -it to answer. +#### 1a. One element's expansion can be surrounded -`WorkflowInspectionSnapshot` is what a run says about itself, and both forms are -it. The retained snapshot alone adds `retrieval`, because where a definition -could be fetched from now is a fact about the machine holding the run rather -than about the run; the artifact snapshot alone adds which artifact answered and -the boundary it was sealed at. Neither extends the other. That is what makes an -artifact's exclusion of retrieval a property of the type rather than a habit of -one projection: there is no such member to set. The path is never part of a -result either, so two copies of one artifact answer identically. +`Component.expand` offers an observer the whole of one executable element's +expansion: before resolution and props validation, through the accepted body +and its owned cleanup, to the acceptance of what it produced. Core issues a +request and the public chain composes around it; a handler delegates that exact +request once with `next(request)`. -The executor lock's public fields describe it and never validate it. Lifecycle -transitions accept the exact object separately from their data -through an adapter-private provider operation, just as Workspace transaction -ownership is validated behind its provider-neutral selection. Inspection, -cancellation and deletion return immutable parsed values and no database, -stream, transaction or connection capability. `WorkflowDeletion` lists -categories actually removed; it contains no statement about external effects. +The authority is the shape bound execution already uses. The request is branded +with a private field, so a copy carrying the same members is not it; the request +is spent once, so a repeated delegation runs nothing; and the settlement is +readable whether or not the chain returned normally, so a handler that catches +what canonical expansion raised has not rescued it. A handler that returns +without delegating refuses the work, and what a handler returns is ignored. -Acquisition has three observable outcomes: acquired, already held by a live -workflow executor, or a storage/authorization failure. The initial local CLI -reports the active workflow executor rather than following it. Once acquired, -lifecycle changes are compare-and-set transitions against the retained state -and unfinished document-execution record read in the same transaction. -Beginning an execution, finishing its record and publishing the run state are -therefore lifecycle transitions, not independent writes another command can -observe between. -For a new `start`, the begin transition initializes pristine storage with the -immutable run, retrieval metadata, empty Workspace, first execution record and -`running` state in one transaction. For a resumable existing run, it validates -the requested lifecycle action and inserts one execution record while -publishing `running`. A replay of an already completed or failed root instead -inserts its execution record while preserving that terminal run state: the -replay observes an outcome that already won and does not make the run mutable -again. The settlement transition finishes the record while publishing its -terminal or resumable status. Begin and settlement are separate transactions -enclosing the document execution, and each is atomic within itself. +Observation reaches no outcome. Phases carry `Result` and nothing else — +what the element produced, what it bound and which error identity core is +holding stay private — and a failure is reported as a detached, frozen `Error` +preserving the selected name, message and explanatory causes. Completion is +published only after the whole dispatch, middleware cleanup included, has +unwound and canonical acceptance is reconciled. -An unfinished document-execution record found after acquisition belonged to the -previous workflow executor and is proven stale. If the run says `running`, the -new workflow executor first reconciles retained root history. A root Close -restores its canonical terminal state. Without one, execution recovery closes -the execution and publishes `interrupted` before beginning another. A -cancellation command that -acquired the executor lock instead finishes that exact stale execution and -publishes `cancelled` without beginning another. If the run already carries a -terminal state preserved during replay, stale recovery closes only that -replay's execution as `interrupted` and leaves the authoritative run state -unchanged. It never guesses staleness from a PID, timestamp, timeout or status -row. +Each subscription is registered with the element's latest phase and then told +each change in order, ending after exactly one terminal observation. A reader +that is slow, absent or cancelled delays nothing and cancels nothing: execution +never waits for an observer, and a consumer that must see the terminal phase +belongs to an owner outliving the dispatch rather than to the handler's frame. -An execution settlement has one workflow executor and one winner. Canonical -completion, failure, suspension and foreground interruption meet at the -execution-owned settlement boundary. A retained root Close proves canonical completion or -failure won before interruption or later lifecycle management. A cancellation -command never enters this competition: while another workflow executor holds -the lock it can only refuse, and after that lock is released there is no live -document scope left for another process to halt. Durability and Files -infrastructure failures retain their existing precedence. The winning execution -transition waits for -the document execution's complete child and resource teardown, then atomically -finishes its document-execution record and publishes the run state. Only after -that transaction commits may the host report the status and release the -executor lock. If settlement storage refuses, no unretained status is reported -and lock teardown leaves the run recoverable by the next proven workflow -executor. +Every path that expands something an author wrote crosses this once: the +component paths, and the structural constructs ``, ``, ``, +`` and ``, plus the selected ``, the `` that runs and +each `` an `` starts. An unselected branch expands nothing and is +told nothing. -Teardown is part of settlement evidence, not work performed after the outcome. -If teardown raises, the host does not report `suspended`: the -execution settles `failed` with a filtered journal reason when one identifies -the failure and a categorical host reason otherwise. Durability and Files -infrastructure failures still cross by identity and win by kind; if their -lifecycle transition cannot persist, no terminal status is reported. Every -finalizer is attempted before any such result is observable. +#### 1b. Where a text's elements are, without running it -Retained lifecycle state gates every later acquisition before canonical -execution. A `failed` run carrying its root Close replays that recorded failure -for compatible `start --id`. A host-settlement failure with no root Close -reports the retained `failed` state and its categorical or filtered reason -without entering replay. A `cancelled` run reports its retained state without -entering replay under either command. None can be advanced under the same run -ID. +`inspectSource(text, kind)` answers where each recognized element's delimiters +are and what it is called. It is the scanner's own reading, collected by the +same walk that decides what a fence, an inline code span, a quoted `>` and a +tag-like expression are, so there is no second grammar to disagree with the +first — and a nested collector is committed only with the parse that found it. -The executor lock remains in the trusted host's outer lifecycle scope; it is not -installed as a document provider. A completed replay may hold -the lock while recording its document-execution envelope, while the run remains -`completed` and cancellation remains refused. Canonical core returns the -retained result before preparation or root expansion. It attaches no Workspace, -Files, Service, Agent, process or external provider and performs no live durable -effect. +A document's body boundary is read lexically, matching the installed +extractor's envelope rule without calling its value parser, so inspecting a +document interprets neither its header nor its body. Definition parsing asserts +the two boundaries agree. -### Local executor lock +#### 2. Decide once -The Deno lifecycle adapter acquires a non-blocking exclusive advisory lock on a -deterministic sidecar beside the run database. The open file and its lock belong -to the workflow executor's scope. The operating system releases the lock when -that process dies, which is the stale-execution proof the next acquisition -uses. The sidecar path and empty file are host arrangement: neither takes part -in run identity, the database schema, the journal or history. The exact -in-process `ExecutorLock` registered by the adapter is the private capability -validated by every mutation; no descriptor publishes it and no second process -can address it. +An error no middleware handles is decided exactly once, where it is raised, +by the region's error mode — and the two outcomes move differently: -The lock file may remain empty after run deletion. Unlinking a locked sidecar -would let another process create and lock a different file at the same path -while the deleting workflow executor still holds its lock. An empty lock file -is not retained run state and is not reported as history or provider-session -data. +- A failure propagates by throwing up the scope tree, tearing down as it + goes, until middleware handles it or the document execution fails. +- A printed error does not propagate. It is printed into the document — data. -### Read-only lifecycle inspection +A decision is final in both directions: nothing turns a printed error back +into a failure, and execution does not continue past a stop — re-executing a +whole region starts a new attempt. Reacting is allowed: anything that can see a +printed error may raise a new failure in response, decided once at its own raise +point. -Inspection has its own provider-neutral immutable snapshot surface. It returns -run metadata, ordered history and current root identity rather than a writable -`WorkflowRunDatabase` or an executor lock. `status`, `list` and `history` use -only that surface. They never invoke canonical execution, read through replay, -attach a Workspace, materialize a root, import a document, contact an Agent, -process or external provider, or append. +#### 3. Error mode is lexical -Two sources answer the same questions. `inspect(runId)` and `history(runId)` -address this host's retained storage; `inspectArtifact(path)` and -`historyArtifact(path)` address one sealed `.xmd` file. They are siblings rather -than one operation over a union: a run id names live lifecycle ownership — -something a caller may still advance, cancel or delete — while a path names -immutable evidence that left the machine which produced it, and collapsing the -two would make that difference a detail of a parameter. The artifact operations -verify the whole container before answering with any of it, reach no run store, -lock, Workspace, definition reader or external provider, and project the same -snapshot and history values plus the artifact's identity and committed frontier. -The path never enters the answer, so two copies of one artifact answer -identically. History is shared: both sources run the same projection over -ordered rows — authored source, cumulative forkability, inherited provenance — -because two mappings are how history quietly starts meaning different things -depending on where it was read. +The nearest error mode in the scope tree governs an undecided error. A mode +is a context value, so it reads from the document's structure (see Two +layers). -The Deno provider first reads each retained database through its ordinary -read-only connection. A host crash may leave a hot rollback journal: that is a -healthy database one SQLite rollback away from the last committed state, but -SQLite cannot perform that rollback through a read-only connection. Only the -exact `SQLITE_READONLY_ROLLBACK` condition enters recovered inspection. Another -read-only failure is reported as itself rather than treated as crash recovery. +| Mode | An undecided error… | Installed by | +| --- | --- | --- | +| `print` | is printed; the document execution continues | ``; `printErrors(fn)` | +| `output` | fails the document execution; `` can print instead | every text root; every `` region | +| `throw` | fails the document execution, even inside `` | documentation; value roots | -Recovered inspection acquires inspection recovery coordination and retries the -retained database read-only. If a write-capable owner recovered it first, that -ordinary source snapshot is the answer. If it remains hot, inspection copies -the database and its rollback journal together into a private scratch directory -while it holds the coordination, then releases the retained source and lets -SQLite recover the copy. The copy keeps the candidate's exact database name and -passes through the same strict schema, retained-content, live/current and -location recognition as an ordinary snapshot. Recovery discards only the -uncommitted transaction, so the resulting recovered inspection snapshot is the -committed state the retained database describes rather than a repaired or -alternative run. +Printing is asked for. A text root installs `output` for its whole body, so an +uncaught, undecided failure is the run's own outcome whether or not the document +declares ``. `print` is the low-level default the context carries +outside root execution — where direct expansion and component-level harnesses +read it — and inside a run it is what a printing boundary establishes for its +region. -Every write-capable connection to a retained workflow database holds the same -coordination for as long as it is open. SQLite recovers a hot journal on -whichever read first needs a page, and for a connection a host already has that -read can come at any moment, from a caller that knows nothing about any of this. -A hold that ended once the connection was open would leave exactly that read -free to roll the journal back while an inspection was copying the database and -the journal, so the two would describe different states. A connection therefore -owns the pair from the moment it opens until it closes, and a cached lookup -takes nothing because the connection it answers with is already holding it. +The mode's name decides nothing on its own. `` selects which regions render, +and it selects nothing at a root that declares none; root settlement decides +what an uncaught failure means. That the two share one internal mode says they +settle a failure the same way, not that `` owns the decision. -One consequence is deliberate: inspection of a crashed run waits while this host -has a write-capable connection to it open. That connection is the pair's owner -and is the very thing that may recover it. Deleting a run closes its connection -before recognizing it, so recognition never waits for a connection only the -deletion itself would close. -The coordination is separate from the executor lock, produces no public -capability, authorizes no transition and cannot make an inspector appear to be -a workflow executor. Its deterministic empty sidecar occupies a provider-owned -non-candidate namespace and may remain after run deletion like the executor-lock -sidecar; it is host arrangement rather than retained state. +Projections carry the caller's error mode; they never set their own. -Scratch state is owner-private, belongs to the inspection scope and is processed -one candidate at a time. Every success, failure and cancellation attempts its -complete removal during teardown. A successful snapshot is observable only -after removal succeeds. A permanent removal failure instead returns a typed -inspection-recovery storage error after every cleanup attempt; it identifies the -host-selected scratch location so the operator can remove it and never repeats -retained content. Cancellation remains Effection control flow unless cleanup -itself fails. Private non-candidate scratch may therefore remain only behind an -explicit cleanup failure. Inspection never retries removal indefinitely, starts -background cleanup or reports successful inspection while such residue is -known. +#### 4. `` prints a failure -Discovery remains arithmetic over the one run-storage root; there is no list -registry. Each run contributes one database candidate. Exact advisory-lock -sidecar names occupy a distinct provider-owned namespace and are not candidates. -The Deno provider applies the same strict schema, retained-content and -live/current recognition as execution to the direct or recovered snapshot. -One absent, foreign, incompatible, damaged or unparseable candidate fails the -whole list request with its distinct condition. Healthy rows are not returned -as though they were a complete list, and every candidate remains unchanged. +The nearest `` or `printErrors(fn)` turns a propagating +failure into exactly one printed error whose `cause` is the complete original +failure, and sets `print` for its region. `printErrors(fn)` is keyed to the +exact function object — nothing is shared by name. Inside documentation a +failure still ends the document execution — a hidden region gives the author +nothing to read. -Every public history entry is one retained protocol row: +A component's declaration speaks for the component. `` is written +by the author of the region it encloses and decides everything written there; +`printErrors(fn)` is written by the author of one component and decides what +that component does, never what a caller wrote inside it. Content a caller +projects keeps the mode of the region it is written in (rule 3), and a failure +of it the component does not recover from passes outward wherever that region +does not print. -```ts -interface WorkflowHistoryEntry { - readonly eventId: string; - readonly event: DurableEvent; - readonly workspaceRootId: string; - readonly source?: Readonly; -} -``` +**Describing a failure is not owning it.** A component that could not use its +caller's content may still have something worth saying about what it therefore +did not do — a file that was not written. Saying it does not move the decision: +the region that decided the failure still settles it, and the component's +sentence travels as an account of that decision rather than in place of it. +Only a decision the region has not made is the component's to make, which is +why content that merely *printed* its errors leaves the component free to +refuse on its own terms. The distinction is carried explicitly by the failure a +component throws, never inferred from what a cause graph happens to contain: a +component that recovers and then fails on its own terms owns that failure, +however much of the content's wreckage is still reachable beneath it. -History includes Yield and Close rows, trusted preparations and root events, in -append order. The Workspace root is read from that row's existing -`workspace_root_id`. An authored durable operation carries its normalized -optional source in the stable namespaced -`EffectDescription["executablemd.source-position"]` field before the complete -event crosses the existing secret gate. History parses that field into -`source`; a Close or trusted-host event may have none. Source is non-identity -event data and never a location reconstructed from an expansion ID, current -definition or Workspace. A present field that does not parse is an unreadable -history entry, never silently treated as absent, and no value it held is quoted. +#### 5. Raise decides by value -The JSON projection is lossless: it returns every entry above with the complete -already-filtered protocol event. Human history presents each Yield as a durable -operation rather than repeating the protocol word `yield`. It renders the root -Close as the run's canonical outcome footer instead of an otherwise empty -operation row. When structured execution produces child Closes, it associates -their terminal results with those child coroutines so their ordering remains -visible. A suspended, interrupted or cancelled run with no root Close instead -states that no canonical document outcome was recorded. Presentation never -removes an event from the JSON contract or synthesizes a journal event. +An error that emerges from the middleware is raised where it stands. Raise +has exactly two outcomes, decided by the region's error mode: printed, or +failed. At raise, middleware observes only — it sees each error exactly once, +where it was raised, and changes nothing. Control at raise belongs to the +mode, never to middleware. -### Portable XMD artifacts +Nothing becomes a printed error or a failure without being raised: whoever +creates an `ErrorSegment` raises it. -An XMD artifact is a semantic export, not a renamed workflow-run database. The -exporter reads one committed retained snapshot and writes a separate sealed -container containing that snapshot, every retained Workspace root and byte it -references, and the workflow definition source closure. Host paths, lock -sidecars, rollback journals, open connections, retrieval metadata, credentials -and provider-owned session directories do not cross the boundary. The initial -container encoding is SQLite, but its tables and SQLite schema are private; the -public compatibility boundary is the versioned XMD artifact contract. +#### 6. An error is never a value -The source run stays authoritative for itself and is never moved into the -artifact. Export takes the executor lock before choosing the artifact frontier, -so a live workflow executor makes export refuse and no later committed event can -race the chosen frontier. Crash recovery, when required, operates on a private -copy under inspection recovery coordination. Export performs no lifecycle -transition and never changes the retained source. +A binding (`as=`, ``, ``) refuses a body holding +a printed error. Holding a failure as data is asked for explicitly: +`` binds the Effection `Result` shape — `{ok: true, value}` with +the value by reference, or `{ok: false, error}` — converting the failure +before any raise. Private buffers never merge into document output. -Version 1 has two profiles. One holds no Agent portability content and is read -exactly as it always was: a session that contributed a retained Prompt may carry -no classification, and nothing is reconstructed for it. The other classifies -every such session exactly once, as Agent session portability evidence — -portable, with ordered provider checkpoint tokens and an Agent session bundle, -or explicitly unavailable. Which profile a file holds is decided only after its -manifest and identity have been verified, so a descriptor is never interpreted -out of content nothing has authenticated, and partial or mixed evidence is -corruption that yields no projection. Recognition is what exists: no run -captures a bundle, the exporter emits no such evidence, intrinsic forkability -still reports `agent-state-unavailable` for an Agent turn, and status and -history render no classification, token or bundle byte. +A command's exit status is not an error until something decides it is one. +`exec as=` binds the settled process outcome — the status and both channels the +per-exec boundary received — so a nonzero status is ordinary data and there is +no error to raise, in any error mode. What never reaches a settled status is +unaffected: a process that could not start, a timeout, cancellation, a +durability failure and a Files infrastructure failure are each the failure they +already were, and bind nothing. Buffering both channels for that binding is a +document-owned buffer and not a retention decision; what a run keeps durably +stays the trusted host's. -Every command that consumes an XMD artifact opens it read-only and verifies its -complete artifact manifest before returning any partial answer. That manifest -determines the XMD artifact identity. A bytewise digest of the finished file is -reported separately for transport verification; neither digest is a signature, -so authenticity still comes from the channel that published the artifact. +#### 7. A blocker is not a failure -An artifact grants inspection and history-fork input only. It is never resumed, -answered, cancelled, deleted as a run, or admitted as the writable storage for -an existing run ID. A history fork from it creates a new run ID, copies retained -state rather than sharing pages, records the artifact identity with the source -run and checkpoint, and passes through the same compatibility replay and -forkability checks as a fork from local retained storage. The artifact remains -byte-identical before, during and after the fork. +A workflow run waiting for input — auth, a human answer, days if needed — has +not failed. Suspension is a durable effect: the journal records up to the wait, +the process may terminate, and a later document execution arrives back at the +same wait. An error that reveals a blocker (an expired login) reaches suspension +through middleware; waiting itself is never raised. -This split is the concurrency boundary between environments. Arbitrarily many -hosts may inspect the same bytes or fork independently because none of them can -advance the source identity. No SQLite lock can coordinate downloaded copies, -and the design never asks one to. +#### 8. Durability and Files infrastructure failures are outside the model -### Cancellation control +A durability failure ([language §6.11](specs/executable-mdx-spec.md#resuming-from-a-partial-journal)) +says the journal no longer describes the document +execution. No middleware sees it; it is never the document's own outcome. A +backing-journal append failure is the same kind of boundary failure: it +preserves the adapter error as its cause, resumes no consumer with an +unpersisted success, and never triggers a compensating `Close`. The first +durability failure is shared by the root and every durable child. From that +point, durable entry fails with that exact error before replay or execution, +and the ordered append boundary rechecks it immediately before storage. Work +already executing may finish. Concurrent work that began earlier cannot be +undone, but its not-yet-started append is fenced. An unmarked backing-stream +rejection is a persistence failure regardless of the error class the adapter +throws. A pre-persistence policy rejection remains the policy's ordinary +document failure; the guarded stream marks that boundary before the backing +append and does not activate the fail-stop state. -Cancellation never reaches into a live document execution. The initial local -workflow runs in the foreground, so the process that owns its Effection scope -also owns the only supported way to halt it: foreground interruption. When the -advisory lock reports a live workflow executor, -`workflow cancel ` refuses without mutation and tells the caller to -interrupt that foreground process. -Ctrl-C performs orderly scope teardown, publishes `interrupted` and leaves the -run resumable; it is not explicit cancellation. - -Without a live workflow executor, the management host acquires the executor -lock before any cancellation mutation. The exact executor lock is validated -inside the transaction that publishes `cancelled`. A stale `running` -execution is first checked for a retained root Close: a Close restores its -canonical completed or failed outcome and cancellation refuses; without one, -the transition finishes that exact -unfinished execution as `cancelled`. A suspended or interrupted run has no live -execution to halt and becomes `cancelled` directly. Success is returned only -after the lifecycle transition commits. There is no watcher, poller, control -descriptor, cancellation-request sidecar, acknowledgement protocol or -cross-process cancellation channel in this slice. - -Cancellation follows one status matrix: +A Files infrastructure failure is outside the model on the same terms. A +missing provider, a refused operation, and a provider that broke its own +contract are none of them things the document did or can act on, and printing +one would let every step after the file work run as though the file work had +happened. No middleware converts one, and no printing boundary prints one. -- `running` with a live workflow executor: refuse without mutation and direct - the caller to interrupt the foreground process; -- stale `running` with no root Close: acquire the executor lock, finish the - unfinished execution and publish `cancelled`; -- `suspended` or `interrupted`: acquire the executor lock and publish - `cancelled` without starting a document execution; -- `cancelled`: idempotent success; -- `completed` or `failed`: refuse because their terminal outcome already won; - and -- absent, foreign, incompatible or damaged storage: report that condition and - change nothing. +Both are discovered through one cycle-safe traversal of the whole cause graph, +and precedence is decided by kind rather than by position: a durability failure +first, then a Files infrastructure failure, then a documentation failure. The +selected failure comes back by identity, because a fail-stop that records "the +first error" has to record the one that happened. -Deletion also acquires the executor lock. A live workflow executor is refused; -a stale `running` record is proven stale before the exact run's database and -provider-owned retained records are removed. Only the empty lock file may -remain. Every non-live status may be deleted. The explicit run ID is the -confirmation boundary, so the command has no interactive prompt and no -wildcard. An absent run is reported rather than treated as an idempotent -deletion. Deletion does not report that it undid an external effect. +The two kinds are *recognized* by different mechanisms, and the difference is +deliberate. A durability failure is recognized exactly as it always has been — +by class, against the failure types durable-streams exports — and that is +unchanged. A **Files** infrastructure failure is recognized structurally: a +stable tag on frozen data, the fixed diagnostic for its kind, and no cause, +never `instanceof`. It has to be, because the Files boundary is the one a +separately loaded copy of a package reaches across, where `instanceof` answers +false. Recognizing one is also a decision to let that exact object travel +onward, so a candidate carrying anything else — a raw message, a cause chain, +an extra field — fails the contract and is replaced by a fresh invariant rather +than preserved. -## Durable suspension +The traversal reads values it did not create, so every read in it is total: a +thrown Proxy or a failing accessor narrows what discovery finds instead of +replacing the failure being classified. -Suspension is an authoritative document-execution outcome, separate from -completion, failure, interruption and cancellation. `@executablemd/workflow` -exposes a provider-neutral operation with this contract: +### Component invocation settlement -```ts -interface WorkflowSuspensionRequest { - readonly request: Json; - readonly responseSchema: JsonObject; -} +A component invocation owns projected content, body resources and retained eval +resources. Teardown stops those three stages in that order and attempts every +stage even when one fails. A body failure without teardown failure propagates +its original error. Teardown failure alone produces one `InvocationTeardownError` +containing every failed stage; when both fail, an `AggregateError` retains the +initiating body error first and one `InvocationTeardownError` second. -function suspendFor(request: WorkflowSuspensionRequest): Operation; -``` +Fatal classification still searches the complete cause graph in the order +[durability, Files infrastructure, documentation](#8-durability-and-files-infrastructure-failures-are-outside-the-model). +This nested component boundary is distinct from [whole-document execution +settlement](#capability-backed-execution), where an existing ordinary document +failure outranks ordinary root-scope teardown. [Plan candidate recovery](#typed-candidate-recovery-around-evaluate) +adds its own condition: cleanup must succeed before a refused request can become +another Agent turn. -The request says what is awaited and the schema says what value can resume it. -The caller supplies neither a suspension ID nor lifecycle ownership. The -trusted execution derives one suspension ID from the run and durable coroutine -position, so a component or middleware reaches the same logical wait across -replay and a different logical wait cannot receive its input. +### Attempts -The operation first publishes an ordinary durable `suspension_request` Yield. -Its description contains the suspension ID, request and response schema. Its -successful Result means only that the request publication committed; it does -not establish that the wait or answer completed. The complete event crosses the -existing secret gate and receives the current Workspace-root association in the -same way as every other nonmutating event. The run's stop reason references -that already-filtered event by ID, so request text is never duplicated into -lifecycle state. No third protocol event and no pending-request table is added. +- The journal records every attempt in full; replay restores the outcome + without re-executing. +- Rendered output shows the winning attempt plus a brief summary of failed + ones; verbose rendering expands the detail. +- On final failure, the last attempt's partial output renders, then the document + execution fails. -After live publication or replay of that Yield, the operation invokes an -execution-owned suspension controller through a stable contextual API. That -named route is what lets a component's separately loaded workflow package reach -the controller installed by the binary's bundled copy. It is composition, not -admission: middleware may refuse or suppress the route for its descendants, but -no value it returns is a suspension answer and it cannot make `suspendFor()` -continue past an unanswered wait. +### Partial output -The shipped caller is the Elicitation provider the workflow host installs, so a -document asks for a durable wait by writing the `` it would write -anywhere. Two consequences follow from where that call has to sit. A workflow -run resolves `Elicit` to a registration of the host's own, which asks the -question and writes no durable record: the wait must be the outermost durable -operation at its position, because a request published inside an enclosing -durable operation takes the journal slot that operation never settles, and the -resumed execution replays the coroutine and finds the wrong entry waiting. And -the answer is therefore retained once, by the suspension protocol, rather than -twice. Public Elicitation middleware — an `` region, or anything else -installed nearer — may answer the question or refuse it, and in both cases the -run it affected never suspended; continuation of a suspended run is retained -delivery state and nothing else. +- A failing region keeps what it rendered: everything rendered before the + failure stays in the document and reaches the output stream, not only the + journal. +- Only work the document was going to render can reach the output: a binding, + a value, or documentation that fails never adds its content. -The retained request at the execution's exact current durable position is what -authorizes entry. The controller derives the identifier for the position -immediately behind the caller's own, requires the presented one to equal it, -reads the Yield at that exact position, parses it as a suspension request and -requires it to describe what is presented. An identifier from a request earlier -or later in the journal, a reconstructed contextual API name, or a matching row -without the exact current position authorizes nothing. +### Outcomes and the journal -Durable replay identity compares a type and a name, both public, so another -durable operation can reproduce the retained request and arrive at the same -position. Once the controller has validated the exact retained request, that -operation is observationally the same request to wait: it gains nothing -that calling `suspendFor()` with that request would not have. Package-instance -identity therefore does not take part in admitting a suspension. Requiring it -would make the public operation fail when the binary and a repository component -load separate copies of `@executablemd/workflow`. +- A document execution that fails is still a complete record: replaying it + restores the output and the failure without re-executing anything. Only a + durability failure means the journal no longer describes the document + execution. +- Object identity never crosses the journal: a live document execution reports + the failure it caught; a replay reports what the record describes. The + journal is parsed, never trusted — an unreadable record is refused, not + coerced. -With no delivered input, the controller reports the request to the workflow -executor and remains pending while that executor initiates an orderly halt. -`suspendFor()` also accepts no returned value as an answer in this state, so a -middleware handler that suppresses the controller can only leave its descendant -pending, as document code can already do; it cannot publish `suspended`, supply -an answer or continue past the wait. The operation throws no ordinary error and -produces no value; the halt leaves the root without a Close. The executor -observes the validated suspension notice as its separate settlement candidate, -waits for complete scope teardown, then atomically finishes the -document-execution record and publishes `suspended` before releasing the -executor lock. +### Foreground commands -A root without Close is valid partial history. On resume, earlier durable -effects and the request-publication Yield restore without executing or -appending. Ephemeral structure is rebuilt only as the definition reaches it; -provider attachments stay lazy, and a provider is never contacted for a -replayed effect. At the same suspension ID, absent input reports the same -notice, halts again and adds no second request event. The new document-execution -record may settle `suspended`; the request remains singular. +An executable block is a foreground child process. Its output reaches the reader +as the child produces it, before it exits, and each byte is displayed once: what +was forwarded is never rendered into the document again at completion. -### Typed answer delivery +A command's output passes through several hands, and where a run observes it +decides what "the command's output" means: -Issue #300 builds typed answer delivery. Its boundary is the suspension ID and -the retained response schema. Delivery is an operation separate from execution: -`xmd workflow answer ` retains one value for that -exact wait and takes no executor lock, fetches no definition, attaches no -Workspace, inserts no document-execution record, appends no journal event and -changes no run status. A host offers a value only after the run is `suspended`, -its stop reason names a retained `suspension_request` event carrying that ID, -and the value satisfies the schema that request retained. The retained state and -the answer event it would become cross the same secret gate durable journal -persistence uses; `--no-secret-detection` disables that gate for one delivery. -Every refusal — duplicate, consumed, wrong run, wrong request, late, invalid, -cancelled-run and deleted-run — leaves the database unchanged, and neither the -rejected value nor a secret match appears in a diagnostic. +``` +child → enclosing host stdio middleware → the per-exec boundary → this +document's display policy → terminal +``` -On a resumed execution, only authenticated retained answer state lets -`suspendFor()` publish a separate ordinary durable `suspension_answer` Yield and -return its value; a contextual handler's return value never publishes an answer. -Consuming the retained answer and appending that Yield are one database -transaction, so a crash before it commits leaves the answer pending and a -transaction that does not commit publishes nothing. Replay after that -transaction commits restores the recorded answer event without reaching the live -controller and without consuming or publishing again. +**Middleware enclosing an execution is treated as preprocessing.** The process +stdio chain is a documented extension point: middleware installed around an +execution may consume, transform, redact, or redirect a channel before the run +receives it, and a host that does so is exercising its own ownership of its +own child processes. What it forwards is what exists as far as the document is +concerned. -Scheduling — automatic resume, watchers, unattended iteration and remote host -selection — is #300's and is not part of this behavior. Nothing here waits on -it: a suspended run continues through `xmd workflow answer` followed by an -explicit `xmd workflow resume`. +What a run retains is therefore what its **configured execution environment** +forwarded. `xmd run --journal` is diagnostic persistence: it records what that +environment forwarded, not what a child wrote to its pipe, which nothing here +observes and nothing here guarantees. A workflow's process results are durable +within the environment the workflow authorized. Middleware enclosing an +execution — the host's own, and anything the host composed into it — may +preprocess that output before either sees it. -This contract folds issue #322's suspension effect into #367. Issue #322 no -longer supplies a separate implementation prerequisite; its typed correlated -answer delivery remains with #300. +*Current limitation.* `Process.join()` may settle before the pumps and their +`Stdio` middleware finish, so output a child writes as the pumps settle may +never reach the boundary and may therefore be absent from what a run retains. +effectionx #244 owns that, and until its solution is integrated nothing here +guarantees pump-complete tail delivery or retention. +So transformed text is what a run captures and journals, consumed output is +absent from both, and output an enclosing handler forwards on the other channel +is recorded as that other channel. -Issue #367 changes no durable schema shape. Single-executor enforcement uses only the -provider-owned advisory-lock sidecar, suspension uses existing filtered journal -and run-state records, and history reads the existing event-to-root association. -Issue #300's retained answer state does add a durable record, and it amends the -one schema in place on the same terms as every other amendment: one -`workflow_suspension_answers` table, no second version, no migration, and strict -recognition refusing every other shape. +**The per-exec boundary is where a run observes.** One wrapper is installed +before `Process.exec` begins, so a chunk forwarded while the child is being +started is received like any other rather than raced for. The snapshot is taken +when the current `Process` operation settles, and that is as strong as the tail +guarantee gets today: `Process.join()` may settle before the stdout and stderr +pumps and their `Stdio` middleware have finished, so output written as the pumps +settle may never reach this boundary. effectionx #244 owns the stronger +guarantee. "Exactly what the command produced" means, throughout this +repository, *exactly the bytes received here* — never a guarantee about what the +child wrote to its pipe, which no execution observes, and never a guarantee of +pump-complete tail delivery. -Issue #293 amended that exact shape in place rather than introducing a second -version, and Repository and Worktree creation records joined it the same way. -This pre-release carries no persistence compatibility obligation: there is one -inventory, no migration and no upgrade path, so a database an earlier -development build produced is refused as damage rather than recognized as an -earlier shape. Strict recognition compares the amended declaration exactly. +**A run's own display policies are downstream and change nothing.** `silent`, a +`` region's suppression of terminal stdout, quiet runtime output for +a caller whose subprocess output is an answer, the command line's choice of +which stream a value root's diagnostics land on, and the terminal writers all +sit below the boundary. They decide what a reader sees; they never decide what +the run keeps. Not showing something and not knowing it are different decisions. -A Repository record holds a Workspace-local name, the admitted credential-free -locator and its fingerprint, the requested base or its absence, the commit -pinned once at creation, the initial primary branch, the object format and a -stable Workspace-relative checkout path. A Worktree record holds its -Repository's name, its own name, the requested branch, the requested base or -its absence, the creation commit and its checkout path. Both are immutable -creation identity. Current HEAD, refs, index, checkout and linked-worktree -administration live in the versioned Workspace filesystem, so one selected -Workspace root recovers the Git state belonging to that frontier without any -unversioned "current commit" column to disagree with it. The locator is stored -beside the record rather than inside it: compatibility needs the bytes, while -the journal and the document receive only the fingerprint. +#### Routing, retention and failure -## Workflow Workspace +Three questions about a command are separate, and answering one never answers +another: -The command selects the environment; the document describes the procedure. -`xmd run` uses the caller's current environment and makes no restoration -promise. `xmd workflow` creates a workflow run with one implicit root Workspace. The same declarative components use contextual -capabilities in both modes; the workflow host supplies the stronger durability -and permission boundary. - -Workspace identity belongs to the workflow run. A document path locates an -immutable definition but does not identify a previous run. A host-generated or -caller-selected run ID addresses `start`, `resume`, `status`, `history`, -`cancel`, `fork` and `delete`. Reusing a caller-selected ID is idempotent only -when every immutable creation input agrees. +- **Routing** is what a reader sees. An ordinary block forwards both channels; + a `` region takes stdout into the captured value and leaves + stderr diagnostic; `silent` displays neither. Routing answers "does this + channel reach the terminal, and does anything else want it" — never "which of + the host's streams does it land on". It is visible in document structure, + never selected by a root-level presentation declaration or by which runtime is + executing. +- **Retention** is what the run keeps. The host path that starts a run states + it; nothing infers it from which stream implementation a journal happens to + use, or from whether a pathname was named. A run that keeps no diagnostic + record keeps the exit status alone, and the transient path never accumulates + the bytes on their way past — a command that writes a gigabyte costs a + gigabyte of nothing. A run that retains keeps both received channels in + addition to whatever routing the document chose, because a display policy does + not weaken an explicit audit record. +- **Failure** is neither. A nonzero exit is a checked failure: a printing + boundary may decide how it is reported, never that the run succeeded anyway. + Later executable work does not start, and the run fails without any + `` declaration. `` governs rendered document selection only. -One workflow-run database is the physical retention boundary for logically -distinct data: +#### Authored recovery and checked failures -```text -WorkflowRun SQLite -├── filtered journal events and effect results -├── versioned filesystem roots and content -├── Repository and Worktree metadata -└── Agent-session mappings -``` +Recovery is the one exception, and it has a single origin: an +explicit error-handling construct written in the document. Nothing else confers +it: not `printErrors(fn)`, not a built-in printing boundary like ``, +not ordinary printing mode, not component failure middleware, and not a +component catching the `ContentError` its projected content raised. Those decide +how a failure of their own is reported; whether a command that exited nonzero +failed the run is not theirs to decide. A run that suffered an unauthorized +checked failure records it in an execution-owned ledger passed by value through +core's own calls, so an enclosing boundary that prints and returns cannot make +the run succeed, and no later work in any frame begins. `` and `` +are covered by that rule like any other printing boundary: their own cleanup and +their own report still happen, and neither writes, replaces, or continues. -Co-location does not turn arbitrary filesystem content into journal or training -data. The journal security policy filters a value before insertion. Training -ingestion deliberately selects retained filtered history; it never receives the -whole database merely because the database is retained. +`` *recovers*: a region that says the failures inside it are +printed and the document continues. It is the only thing that says it. A root +asks for nothing, so a root gets the failure. -Repository, filesystem, process, Git-host and Agent behavior reaches production -code through contextual APIs. A provider adapter installs host behavior; shared -modules neither detect a runtime nor fall back to the caller's host when a -workflow capability is absent. +One other construct *contains* a failure instead, which is a different thing. +An invocation of `` keeps its checked failures to itself: the failure +becomes that test's failed result, the run's own record stays clear, the tests +after it still run, and the testing completion policy is what fails the run. +Keeping them is the whole of it — a contained failure is not re-raised into the +region the element was written in, because handing it outward is exactly what +the containment exists to prevent. -## Replay and effect transactions +#### Canonical test containment -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 and re-register Agent directories. A completed root -result returns without attaching any provider. +Canonical core owns that construct. `` is one of core's registered +defaults, and containment is granted while core expands that definition and at +no other time. Nothing nominates it: not a function another package supplies, +not a component name or registration origin, not a context, marker or metadata, +not the options or the request, not an installation. There is no public API that +accepts a component and confers containment, so there is nothing for a handler +to replace, counterfeit, or hand to itself. -The Workspace stores the current frontier state. The journal stores the -historical 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. +What core does not own is testing. Activation, isolated bindings, the timeout, +how a failure is classified, what is recorded and how it is reported are +`@executablemd/testing`'s, supplied through a contextual operation core calls +from inside the invocation. That surface accepts no function, component name or +container, and cannot make another component contain a failure: it says what a +test does, while canonical core says which element is one. Its name is stable, +so a testing package loaded beside a second copy of core reaches the copy +expanding the document without either comparing the other's private function. -Structured durable operations accept an explicit provider-neutral live -coordinator. The default coordinator executes once, converts execution success -or failure to the existing durable protocol `Result`, calls the existing Yield -publication continuation once, and returns that same result only after -publication completes. The continuation publishes through the durable stream's -ordered append fence. A backing append failure activates fail-stop state and -raises `DurablePersistenceError` with the adapter error as its cause; it writes -no compensating `Close`, and catching it cannot admit later durable work. A -marked pre-persistence policy rejection remains an ordinary policy failure. -There is no generic durable validation hook: validation owned by a caller or -provider occurs before it constructs the durable effect. Replay, including the -replayed prefix of a partial run, bypasses the coordinator, execution and live -publication. Callback-based durable effects keep their existing path. +Being a default is what makes the rule hold at the edges. A repository `Test`, +or a package that registers the name, is selected ahead of core's — so core's +definition was not expanded, and the definition that was receives the ordinary +disposition, whatever it is called and whoever registered it. -The shared Workspace durable-operation wrapper reads a contextual provider -selection explicitly. That replaceable API carries only a non-operational -selection identity: execution, publication, failure activation and journal -provenance never pass through that middleware. Provider selection -creates a one-use route and a separate opaque credential. The live call keeps -that credential out of its contextual invocation request and places the exact -executor, publisher and failure activator behind an execution-owned capability -that accepts only the credential. A provider installed by any loaded package -copy terminally consumes the route and invokes that capability directly. It -does not send inspection, execution, publication, failure or completion through -the contextual continuation. Enclosing middleware at either priority can -therefore route or refuse the initial request but cannot observe or acknowledge -an operational phase. No module registry or replaceable structural value -resolves provider or invocation ownership. +Recovery travels by execution-owned structure: the element hands it to the +expansion of its own body as an argument, and expansion carries it to everything +the region causes — the branches, iterations, captures and answers written +inside it, the bodies of the components its elements invoke, and the content +those invocations project back, which the document wrote inside the region. It +reaches nothing else: a sibling after the region closes, a later invocation, or +a root all start outside it. Ambient replaceable state never decides it. Contexts resolve by name, so a separately +created context with the same name is the same binding, and anything able to +name one could otherwise turn a failed run into a successful one from outside +the document — which is precisely the decision a document's own text is +supposed to hold. -The execution-owned capability accepts execution and publication once and -records the exact `Result` only after the provider returns it from the completed -publication. The live operation ignores the contextual call's response as -completion evidence and resumes from that recorded result alone. A short -circuit or altered response therefore activates fail-stop before resume, while -an exception or substituted response after authoritative completion cannot -replace the published result. Foreign or substituted selectors and reused, -completed or stale calls fail before transaction work. Installing no provider -cannot leak execution or publication, and selecting Workspace coordination for -one operation does not enlist unrelated durable operations in the same scope. +#### Testing activation and completion -The Deno adapter establishes the canonical module's journal provenance for each -WorkflowRun journal and retains that exact witness. The generic pre-persistence -guard is policy-neutral and returns an unproven wrapper; the trusted -secret-filter and execution-owned admission wrapping sites preserve provenance -explicitly, so a filtered journal — including one wrapped more than once — still -carries the witness its source carried. Before it opens a transaction, the adapter requires both the -exact proof executor and the journal provenance from the consumed invocation to -belong to the selected WorkflowRun. An in-memory stream, another run's journal, -a copied property, an ordinary guard, a custom look-alike, or a wrapper another -loaded copy tried to prove is refused before mutation or publication. -Inside the transaction, the provider's publication operation calls the -execution-owned publisher directly and returns only after that publisher records -the exact Result. The transaction body cannot return or commit on a contextual -publication acknowledgement. +Testing is owned whole. Activating it means owning where results are collected, +when the last staged one is flushed, and what the run settles to — so the two +things that establish it are the two that own all three: a `useTesting()` +session for one root execution, and a `` element for a lexical subtree. +Registering the components is not one of them. -Every Workspace-local expansion publishes one effect through one effect -transaction: +The public `testing` boolean and the recording middleware around it remain +composable policy, and stay useful as policy: a handler may narrow testing to +false, observe a result, refuse, and wrap recording. They are not activation +activation, and neither is a stable contextual or Api name — a name is the +routing every loaded copy shares, and treating it as the authentication would let +anything able to spell it declare a complete boundary. What a `` requires +instead is a conjunction of a private one-use request, a private live boundary +credential, and the acceptance record of a terminal created for that one call: +the same request-and-terminal division *Authoritative behavior* describes, +scoped to the smaller boundary. It is scope-owned and expires with the session +or the element that established it, so a retained credential from a finished +boundary activates nothing. -```text -BEGIN - apply the filesystem, Git or metadata mutation - publish the resulting logical Workspace root - append the filtered journal result -COMMIT -``` +Canonical core enforces it, and the placement is the point. What a test *does* +arrives through public middleware a handler may legitimately answer without +delegating — that is how a second loaded copy supplies behavior — so a +requirement asked inside that chain is a requirement a handler can skip by never +letting it be asked. The decision is therefore taken by the invocation itself, +before the harness is minted and before the behavior chain is dispatched, +through a seam the testing package installs its guard on. Core supplies no +policy there: it dispatches through a private per-invocation terminal and +refuses an invocation whose decision was never taken, so blocking that chain +refuses the test rather than admitting it. With nothing installed the terminal +is reached immediately, which is what keeps core-only use of the behavior +surface working. -All three changes commit or none does. Nested effects finish their transactions -before their parent's effect begins. Declarative Git operations, including -staging, switching and committing, operate on the same transactional Workspace -rather than invoking an untracked native Git side effect. +Every refusal raised while that decision is being taken is a configuration +failure of the composition rather than a test outcome — activation unproved, a +handler on the seam refusing with an error of its own, or a chain answered +rather than delegated. No result is staged, journaled, recorded or reported, the +body causes no effect, and the ordinary component-failure path fails the +document. -Successful effect coordination finishes the mutation scope, including child -cleanup, before capturing the resulting root. The Deno provider installs that -ordering for its Workspace effect operation: the mutation -savepoint, root publication, filtered routed Yield, and caller-owned transaction -commit form one boundary. The Workspace filesystem uses the pinned synchronous DOFS -entry points for its string and byte-array surface, so cancellation leaves no -eager promise or stream pull able to reach the connection after transaction -ownership ends. The transaction-bound Files provider selects that operation for -every document filesystem read, write and search, and the workflow lifecycle -commands install that provider. +The identity cannot be the error's type, because refusing by throwing is +supported and what a handler throws is its own. It is applied by position +instead: core marks whatever escapes the decision dispatch, keeping the message +it arrived with — the refuser's own sentence is usually the actionable one — and +carrying the original as `cause`, so the search that walks causes still finds a +fatal infrastructure failure underneath. -An external provider cannot join that transaction. Prompt, Git push and pull -request effects derive a stable identity from the run and expansion, ask the -provider to perform or reconcile that identity, then append one local result -transaction. Replay adopts proven compatible completion, performs a proven -absence and refuses conflicting or permanently ambiguous state. Temporary -provider unavailability may be retried or suspended by explicit middleware; it -never authorizes a duplicate effect while completion is unknown. +Containment is what makes the distinction load-bearing rather than tidy. A +contained test failure becomes a document failure only through a session's +completion policy, and a composition that never established one has no such +policy — so a refusal absorbed as a test outcome would leave a run that ran no +test reporting success. -Git-host mutations — Git push and pull-request upsert — share one -provider-neutral reconciliation of that boundary, so a new Git-host effect kind -supplies a request and a provider rather than a replay policy of its own. Prompt -is not one of them: it keeps its own Agent-provider contract. A live Git-host -attempt observes before it mutates, adopts a proven compatible completion, -performs a proven absence exactly once, and refuses conflict and permanent -ambiguity. A Git host need not implement every kind; one that does not refuses -from observation, before any remote work. +Canonical expansion keeps a refused decision away from every failure handler +itself, rather than asking one to decline it. That is a loaded-copy consequence, +not a preference: the mark is private, so only the copy that applied it can see +it — and that copy is the one whose own `` was expanding. A package +composed against a second copy of core would answer "not a refusal" about a +wrapper it did not apply and hand it to a handler, which is exactly the +containment being prevented. So nothing outside that copy is asked, no predicate +crosses a package boundary, and the refusal travels on unchanged, after the +fatal search and before any handler is dispatched. -Reconciliation is reached through exactly one stable contextual operation, and -that is a deliberate correction rather than a simplification. An earlier design -selected a provider on one surface and coordinated the invocation on a second; -public middleware could read a credential from the first, take an invocation -capability from the second, and answer a phase itself, and the journal then -retained a completion no provider had produced. Two public surfaces that are -individually harmless composed into the power to settle a completion. +#### Output security and host retention -The single operation is request-only. Public middleware receives one frozen, -one-use routing request describing the complete detached request; it may read -it, refuse it, install narrower policy, or delegate it, and the value it returns -is ignored. It receives no credential, no capability, no answer operation and no -phase evidence, so there is nothing to combine. Each phase builds its own API -descriptor under that same stable name: sharing the name shares the middleware -chain, while owning the descriptor owns the default, and that default is the -invocation's own terminal. The selected provider's handler is installed at the -terminal end of the chain, so its continuation — a parameter of its own -generator — is the only route to that terminal. Through it the handler asks the -terminal to inspect the exact request, calls the provider, and submits the -answer. A handler that short-circuits, forges a return, substitutes or replays -the request, or reconstructs the stable name reaches the provider's handler or -the public refusing default, never the terminal. +Retained output crosses the pre-persistence secret gate exactly as any other +journaled field does, and the gate examines what the boundary received — after +enclosing middleware, not before. Two consequences follow, and both are +intended: a host may redact a credential upstream so that the material never +reaches the gate at all, and a host that introduces credential-shaped text +upstream is refused by the gate like any other run that would persist one. +Output that is never retained is never scanned, because there is nothing to +persist and nothing was accumulated for a scanner to inspect. -The durable operation is named by a fingerprint of the complete detached -request, so a changed kind, input or natural key diverges rather than consuming -the result retained at the same position, and the provider's answer is parsed at -the terminal before the durable executor returns, so a raw payload becomes a -fixed protocol failure rather than journal content. +Which host paths retain is a property of the path, stated where the run starts: +`xmd run` and `xmd test` retain when the caller asked for a diagnostic trace and +keep nothing otherwise; a workflow retains, because it owns its journal and a +resumed procedure reads its process results back rather than re-running the +commands that produced them; programmatic execution retains unless its trusted +caller selects transient execution. A shared runner never decides this from what +it was given. -Some effects need live local state the frozen request cannot describe, and that -travels as an object-source attachment rather than as an input. A push needs the -Git objects its commit is made of and the destination it may reach; both are -built by the trusted host for one reconciliation, authenticated against the -retained identity they belong to, and held in the selected provider's closure. -Nothing about an attachment reaches the durable request, the natural key or the -routing surface, so what discriminates the effect stays the filtered identity a -journal may hold while what performs it stays where no replaceable code can -address it. +A channel is the operation the boundary receives bytes on, and nothing below it +reclassifies anything: no path hands one channel's bytes to the other channel's +operation, so the received channel is never restated and never has to be +recovered from a byte payload, from contextual state, or from state held for a +run whose channels are forwarded concurrently. A value root's stdout carries its +JSON result, so the command line shows a command's stdout on the stream the +result leaves free — visible, never mistaken for the result, and still recorded +as stdout, because stdout is what reached the boundary. -What an attachment grants access to is authenticated separately from where it -came from, because local state can name further local state: a Git object -database names further ones through its alternates chain, and a symbolic link -under it redirects a read before Git reports anything about it. Such a graph is -read in the consuming tool's own grammar rather than as text: Git unquotes a -C-style quoted alternates entry before resolving it, so a containment proof that -compared spellings could be satisfied by a decoy of that literal name while the -traversal went elsewhere. The graph a -provider could traverse is therefore proven contained on the live provider's -first use of the attachment — before any external observation, and on no other -path, so a completed replay reaches no provider and performs no such proof. A -graph that leaves what the run authenticated is a boundary failure: it publishes -no outcome, repeats no authored path, and activates fail-stop. It is rejected -rather than repaired, because editing authored state to make it acceptable would -publish from state the run wrote on the author's behalf. +Forwarding, completion, cancellation and teardown belong to the executable +block's own Effection scope. Cancelling the block stops the child and the tasks +forwarding its output before the block settles. -That same contained source is what decides whether a branch may advance. A -destination naming another commit is not by itself somebody else's work: a -supervised run publishes one branch repeatedly, and the ordinary case is a -remote tip the commit being published was built on top of. So ancestry is proved -where the objects already are — inside the authenticated source, out of the -commits it already holds, by one local question whose only answers are ancestry -and divergence. A proven ancestor other than the commit being published is a -performable pre-state, published over by the same exact non-force refspec a -proven absence gets; an equal destination is adopted with nothing performed, and -is never an attested advance, because a commit is its own ancestor and observing -the destination at exactly this commit is what adoption is; and compatibility -that was not proved — a divergent commit, or one the source does not hold — is a -conflict. Nothing is -fetched to prove it, because an object retrieved to answer the question would be -evidence the question created, and a proof that reaches neither of its two -answers is infrastructure rather than a finding about the remote. The relation, -not the traversal that found it, is what the record keeps. +### Contextual run configuration -The Workspace transaction such an effect opens is read-only and is -closed before the host is observed: no transaction is held across the -Workspace and the external service, and reconciliation rather than a distributed -transaction is what makes an interrupted attempt reach the host once. +Nothing has a timeout by default. Three contextual values bound three different +things, and each has exactly one consumer: -A provider that needs a program must also not inherit configuration the run -merely stores. A checkout's `.git/config` is retained document data, and several -ordinary settings in one name a destination or a program — a push URL, a URL -rewrite, a hook, a credential helper, an SSH command, a signing program. The -Deno push provider therefore runs its transport in a disposable repository of -its own that reads the selected checkout's object database through a read-only -alternate, and aims it at the exact private retained locator rather than at a -configured remote. The two settings that would otherwise choose how a remote -operation authenticates — `credential.helper` and `core.sshCommand` — are fixed -on the command line, where they outrank every configuration file, for every -invocation that transports to a remote. +- the **run deadline** bounds one whole `xmd run`, preparation and execution + together, and only the outer run boundary reads it. Its expiry is + cancellation, so structured teardown still runs; +- the **exec default** is what an exec block resolves and hands to the Process + operation, and only exec blocks and the built-in `timeout` modifier read it; + and +- the **Fetch default** is what a Fetch resolves when its caller named none — + including ``, which resolves it when the element declares no `timeout` + and retains the effective milliseconds as part of the request it recorded. -Authentication itself is the one live host input such an invocation borrows, and -it is not the same question as inherited configuration. Retained state says -which repository an effect belongs to; it must never say who the run is, because -an identity belongs to the machine a run is standing on and a run resumed -elsewhere authenticates as whoever is there then. So one live provider -invocation — a Repository creation, a Push reconciliation, a pull-request or -Issue call — opens one authentication session lazily, for its own exact retained -locator, after the admission checks that operation requires. Every native -command that invocation runs attaches that same session, so an observation and -the mutation it decided go out under one identity rather than two, and the -session is disposed with the invocation. A later attempt on an interrupted -request opens its own. +An absent value is no timeout rather than a number the runtime chose, and no +operation falls back to a value another operation owns — an exec block never +inherits the run deadline, and a Fetch never inherits the exec default. What a +document declares locally overrides only the default it names, for the region +that declared it. A per-call value always outranks a configured one. -The trusted Deno host adapter retains one HTTP credential in memory for one live -provider invocation and exposes it only to that invocation's own Git and helper -children, through private child-process environment. A provider-owned helper is -what Git runs, and it answers one exact locator, so a redirected transport -receives nothing and fails closed — and the transport declines to follow a -redirect at all, since Git would otherwise carry a credential it already holds -to a destination this run never authorized. `store` and `approve` reach no ambient helper -or store; `erase` for the exact locator writes a fixed, nonsecret marker, which -is how a refused identity is told from an absent one. Both are authentication -unavailability, never an invalid locator. The containment is only worth what the surface around it allows: a substituted -Git host sees every invocation, and an authenticated one carries the attachment -the credential travels in. So the published Deno entrypoint offers a Workspace -attachment that accepts what a host owns and nothing that could observe or -become that host; the broad options stay inside the package. +Layers outside these three keep their own defaults where a subtree needs one: +an agent prompt inherits what its enclosing provider declared, through the +agent layer's own Api rather than through the run deadline. -Infrastructure is only what the adapter -watched fail: starting acquisition, installing the launcher, reading the marker -for any reason but its absence, and removing what it made. The absence of a -signal from the helper proves nothing — there is no readiness, preflight or -channel to it — so an acquired credential with no rejection keeps the ordinary -transport classification rather than acquiring a new one. +Verbosity is a fourth contextual value in the same place, and it bounds +nothing. It says whether the scope reading it renders verbose-only content, it +is false until something says otherwise, and the command line seeds it for the +run from the `--verbose` it already resolved. `` reads it where the +element was written, so a component that installs a nearer value changes what +its own content renders and nothing else: the ancestors around it and the +siblings after it read the value that encloses them, restored by ordinary +Effection teardown whether the content finished, failed or was cancelled. A +value that is not a boolean fails where it is read, before the body it would +have decided expands. -A session opened for one locator never authorizes another, nothing about one is -retained, and a completed replay opens none at all because it performs no remote -operation to attach one to. A missing or rejected credential is ordinary live -refusal: it proves no absence and adopts no completion, and a transport that -carries an identity attempted with no mechanism to prove one is refused under -its own word rather than as a locator that names nothing. +What the command line prints is not this value. The journal file, the event +echo on stderr, the observational output of a value root, and the testing +report's own verbosity stay with the host that owns those streams and read the +flag directly — so a document can decide what it renders and cannot decide what +the process presents. Contextual verbosity decides nothing: it takes no +part in durable identity, authorization, replay admission, retained-effect +ownership or journal identity, and no execution is admitted, substituted or +settled differently because of it. -Only an answer the terminal accepted decides the effect. A conflict, a permanent -ambiguity and a temporary unavailability so accepted publish the effect's failed -result and replay as the same fixed local failure, rebuilt from its closed name -rather than recognized by identity across loaded copies. Everything else that -can raise before an answer is accepted — routing middleware, the provider -handler, the provider's own body, an unsupported effect kind — publishes nothing -and activates fail-stop under one fixed cause-free failure that repeats none of -what was raised. The attempt decides which of the two it has by the identity of -the failure it authored itself, never by a name or a class received from -replaceable code: otherwise a handler could retire an effect as conflicted -without a Git host ever being asked. Fail-stop rather than a bare refusal, -because a live operation that appended nothing would leave the next one to -append at its position. +## Workflow state and lifecycle -Issue creation is a separate boundary, and not that one. Everything above is the -Git-host contract: a shared state machine, one request-only surface, phases, and -a private terminal that is the only thing able to author an outcome. `` -has none of it, and none of it is missing. +Retained workflow state makes an execution addressable and replayable. Storage, executor ownership and effects each enforce their own boundary. -An Issue provider does not necessarily own a Git repository — Atlassian issue -tracking owns none — so an Atlassian issue could not truthfully execute or -persist as a Git-host effect. `` therefore reaches its own stable -contextual operation, `executablemd.git.issue`, whose members are -`read(url, options)` and `upsert(issue, options)`, and journals its own durable -effect type, `issue_effect`. What is Issue-owned, and therefore an Issue -compatibility boundary, is that API name, the two normalized requests, the -idempotency key an upsert derives, the results and the effect type. +### Workflow runs -The difference is not only which name is called. A Git-host provider is selected -by the reconciliation and answers through a terminal it cannot address directly, -because the state machine is shared and the journal may hold only what that -terminal accepted. An Issue provider is ordinary middleware: it composes -`IssueApi.around(...)`, reads the request it was handed, and either handles it or -delegates it untouched. It answers that question independently for each member, -so recognizing a read is not recognizing an upsert. There is no registry, no -resolver, no host-side URL resolution, no phase protocol and no terminal. What would have been the state machine's job is -the middleware's instead, because knowing whether an issue already exists is -knowledge about one service rather than about external effects in general, and a -shared machine would have to be told it by every adapter that disagreed. +Core retains one document-execution entry point: `execute()`. A host that needs +workflow-run metadata passes an `ExecutionInstallation` to `executeInstalled()` +for the one document execution it is preparing. The run is created or +restored by that installation's preparation, which canonical core invokes +inside the durable root — so the journal is active before the workflow run +exists, and the run exists before any document policy observes it and before the +root document is imported. +A later document execution, including one that continues the same workflow +run, gets a new child scope and a new middleware installation. Workflow +metadata is durable; the middleware and every ongoing effect remain +scope-owned. -So the Issue boundary keeps little for itself: it derives an idempotency key -from the canonical target and the run's own effect identity, wraps one call -durably, validates that what came back is a URL and nothing else, and retains -it. Replay answers from the journal without calling the operation at all. What -it does not promise is that the service was touched once — observing before -mutating, adopting a compatible issue, creating once, recovering an interrupted -creation and refusing conflict and ambiguity all belong to the middleware, and -the key is what this side hands over so the middleware can make it true. +Installing the middleware alone creates no workflow run. On the first live +document execution, the first durable operation installed by the middleware +uses the host-authorized requested run ID or allocates one using cryptographic +randomness and durably records one value: -Which external system an issue is requested in is lexical context, and whether -that target is authorized is the provider's. The nearest tracker supplies a -canonical target URL and an optional provider discriminator; the adapter-private -ceiling installed beside the credential decides whether that target may be -reached at all. A tracker narrows within the ceiling and can never widen it, -which is what keeps a replaceable context from being a grant. +```ts +interface WorkflowRun { + readonly runId: string; + readonly bundleHash: string; + readonly targetPath?: string; +} +``` -Without a discriminator each provider matches its own URLs; with one, only the -provider of that name may act, which is how a self-hosted deployment is -addressed. Once middleware matches it owns the answer: it never delegates -afterwards, and nothing catches its refusal to try somebody else, because a -search is how a document that named one service quietly reaches another. A -destination every provider delegated reaches the operation's own base error. +The workflow run exists once that value is durably recorded. A document failure +or cancellation after that point does not erase it. Failure before that point +creates no workflow run and the root document does not expand. -Every committed journal event references the current logical Workspace root. -Only committed event boundaries are checkpoints. A history fork copies the -selected root and the roots the inherited prefix names into the new run, replays -the inherited prefix under a compatible modified definition and appends new -history under a new run ID. It never rewinds external branches, pushes, pull -requests or provider state. +`WorkflowRun` is one closed shape carrying `runId`, `bundleHash` and the exact +target when it has one. It has no base and no pinned commit: a run starts from +exact retained bytes rather than from a repository state, so a member naming one +would be a statement about a repository the run never had. It neither creates nor +seeds a Workspace Repository. Each `` inside the definition resolves +and pins its own optional Repository base, so one workflow may compose +repositories unrelated to the definition repository or to one another. -Reading what a Git host holds is not reconciling what it owns. A reconciliation -exists to mutate a remote at most once across interruption, and everything it -carries — the natural key, the pre-state, the observe-then-perform pair — is -there to decide whether an attempt already landed. A read decides nothing of the -kind: repeating it is safe, and a completed one restores from the journal. So a -Git-host evidence read is an ordinary durable effect, and the machinery that -makes a mutation exactly-once is deliberately absent rather than reused where it -would only add a second identity to keep in agreement. +Read-only Git questions go through the contextual Git capability, which the +workflow run itself never asks: -What such a read must not do is answer partially. A collection the host -completed and a collection nobody could finish reading are opposite facts, and -only one of them is `[]`; a truncated list would report the first while meaning -the second, so an incomplete walk fails the read and retains no array at all. -Both questions about a pull request — what it holds, and what it should say — -are asked of one surface, and a provider is ordinary middleware around it. That -is the Issue surface's topology, adopted deliberately: an adapter recognizes the -URLs it can act on and delegates the rest, and once it matches, its validation -and its refusal are final. A refusal ends the request rather than starting a -search for another provider, because a search is how a document that named one -service quietly reaches a different one. +```ts +interface GitQueryApi { + resolve(revision: string): Operation; +} +``` -The bound here is the operator's `allowed` list, checked before a session is -opened, a credential is read or anything is sent. It bounds reads alone: an -upsert names a branch this run published rather than a URL a document wrote, and -what admits it is this run's own matching Push evidence, which no configuration -grants or withdraws. "Permission ceiling" is the word for the bound; what an -operator writes is the list of places this host is allowed to reach. +`GitQuery.resolve(revision)` has the semantics of +`git rev-parse --verify --end-of-options ` in the contextual working +directory. Its default provider invokes the Git CLI; another provider may +replace it lexically. It lives in `@executablemd/git` with the rest of the +capability, and the workflow package calls none of it. Ordinary `execute()` +remains Git-independent, and so does starting, resuming, forking or exporting a +run. -A replayed effect that has to name what it asked for reads the moment it asked -from. A Push names its request out of the checkout, and a run that publishes one -branch repeatedly has moved the Workspace on by the time it is resumed — so each -completed Push is reconstructed from the root its own journal event was appended -against, which is the association the run made when it wrote that event, rather -than from the frontier the run reached. That reconstruction is a read: the -historical root is materialized, exported from, and discarded on every path out, -leaving the current root, the retained roots, the metadata and the journal -exactly as they were. It decides only which question is asked, because the -request it produces still has to be the one that position asked before the -retained record answers it. +**The workflow package imports no Git feature, and a retained run reaches no +repository either.** The CLI establishes a candidate from filesystem bytes, and +the retained lifecycle re-derives the source from the run's own store, so a run +genuinely needs none: there is no definition whose Markdown lives anywhere but +in the run that retained it, and therefore no source capability for a host to +supply. -An external effect is judged by its retained record rather than by its type. A -completed Git-host reconciliation record replays without contacting a provider, -so a fork inherits it and the remote is mutated once; the record keeps the -identity it was written under, and the effect at a position the history already -holds a record at is named by that identity, while every live attempt is named -by the run performing it. The identity is established when the engine -admits the run's retained snapshot and is published beside the run, so every -physical copy of the workflow package reads one answer. What authorizes live -execution is not that answer: a coroutine that walked away from retained history -it never consumed performs no Git-host effect for the rest of its run — the -engine's own account decides it, at every position after the divergence — so -removing or replacing the transported identities can only make the effect replay -correctly or refuse. It belongs to the exact retained event it -came from, at the position that event occupies: records are offered in order, -a call may take only the next untaken one and only when it asks exactly what -that record answers, and a request named by a retained record that reaches live -execution performs nothing rather than performing under another run. A Git-host event that settled into no such record -leaves the remote's state unestablished, and that checkpoint is not forkable. +Replay restores the recorded `WorkflowRun` without allocating another run ID or +invoking Git. The supplied bundle hash must equal the recorded one. -A fork inherits the source's normalized props as its baseline; explicit -generated arguments add to or override them, and the merged set is validated -against the candidate definition before anything is created. Those props are -what the compatibility replay runs under, what the run retains, and one of the -terms a reused fork ID is compared on. +A host that has already created the run's storage record passes +`retainedWorkflowInstallation(run)` instead, with the exact frozen value. +Nothing is left for the execution to decide: it records that value through the +same `workflow_run` durable operation, allocates no identifier and resolves no +base, and every journal state — live, truncated and completed — requires the +recorded run to agree with the supplied run ID, bundle hash and exact target. +The host input may carry additional bookkeeping: installation projects these +named fields into the closed journal value rather than making bookkeeping part +of run identity. A journal that disagrees is not this run's journal, and the +refusal names the fields rather than their values. -A fork writes two records for itself and inherits the rest. Its own -`workflow_run` record is what its journal is held to, and its own root import is -what makes the run a run of the definition the caller named — that record holds -the document's text, so inheriting the source's would run the source's document -whatever was asked for. Everything after them travels unchanged, keeping its -public event ID, its filtered result, its authored position and its Workspace -root, and carrying the source run and source event it came from. Retention moves -with the fork: once the fork commits, deleting the source leaves its prefix and -its selected root exactly as readable as they were. +`getWorkflowRun()` returns the frozen `WorkflowRun` for the current document +execution. Every call in one live execution returns the same object. It throws +outside a document execution a workflow installation was passed to, and it +exposes no journal, Git, workspace or continuation capability. Replay preserves +the field values, not JavaScript object identity. -Compatibility is decided before any storage the host would recognize as the new -run exists. The candidate replays the inherited prefix against a staged copy of -the fork — the whole run, assembled where nothing discovers it — and the replay -ends at the checkpoint, before the first live effect, rather than after it. A -divergent candidate leaves no fork. After it succeeds, one transaction commits -the run, its lineage, the inherited prefix, the copied roots, the selected -current root and the first document execution together. +The `@executablemd/workflow` package owns `WorkflowRun`, +`retainedWorkflowInstallation()`, `getWorkflowRun()` and the generic run, +storage, replay and Workspace boundaries. It owns no Git: repository +composition, the Git capability, issue and pull-request contracts and the +Git-host engine are `@executablemd/git`'s, and that package imports Workflow's +published extension boundaries rather than the other way round. It depends on +`@executablemd/core`, `@executablemd/durable-streams` and +`@executablemd/runtime`; core never imports workflow or Git. `xmd workflow +start` and `xmd workflow resume` are the CLI lifecycle, and they resume through +the run storage below. Ordinary `xmd run` carries the bundled Git Plugin, so +the repository vocabulary is available there without an operator naming it. -## Plan information requests +Git publishes on three subpaths, and which one a consumer imports from says +what kind of thing it is reaching for. `@executablemd/git/api` is the only +route to the contextual Apis — `Repository`, `RepositoryContext`, `Git`, +`GitQuery`, `PullRequestAPI`, `IssueApi`, `IssueTrackerContext` and `GitHost` +— together with their named interfaces, the identity each was minted under, +the base refusal each falls back to, the direct operations and accessors +consumers call, and the types those interfaces are written in. Those are the +seams: a consumer reaches one to call an operation and replaces one to answer +it. The root publishes the Plugin and its profile predicate, the component +registrations and definitions, the workflow installation, the durable effect +identifiers, the errors and the record parsers — what was said, rather than +who answers. `@executablemd/git/deno` publishes the host adapters that +implement the seams, and `@executablemd/git/credential-helper` is the +standalone program Git spawns as itself. An Api is published from exactly one +of these, not re-exported from the others, because a seam reachable two ways +is two contracts. -### Delivery status and standing of this amendment +**Each Api is named for the operations it answers, and every identity is under +one `executablemd.git` prefix.** A consumer writes the vocabulary of the thing +it is doing rather than of the machinery behind it: -This section records the Product Owner's #762 amendment against -`1a7b96ab6c75f29fcf82513017f6daabab4d0d82`. PR #776 delivers protected Syntax -routing and PR #786 the ordinary generated output and `as` bindings the requests -compose with. The contracts below replace the earlier Planner artifacts' -Syntax-only child execution, prohibition on file reads, output-only context, and -profile-owned evaluation limits. +| Api | Identity | Operations | +| --- | --- | --- | +| `Repository` | `executablemd.git.repository` | `select`, `worktree`, `ambient` | +| `RepositoryContext` | `executablemd.git.repository.current` | `current` | +| `Git` | `executablemd.git` | `switch`, `add`, `commit`, `push` | +| `GitQuery` | `executablemd.git.query` | `resolve`, `root`, `format`, `read` | +| `PullRequestAPI` | `executablemd.git.pull-request` | `read`, `upsert` | +| `IssueApi` | `executablemd.git.issue` | `read`, `upsert` | +| `IssueTrackerContext` | `executablemd.git.issue-tracker.current` | `current` | +| `GitHost` | `executablemd.git.host` | `route` | -Everything below is delivered on this stack. The shared ordinary read profile — -`xmd run` and every `host="run"` child stating one profile whose read table is -canonical self-closing ``, canonical self-closing `` and exact -canonical protected self-closing ``, with the write table unchanged. -Every built-in structural construct inside generated evaluation, with ordinary -semantics and whole-fragment preflight across every alternative and nested body. -The lexical response classifier, the eight-request loop the packaged `` -owns, and the narrow typed recovery around the public throwing ``. +`Git` and `GitQuery` are two seams and never one. `Git` performs the four +authored durable transitions a document writes as ``, +``, `` and ``, and what lifecycle they have is +the installed provider's. `GitQuery` asks read-only questions of the checkout +containing the contextual working directory and mutates nothing, so replacing +it answers questions rather than becoming a route to a transition. -### One authorship workflow, one ordinary profile +These names and identities are a break from `@executablemd/workflow@0.12.1`, +taken deliberately and without compatibility aliases. A provider compiled +against an old identity intercepts nothing, and an import of an old name does +not resolve: two vocabularies for one seam is the outcome an alias would +guarantee, and a silent miss is what an old identity would produce. The +package-internal names that still read `Composition` — the provider +installers, the error classes and the serialized refusal names retained +history holds — are not this layer and did not move. -Plan authorship obtains component documentation, file contents and matching file -paths through read-only generated XMD. The loop belongs to packaged ``: -`xmd plan` and `` in an ordinary `xmd run` use the same workflow. A complete -Plan remains inert source throughout classification, review and approval. -Reading information is not execution of the program being authored. +### Workflow Workspace -The command hosts supply the same ordinary evaluation profile before their -executions begin. Its always-on composition table admits core's exact ``. -Its read table admits canonical self-closing `` and ``, and exact -canonical protected self-closing ``. Its write table remains paired -File and self-closing File.Delete. The ordinary run's -write-enabled evaluations retain their existing behavior. Plan information -requests select only read: +The command selects the environment; the document describes the procedure. +`xmd run` uses the caller's current environment and makes no restoration +promise. `xmd workflow` creates a workflow run with one implicit root Workspace. The same declarative components use contextual +capabilities in both modes; the workflow host supplies the stronger durability +and permission boundary. -```mdx - -``` +Workspace identity belongs to the workflow run. A document path locates an +immutable definition but does not identify a previous run. A host-generated or +caller-selected run ID addresses `start`, `resume`, `status`, `history`, +`cancel`, `fork` and `delete`. Reusing a caller-selected ID is idempotent only +when every immutable creation input agrees. -The complete Agent response is the generated program text. A literal child of -Evaluate instead runs while producing that text and is not this request -boundary. No Syntax-specific parser replaces generated-XMD admission. +One workflow-run database is the physical retention boundary for logically +distinct data: -The host installs the profile, not Plan. A component cannot install, replace or -widen its execution's captured profile. Shared Plan consumes the enclosing -profile; it introduces neither a Plan-private profile nor a child document -execution. Another trusted host's explicit profile remains authoritative and is -never replaced by Plan. Missing required installation is a host setup failure, -not a recoverable Agent mistake. This amendment changes the ordinary command -hosts, not the workflow host's independently installed profile. +```text +WorkflowRun SQLite +├── filtered journal events and effect results +├── versioned filesystem roots and content +├── Repository and Worktree metadata +└── Agent-session mappings +``` -The constrained coding-Agent session remains separate. It receives prompt data, -not native filesystem tools, a caller checkout, additional directories or a -permission escalation channel. An admitted XMD read is the ordinary `` or -`` on the ordinary contextual route — the installed provider, the -middleware around it and the effective `Permissions` of the consuming scope — -and it uses the ordinary run's contextual working-directory rules rather than -the Agent's empty session directory. +Co-location does not turn arbitrary filesystem content into journal or training +data. The journal security policy filters a value before insertion. Training +ingestion deliberately selects retained filtered history; it never receives the +whole database merely because the database is retained. -The writer frame refuses no read. The packaged workflow is trusted source and -reading is how it decides what to propose, so `checkFilePath`, `readTextFile` -and `globFiles` are absent from its refusal table rather than denied and -re-permitted for one phase: a read before, during or after an information -projection, and one from a concurrent trusted workflow task, are the same -ordinary operation, and an enclosing read restriction still denies all of them -before target I/O. What the frame does refuse is every way of changing -something — write, delete, directory ensure and temporary acquisition — plus -Process, Fetch and Service, wherever they are asked from. What bounds the text -an *agent* supplies is the generated admission it runs under: a draft is -unexecuted text, and an information program enters public read-only -`` under the host's admitted vocabulary and complete preflight. +Repository, filesystem, process, Git-host and Agent behavior reaches production +code through contextual APIs. A provider adapter installs host behavior; shared +modules neither detect a runtime nor fall back to the caller's host when a +workflow capability is absent. -### Protection, admission, selection and documentation +### Workflow run storage -Canonical protection fixes which implementation answers for a name canonical -core owns. Five names are in that tier: Syntax, Evaluate, File, File.Delete and -Dir. Selection answers them above the repository, default and host tiers, and -the final `Component.importComponent` answer is verified against the copy -canonical execution retained — so a registration, a repository file, a bundle -member, a declared Markdown component, a middleware replacement and a -definition from a second loaded copy are each refused where they are made. +A workflow run recorded only in the journal of the document execution that +created it can be found only by whoever already holds that journal. Retained +storage is what makes a run addressable: another process opens it by public run +ID and continues from durable data. -The two groups differ only in where the implementation comes from. Syntax and -Evaluate are built per execution from an invocation authenticator that execution -minted, because each is handed a site canonical execution built for it. File, -File.Delete and -Dir are ordinary components whose bodies reach the filesystem through -contextual `API.Files`, so each is one definition built once per loaded copy of -core and answered with as it stands — which is also the definition the -generated-XMD write table pins, so an authored element and an admitted -generated one invoke one implementation. Owning those three names fixes no -provider: Files middleware still answers, transforms, delegates and refuses -every operation they perform. +Shared code reaches storage through a contextual Api that names no provider, so +the lifecycle stays host-neutral. The Deno host installs its own from a +runtime-named entrypoint, and that entrypoint is the only place SQLite, run-ID +hashing, filesystem paths and host filesystem behavior appear. No shared module +imports them or detects the active runtime. The absence of a provider is +reported rather than answered with an empty store: a run that appears to start +and retains nothing has not started. -The host profile admits that exact implementation and self-closing form to -generated XMD. The authored `allow` selects an already-installed class as a whole; it -neither selects individual entries nor grants anything absent from that class. -Plan's read selection never selects write. A generated request cannot widen it -by naming Evaluate, writing another `allow`, or requesting documentation. +A storage handle is a lease owned by the scope that opened it. Lease teardown +makes that handle unusable without closing the run's physical connection or +invalidating another handle. The Deno provider owns the authoritative +SQLite/DOFS connection for each canonical workflow-run database path and closes +it at provider-scope teardown after its child scopes finish. The public handle +contains no raw connection association; the provider owns an exact-object lease +registration and refuses foreign, fabricated, closed, or stale handles before +they can reach SQLite. -The enclosing lexical SyntaxReference carries the future-run documentation -index. Evaluate narrows availability against the union of the profile's trusted -composition table and the selected effect tables without discarding that index. -The ordinary profile's composition table contains the exact core Json -definition. It is always available inside generated XMD and carries no effect -operation. Bare generated Syntax lists Json beside the admitted read vocabulary: -Syntax, File and Glob under the ordinary profile. Named Syntax returns only the -requested documentation and marks components outside that vocabulary unavailable. -Availability of File does not admit its paired write form. Describing a -component neither imports it nor grants its effects. +#### Identity is separate from retrieval -Bindings and structural constructs are engine-owned language syntax and require -no profile entry. Every built-in construct is available inside a generated -fragment with its ordinary semantics — `Content`, `Output`, `Return`, `Let`, -`Each`, `If`, `Else`, `Switch`, `Case`, `Loop`, `Break`, `All`, `Spawn`, -`PrintErrors`, `Answers` and `Answer` — decided by the same source rules ordinary validation -and expansion read, so a construct means one thing whether a person or an Agent -wrote it. Preflight dispatches a reserved name to those rules before the -admitted table is consulted, which is why a construct is never reported as a -component the host withheld; a construct the generated root supplies no context -for, a stray branch, and a `` outside every `` fail with their -ordinary structural rule instead. +Identity is the run ID, the whole workflow definition, and the normalized props. +Values are compared canonically, so props differing only in key order are the +same props. The comparison covers the bundle hash, the entrypoint, the whole +canonical source manifest, the component mapping and the exact target. The +descriptor carries no version and no kind: one shape is the only shape, so there +is nothing to dispatch on. -Preflight walks every branch, every ``, every iteration body and every -nested region before the first effect, so a prohibited component in a path the -run never takes refuses the whole fragment with no read performed, while every -effectful component a construct reaches stays held to the selected permission. -What a value decides stays at runtime: a condition, a matcher, a computed `max` -and a failing read are evaluated where they always were. +Everything a run accumulates is excluded from that comparison: status, stop +reason, retrieval metadata, timestamps, document executions and journal +records. A completed run asked for again is found, not refused. -Binding ownership is stated per region. Component children do not escape. An -`` body is a fresh scope holding the enclosing bindings and the item, and -nothing it binds survives it, because that body may run no times at all — only -`` joins the enclosing scope. The alternatives of an `` and the -`` branches of a `` are checked from one incoming snapshot, so no -alternative supplies a binding to another; the union of what they can produce -becomes visible afterwards, which keeps reading a binding only one arm makes a -runtime failure rather than a preflight refusal. Each `` under an `` -is checked from that same incoming snapshot and contributes nothing back, which -is the binding contract the children actually run under. ``, -`` and `` bodies read and write the enclosing environment. Constructs keep their -exact spelling in the retained source, never join the retained named-component -list, and change no record version. +Creation is therefore also lookup. A request describing the stored run answers +with it; a request differing in any immutable field is a different run wearing +the same ID and is refused. That is what makes a caller-selected run ID usable +twice without a separate idempotency concept. A refusal names the fields that +differ and never the values behind them. -A trusted host may add another pure component only by placing -its exact definition and stable identity in the composition table captured -before execution. It is not inferred to be pure from its name, documentation or -absence from an effect table. Names and forms across composition and effect -tables obey the same conflict checks as effect entries, and continuation -reconciles the selected composition identities with the rest of the admitted -surface. +Where a definition can be fetched from is retrieval metadata, not identity, so +a run that moves between hosts is the same run. -Each evaluation receives a fresh narrowed protected-body route. It closes with -the evaluation, before another Agent turn. Continuation verifies current -canonical identity and retained structural identity before rebuilding a route. -Same-name replacements, copied functions, middleware substitutions, another -loaded core copy and expired routes do not inherit protection. No protected -function, live profile, provider or capability handle enters durable history. +#### Discovery has one source of truth -### Classify the response before doing anything with it +A run's location is derived from its run ID, so discovery is arithmetic and no +second registry exists that could disagree with what is stored. The storage +root and the location it produces are host arrangement rather than durable +identity, which is why the run ID is also stored inside the run and checked +against the one requested. Storage holding a different run than its location +implies is a collision or tampering: it is reported as its own condition and +left unchanged. -Initial drafting, structural repair and review revision share one non-executing -classifier. A response is a draft when its first body block, after optional -lexically closed frontmatter and whitespace, is a non-empty level-one heading. -Both ATX and Setext headings qualify. Closed but invalid YAML followed by that -heading remains a draft; structural validity belongs to CheckDraft and repair. +#### What a run retains -An unterminated frontmatter envelope, no first H1, or a body block before the H1 -makes the entire response an information candidate. Admission still decides -whether it is executable. Review-explanation responses are outside this loop. -No classifier strips fences, repairs source, executes a draft or replaces the -exact bytes delivered after approval. +A run retains its immutable identity, one of six statuses — `running`, +`suspended`, `interrupted`, `completed`, `failed`, `cancelled` — a nullable +stop reason, replaceable retrieval metadata, one document-execution record per +start and per resume, and the filtered journal. Document-execution records are +not attempts: an attempt is one execution of a retried operation or region and +remains in the journal. -### Compose reads and render findings +A stop reason is a categorical host code or a reference to an already-filtered +journal event. Arbitrary exception text is never duplicated outside the journal +that filtered it. -One information response can bind several admitted reads and explicitly render -the result it wants to return: +WorkflowRun schema version 1 is complete in place. It contains the run records, +filtered journal, pinned Cloudflare DOFS version-5 structure, immutable +Workspace-root tables, current-root state, a non-null Workspace-root +association on every journal event, and the Repository and Worktree creation +records #293 added to it. A fresh run starts with one +content-addressed root manifest describing only the root directory, empty +retained manifest and blob reference sets, and the corresponding root-only DOFS +frontier. -```mdx - - - - -``` +`workflow_run` has **no `base` column**, and two tables hold the run's own +source: content keyed by its SHA-256 with its byte length, and one manifest row +per descriptor entry mapping a logical path to that hash. Content is retained as +BLOB bytes rather than as text or re-encoded JSON, and two paths holding +identical bytes reference one blob. -Glob keeps its existing required `include`, optional `exclude` and value-returning -contract. With `as` it binds a sorted, deduplicated array of relative file paths -and renders no text. Generated evaluation supports ordinary fragment-local -bindings, structural constructs, declarative expression props over those -bindings, and pure components. It imports no caller bindings and exports none. -Executable fences, -imports and effectful components outside the selected permission remain refused. +A new run is written at `user_version = 1`, and initialization never upgrades an +existing database. Recognition reads the application ID first, then the schema +version, and holds the whole inventory to its one declaration. Version 0 under +that inventory is corruption, any other version is unsupported, and an altered +or extra object is corruption rather than a migration candidate — there is no +earlier shape for a file to be recognized as. -Core exposes Glob through the same captured Files-capability path as File, -including captured glob operations, identity, authored form and revocation. It -does not call ambient Files middleware as a fallback. No Plan-specific discovery -parser or replacement Glob component is involved. +Before a record is exposed, its retained source is re-derived in full: +the manifest equals the descriptor's `sources`, every referenced blob exists, +both retained lengths equal the content's own, recomputing each blob's hash +produces its key, no blob is unreferenced, and recomputing the bundle hash from +the retained manifest and mapping produces `bundleHash`. No reader returns a +partial bundle, and nothing falls back to the original file or the provenance. -Evaluate renders the generated fragment exactly as ordinary XMD renders it. It -does not inspect component results or synthesize an observation collection. A -component written without `as` contributes its ordinary output; a component -written with `as` binds its result inside the generated fragment and contributes -no output of its own. Bindings are ordinary language constructs, remain local to -the fragment, and may be composed through pure components. An Agent that wants -structured findings authors that structure explicitly: +A Workspace root is a complete, immutable filesystem checkpoint. Its canonical +format-1 JSON includes `/` and every reachable absolute POSIX path in UTF-8 byte +order, with kind, mode, observable mtime, file size and DOFS manifest identity, +symbolic-link target, and deterministic file-hardlink groups. The root ID is +the lowercase SHA-256 of `xmd-workspace-root\0v1\0` followed by those exact +canonical bytes. Mutable inode numbers, revisions, tombstones and cache state +do not participate in the identity. -```mdx - - - -``` +The root tables are authoritative checkpoints; the DOFS node, dirent and chunk +tables are the live materialization of the current root. Files remain solely in +DOFS content-addressed blobs. Normalized root-to-manifest and root-to-blob rows +equal the root's transitive content exactly and prevent retained content from +being deleted. Opening a run validates every root and referenced manifest and +blob, then snapshots the live frontier read-only and requires it to equal +`current_root`. The schema structure, retained content, live/current comparison +and run row are read through one explicit SQLite snapshot. This recognition +transaction is not a caller-owned Workspace transaction and enables no DOFS +savepoints. -`Json` is a pure renderer, not an effect class. `allow` restricts effectful -component permission; it does not remove language constructs, fragment-local -bindings, or pure composition. Plan captures the fragment's rendered output with -ordinary `Let` and sends that text to the next Agent turn. Empty Glob matches are -an ordinary successful `[]` binding; they reach the next turn only if the -generated fragment renders them. +Recognition also requires every file entry in every retained root, including a +historical root, to declare the size held by its referenced DOFS manifest. A +root whose XMD identity and reference rows are internally consistent but whose +file size disagrees with DOFS is corrupt and not restorable. -Generated expression props use a declarative data-expression subset rather than -the ordinary trusted-document JavaScript evaluator. The subset contains null, -string and boolean literals; finite JSON numbers, including a leading minus -directly attached to a numeric literal; fragment-local binding identifiers; -arrays and objects recursively formed from those values; and object shorthand -for a binding. Exponent overflow refuses. The leading minus belongs to the -number grammar; every other unary expression, call, constructor, operator, -assignment, spread, computed property, template and global access refuses in -whole-fragment preflight. Core parses this existing expression syntax with Acorn -and interprets the accepted nodes against the fragment's current binding -environment; it does not invoke `new Function()`. This is one reusable -untrusted-data boundary, not behavior special to Json. +Which status transitions are legal, and what a caller may do to a run in each +of them, is lifecycle policy applied above storage. -Complete-fragment preflight rejects an unauthorized effectful name or form and -an unsupported executable construct before any read begins. File writes and -deletion, network, credentials APIs, tools, processes, Agents, elicitation and -services are not in the selected read table. -A later runtime read failure can follow an earlier successful read, but the -failed request supplies no partial findings to a later turn. +#### The journal is stored, not re-encoded -### Recovery composes around Evaluate +Journal events reach storage already filtered, through the existing +pre-persistence gate. Storage adds no second filtering policy: a rejected or +cancelled gate leaves nothing behind. A record is retained in the +representation the protocol produced and parsed back through the matching +reader, so replay reads what was written rather than a re-encoding of it. +Events replay in append order. An event's opaque identity is retained +separately from its physical position; the position orders replay and is not a +public identifier. -Evaluate keeps its existing public props and its existing throwing behavior. It -gains no `limits` prop, and the evaluation profile gains no deadline and no -output budget. A profile states permission and nothing else. +#### One database at a time, and a transaction a caller can hold -Information requests add **no local deadline and no rendered-output size limit**. -Ordinary component behavior and the host's own limits are what bound the work, -and the command's existing `--timeout` continues to bound the whole planning -invocation. A request that reads a large file is an ordinary large read. -[#788](https://github.com/taras/executable.md/issues/788) owns a general -`` component as independent future work; this design deliberately adds -no one-off Plan timer and does not depend on that issue. +Operations on one run's storage are serialized, and each runs inside a +transaction. The Deno provider's per-path entry owns the one physical SQLite +connection, the one Cloudflare DOFS wrapper and Workspace filesystem, the +cooperative connection queue, and the savepoint allocator. Turns belong to that +entry rather than to a handle, so two leases for one run take turns instead of +contending — a host whose storage is reached synchronously would otherwise stop +while a second handle waited for a transaction the first one cannot resume to +finish. Different workflow-run paths have independent entries. -What Plan composes around the public component is recovery, not limiting: - -```mdx - - - -``` +Each entry has an opaque generation identity created with the physical +connection. Reopening the same path after provider teardown creates a different +generation. Each top-level transaction has its own opaque identity and exact +active record, including its path, generation, authorized lease and public +transaction handle. Finishing invalidates that record before commit or rollback +can expose the connection to later work. Retained transaction handles and +tokens stay invalid across a later transaction on the same connection and +across a later provider generation. -Plan owns one fixed recovery policy: after the child's structured teardown it -turns the one narrow, typed generated-request refusal into retry context and rethrows -every other failure unchanged. That policy is Plan's alone. It is not another -evaluator, another document root, a special Evaluate result, or a new public -XMD component, and it adds no public ``, `` or `` surface. +A caller that must publish several changes together holds the transaction +itself and receives a handle for taking part in it. Enlistment travels with +that handle rather than with the storage, so work that never received one +cannot join by accident: an unrelated append waits for its turn and commits on +its own rather than being rolled back with a failure it had nothing to do with. -One Plan invocation permits eight information requests total across drafting, -repair and revision. Successes and safe refusals each spend one. They neither -consume nor reset the ten-draft and three-repair budgets. The ninth candidate is -not evaluated and no next Agent turn starts. Review explanations do not spend -this budget. +A transaction commits only once the work inside it has finished, including work +that is still unwinding when the body returns. Cleanup belonging to that work +appends through the same transaction, and a commit that happened first would +leave those appends to publish themselves outside it. Failure and cancellation +roll back everything the transaction did. Adapter-private Workspace work also +waits for its supplied scope to finish teardown before it performs the final +live/current validation. -### Failure and settlement +A transaction opened inside another on the same storage is refused rather than +nested, as is an ordinary operation called from inside a transaction body. +The active-path context chain only detects and refuses that accidental nesting; +it never authorizes transaction or savepoint use. Authorization comes from the +provider-owned identities behind an adapter-private contextual operation, which +validates exact handle possession each time. -Public Evaluate continues to throw. One narrow exported classification lets the -surrounding composition separate a generated-request refusal from -everything else. It carries only a safe normalized reason, is recognizable -across loaded package copies through the repository's stable namespaced -descriptive tag, and confers no permission to recover. Matching message text or -treating every `GeneratedXmdError` as recoverable is invalid. +One monotonically unique allocator serves both synchronous Cloudflare DOFS +savepoints and Effection operation savepoints. The operation form receives an +`Operation`, runs it in a child scope, waits for every child and resource to +finish teardown, and only then releases. Ordinary failure rolls back to and +releases that savepoint while leaving the outer transaction usable. Synchronous +cleanup rolls an open savepoint back on cancellation or halt. A failure to +create, roll back, or release poisons the active transaction, so its top-level +owner cannot commit it. -After successful teardown, malformed candidate XMD, unauthorized names or forms, -declarative expression, binding, construct, form and prop errors, invalid Syntax -requests, invalid glob patterns, and ordinary read failures such as missing or -unreadable files become actionable refusal context. No refusal masquerades as -successful Evaluate output. Reasons contain only safe normalized diagnostics, -not raw host errors, resolved paths or secret material. +Serialization is not a single-executor policy. Which executor may advance a run +is decided above storage. -Caller cancellation, the command's `--timeout`, stale input or admission, -corrupt history, absent or broken providers, a Files provider that throws or -answers with malformed infrastructure data rather than an ordinary `Err`, -installation failure, journal failure, secret rejection and unexpected runtime -failure stop authorship without another turn. A failure during cleanup is -terminal even when the request refusal would otherwise permit a retry, so -settlement must expose that distinction before Plan normalizes a refusal. -Existing execution reconciliation, which can preserve an ordinary document error -ahead of ordinary teardown failure, is not evidence that this narrower contract -already holds. +#### Damaged storage is described, never replaced -The request owner acquires resources only after their cleanup is established, -owns every task and listener, and closes its capture and protected route before -returning success or refusal. No detached evaluation survives a later Prompt. -Cleanup of the whole Agent session still belongs to the enclosing Plan frame, -not to each information request. +Storage is parsed, never trusted, and every condition a caller can act on +differently is reported as itself: absent, conflicting, holding another run, +not this kind of storage at all, a schema version this build does not +implement, damaged, holding a record that does not parse, asked for with a +value that describes nothing, or unable to transact. Collapsing them would +leave a host guessing whether to create, refuse, or report damage. -### Durable ownership and continuation +Recognizing storage means holding its structure to the one this build writes, +not reading the names of its parts. Storage whose own header reports a version +it is not shaped like is damage rather than a version this build has not +learned. Storage is initialized only when it is pristine: something that merely +looks unused is not. -The Plan invocation owns request ordinals. The existing Agent response record -owns the exact candidate source, ordinary durable component records own the work -the fragment performed, and the authored `Let` binding owns the rendered -findings supplied to the next Prompt. Evaluate introduces no parallel -observation or result record. +Complete version 1 is the first XMD schema. A database carrying the XMD +application identity with schema version zero is a partial initialization and +therefore damage, not a supported historical version. -Evaluation and its internal admission/read records use occurrence-owned -identities in the existing execution's ordinary durable sequence. This is -journal composition, not a child document execution or profile installation. -Canonical execution supplies invocation identity and protected routes; no -caller-controlled DurableContext, stream, cursor, coroutine ID or durable owner -participates. Plan does not install a transient execution around its content. +Records are held to what they mean and not only to the types they are stored +in. A retained timestamp is an instant, a retained identity is not empty, and +normalized props are an object. -Each admission or effect record is appended before its operation resumes and -becomes ordinary replay history immediately. Evaluate does not stage, hide, -publish or discard another component's records and changes no persistence -contract. Two Evaluate occurrences cannot consume one another's retained work. +Nothing initializes, migrates, truncates, deletes or replaces incompatible or +damaged storage, and a lookup that finds nothing creates nothing. Messages +describe what failed without repeating retained props or journal payloads — +including their member *names*, which can carry a credential as readily as a +member value can. -A completed request replays without another Agent turn, fragment body, Syntax -lookup, File read or Glob traversal. Re-entry needed to validate structural -identity is not permission to repeat those effects. Interrupted work resumes -through ordinary replay: completed effects restore and the first unrecorded -effect runs. The generated projection and its structured teardown complete -before Evaluate returns and before later parent work begins. No incomplete -rendered findings are presented to the next Agent turn. +### The workflow lifecycle -Replay admission verifies both successes and refusals before any completed -invocation or root is reused. The fingerprint binds source, selected classes, -ordered admitted identities and forms, lexical documentation-reference identity, -and filesystem scope/policy identity. There are no bound values to compare. -It uses stable structural identity, never function serialization or replaceable -context state. Extra effect-description fields alone do not enforce comparison. -Changing any bound fact is stale before another turn or read. Absent, malformed -or inconsistent records fail closed, including a retained refusal for different -source. Authoritative checks cannot be skipped by public replay middleware. +`xmd workflow start [--id=] [--props-*=…] ` and +`xmd workflow resume ` are the two commands. Both run in the foreground, +stream the document's own output to standard output, and report identity and +outcome on standard error as two stable lines — `workflow run: ` once +the run has been created or found, and `workflow status: ` once the +execution settles. Only a completed run exits zero: failed exits 1, suspended 2, +cancelled 3 and interrupted 130, so shell automation cannot mistake an +incomplete workflow for a finished one. A request the command refuses — bad +grammar, a missing run, an incompatible reuse, damaged storage, an unsupported +host — exits 1. -Unchanged completed findings remain historical information. Replay does not -refresh documentation, file contents or directory listings to compare them with -today's world. A different admitted provider/reference identity is stale; a -historical answer is not silently recomputed. The command's `--journal` remains -write-only JSONL and starts fresh; continuation belongs to enclosing durable -uses of the shared Plan component. +`start` establishes an immutable definition from **the bytes the caller named**. +Its argument is a document reference — a path, optionally followed by `#` and one +target selector — so the reference is taken apart before anything is resolved as +a path. It reads the supplied file once, derives the logical entrypoint from that +file's own NFC-normalized final segment, decodes it strictly, parses it, resolves +any selector through core against those exact bytes to the one canonical target, +reads the declared bundle from the root's own directory, and builds and verifies +the source-bundle descriptor — all before storage exists. -### Product interfaces +**The bytes that execute are the bytes the file held**, so a file outside a +repository starts, an untracked file starts, and a file edited since its last +commit runs what it says. Where the file sat is never identity: the containing +directory, the absolute path and the invocation working directory are retrieval +facts. Git is optional provenance, recorded as replaceable metadata when it is +cheaply available; failing to obtain it cannot fail a start, and nothing reads it +back to find the source. -The interfaces below follow the Product Owner's shared-profile and Glob -decisions and the product-interface rules: name available actions, distinguish -source from execution, and make refusals actionable. Recovery remains -surrounding composition, not Evaluate syntax, and there is no local limit for a -refusal to report. These sentences belong in packaged Markdown and command help, -not hidden host prompts. +`resume` executes the closure the lifecycle authenticated from the run's own +store and never rereads the candidate path. A file that is unreadable, not +well-formed UTF-8, not Markdown, whose selector resolves to nothing, or that +declares a component this command cannot read fails explicitly and before +storage exists; none of them creates a replacement run or an empty definition. A +workflow definition is one closed set of retained bytes, so the component search +path is empty and a repository component fails to resolve rather than resolving +to content beside the definition in a mutable checkout. -**Agent instructions**, included at initial, repair and revision sites: +A root may close itself over a **component bundle** by declaring one in its own +frontmatter — `workflow.components`, a non-empty mapping from a component name +to a relative POSIX Markdown path beside the root. `start` normalizes that +declaration against the root's own directory, reads every member from beside the +root as exact bytes, takes each source's own domain-separated content hash as +its source hash, +parses each component before the run exists, and stores the bundle on the +definition as a `components` array in canonical order — so each declared path is +both the logical path the run retains and the file this host happened to find it +at. A root that declares nothing writes no `components` member at all, which +means "closed over no components"; an explicitly empty declaration is refused +rather than becoming a second spelling of it. A declaration may not declare structural syntax, a component the +engine supplies, or a name the host reserved, and the refusal names a component +only once the name has passed the grammar a document writes. -> Return a complete Plan, beginning with a descriptive level-one heading. If you -> need more information first, return an XMD information request instead, without -> a level-one heading. -> -> Information requests may read files, find files using glob patterns, and -> request component documentation. You may combine these in one response. -> Capture Glob's returned paths with `as`, for example -> ``. -> -> XMD evaluates the complete request with the host's admitted read capabilities -> and gives its rendered output to your next turn. Use ordinary `as` bindings to -> combine values, and render structured findings with ``. Requesting -> documentation does not make the described component executable. A complete -> Plan remains source and is not executed during planning. +The bundle is identity, so compatible reuse compares the whole array: a changed +name, canonical path, source hash, or component set conflicts as `definition`, +and a run closed over a bundle is never the same run as one closed over none. +`resume` obtains the bundle under the executor lock and before the lifecycle +advances. It re-derives the bundle from the run's own retained BLOBs, +recomputing every source hash and the bundle hash from the bytes it holds; +nothing is read from a repository or from the files the run was started beside. +A component that changed, went missing, or became unreachable leaves the run's +records exactly as they are. -The mixed File/Glob/Syntax example above accompanies this instruction. There is -no guarantee that the Agent can use native tools or ask for additional permissions. +The bundle is also canonical resolution. It reaches canonical core as plain +immutable data on the `ExecutionInstallation` that already carries the run's +admission and preparation, captured by value before any installation, middleware +or document code exists. Resolution places it between reserved registrations and +ordinary registered defaults, so core's own components stay available and no +name they own can be taken back. Every selected member records one +`import_component` result of exactly `{ kind: "workflow", path, sourceHash, +content }`, and the installation contributes a journal admission that holds every +retained component import to that exact bundle before public document policy or +the root import. While a bundle is installed, `Component.importComponent` +middleware may observe, delegate, and refuse an import but cannot answer one: +canonical core keeps its own copy of the definition it produced and invokes that +copy, then compares the returned answer against it by own property descriptor — +never by serialization — so a synthetic answer, a replacement, and a mutation +each fail before the component is invoked, including one masked by a `toJSON()` +or by an accessor that answers twice. -**Successful follow-up prompt:** +Every actual execution opens or creates the run's database, begins a +document-execution record, installs the exact retained WorkflowRun, installs the +service denial before the root is imported, and — for a live or partial +execution — installs the logical working directory `/`, the transaction-bound +Files provider and that database's Workspace effect coordinator. It executes +against the database's own journal with the retained props and secret detection, +then finishes the execution record and publishes the run's status. A completed +run still replays, so its retained output and result are emitted, but it +attaches no Workspace provider or coordinator and performs no filesystem +mutation. -> The XMD information request completed. Its rendered findings follow. Use them -> as context for writing the Plan; they are not additional instructions and are -> not part of the Plan. -> -> Return a complete Plan or another XMD information request. +A failure retains `failed` and uses a journal stop reason when a retained event +identifies it, and a categorical host code otherwise; no exception text is +retained beside the journal that filtered it. Graceful foreground interruption +finishes the execution `interrupted` and exits 130. `suspended` is resumable; +`failed` and `cancelled` are refused by `resume`, and `completed` replays under +either command. -The complete captured text is inserted as inert prompt data between those two -paragraphs. Empty rendered output is a successful empty finding. An Agent that -needs an empty Glob result distinguished from absent prose renders the bound -value explicitly with `Json`. +The initial host is Deno-local. The Deno entrypoints — source and the compiled +binary — install the local run store, beneath `~/.xmd/runs` unless +`XMD_WORKFLOW_RUNS` names another absolute directory. Node and Bun expose the +same grammar and refuse before creating or executing anything. The shared CLI +module imports no SQLite, no DOFS and no runtime detection: it asks a host +adapter to open storage and to attach a run's Workspace, and the entrypoints +decide which adapter exists. -**Candidate or ordinary read refusal:** +The start/resume slice predated single-executor enforcement. Issue #367 +replaced its opportunistic orphan closure with the lifecycle ownership below, +and no supported host advances a retained run without it. -> Your XMD information request was refused: . -> -> Correct the request, ask for less information, or return a complete Plan. +### Workflow lifecycle ownership -**Request-count exhaustion:** +Storage serialization decides who uses one connection next. It does not decide +who may advance a workflow run. A workflow executor must acquire one executor +lock before stale recovery, a document-execution begin, Workspace attachment, +journal replay, root import or authored work. -> Plan authorship reached the limit of 8 XMD information requests before a Plan -> was approved. No Plan was returned. Start again with a more specific prompt. +The provider-neutral lifecycle API routes acquisition and management requests +to a host. Routing through a contextual Api selects a provider and grants no +permission. Acquisition gives the workflow executor an opaque exact +`ExecutorLock` object registered by that provider for one run and one +acquisition. The provider checks the same object, its open scope, its run and +the expected retained lifecycle state inside every mutating transaction. A run +ID, database handle, structural look-alike, same-named context value or retained +token is admitted nowhere. Releasing or tearing down the executor lock +invalidates that object before another acquisition can mutate the run. -The command adds `Nothing was output.`; the embedded component adds no sentence -about command output. Neither surface starts a ninth evaluation or another turn. +The executor lock is not a time lease. It has no duration, expiry, renewal, +heartbeat, watcher, generation record or liveness poll. The operating system +releases the local lock when the workflow executor exits. -**Stale continuation:** +The shared surface names outcomes and retained data, not a transport: -> Plan authorship cannot resume because retained XMD information does not match -> the current request or evaluation policy. Start a new Plan authorship instead -> of continuing this one. +```ts +interface ExecutorLock { + readonly runId: string; +} -**Default command progress:** +type ExecutorAcquisition = + | { readonly kind: "acquired"; readonly lock: ExecutorLock } + | { readonly kind: "already-running" }; -```text -## Inspecting XMD information -Information request 2 of 8. +interface WorkflowForkLineage { + readonly sourceRunId: string; + readonly checkpointEventId: string; + readonly checkpointWorkspaceRootId: string; +} -## Continuing the Plan -``` +interface WorkflowInspectionSnapshot { + readonly record: WorkflowRunRecord; + readonly executions: readonly DocumentExecutionRecord[]; + readonly journalFrontier?: { + readonly eventId: string; + readonly workspaceRootId: string; + }; + readonly currentWorkspaceRootId: string; + readonly lineage?: WorkflowForkLineage; +} -The ordinal is the invocation-wide count. Default progress reveals no request, -findings or refusal details. Embedded Plan emits no command progress into its -approved-source output. After the request settles, verbose command output shows -the complete request under `## XMD information request` and either the complete -rendered findings under `## XMD information returned` or the safe refusal under -`## XMD information request refused`. Source and findings are rendered as data, -never interpolated into executable Markdown. - -**Help and journal disclosure:** - -> `--verbose`: Show drafts, validation diagnostics, XMD information requests, and -> their results or refusals on stderr. - -> `--journal`: Write a new diagnostic JSONL journal. It may contain prompts, -> drafts, file contents, matching file paths, component documentation, and -> refusals. It cannot be used to resume `xmd plan`. - -Every durable event crosses the existing serialized pre-append secret gate -before publication. Captured information text has one additional, non-durable -pre-disclosure check. After Evaluate and its structured teardown settle, but -before PlanInformation returns, the trusted component asks the current -execution's authenticated secret policy to scan the complete findings or safe -refusal. Canonical execution owns that policy and scanner; PlanInformation -merely consumes them and appends no event. Only text that clears this check may -reach verbose output or a following Prompt. A finding, scanner or policy -failure, cancellation, or cleanup failure is terminal and starts no later phase -or Agent turn. When a trusted host explicitly disabled secret detection, this -check follows that same captured policy rather than inventing an always-on Plan -policy. - -These checks do not make file reading a credentials sandbox: sensitive files -remain subject to the same host read policy. A refusal discloses no partial -findings; already published effect history remains governed by the ordinary -journal contract. - -### Product verification - -This finite matrix carries the requested product journeys through the shared -profile and Glob amendments. The Planner derives focused tests from it rather -than choosing new product behavior. Each row identifies an implementation that -could appear to work while missing the contract. - -| ID | What the person does and observes | Incomplete implementation rejected | -| --- | --- | --- | -| PI1 | Ask for Elicit and File documentation; only those details reach the next turn, which produces a normal reviewable Plan. | Eagerly injecting every component's full documentation or never starting the follow-up turn. | -| PI2 | Submit one Markdown response that binds Glob, File and Syntax, then renders them through Json; receive exactly that structure through the same Evaluate path. Empty glob matches remain an empty array. | A Syntax-specific parser, a direct filesystem shortcut, an implicit observation collection, or bindings that cannot feed pure composition. | -| PI3 | Return ATX and Setext H1 drafts, including closed invalid YAML and later structural errors; repair them without executing any draft. Preserve approved bytes exactly. | Classifying by full validity, finding an H1 anywhere, or stripping fences before evaluation. | -| PI4 | Read a fixture through both Plan surfaces using the shared read profile; try a fragment containing that read followed by a write, process, network or Agent request and observe refusal before any read or prohibited action. | A lingering Syntax-only child or admission that starts the first read before inspecting the whole fragment. | -| PI5 | Request documentation for Elicit and File, then attempt Elicit or paired File in an information request. Both remain unavailable. Bare Syntax reports the read vocabulary. | Treating documentation lookup or name availability as permission for every component form. | -| PI6 | Observe success and refusal with deliberately delayed cleanup; on every ending the projection, every acquired read and the protected route finish before findings are published and before the next turn begins. A cleanup failure stops the conversation. | Detached work, early publication, or a recoverable candidate error hiding teardown failure. | -| PI7 | Interleave information requests with initial drafts, repairs and review revisions. Eight requests share one budget; the ninth is not evaluated. Draft and repair limits remain independent. | Resetting the information counter per phase, counting a read as a draft, or resetting repairs after a read. | -| PI8 | Resume after a completed mixed request and Agent turn with all live readers and Agent calls set to fail if reached. Historical effects restore and reproduce the same rendered findings. Changed source, selected permission, lexical reference, filesystem scope or capture format refuses before reuse; a partial continuation resumes at the first unrecorded effect. | Refreshing a Glob/File read, skipping identity validation on retained work, or matching only a journal operation name. | -| PI9 | Run one scripted embedded mixed-request-to-approved-Plan journey through the source CLI, emitted npm bin and compiled binary. Run the command journey through the in-process production command assembly executing the exact packaged command document. At the npm and compiled boundaries, prove the command and Plan assets are packaged, the Plan origin and digest match the source bytes, its exact text contract and private closure remain intact, and canonical protected Syntax and Evaluate are present exactly once. | A source-only journey, a reconstructed test-only command host, asset hashes without an executable packaged journey, or a distribution that loses or substitutes the command document, Plan component or protected tier. | -| PI10 | Refuse a malformed request and an ordinary missing-file read: core classifies each as a refused generated request and `` yields one safe retry context after cleanup. Then break the profile installation and the protected route, corrupt retained history, make the Files provider throw rather than answer `Err`, reject a secret, and cancel the enclosing command: each stops authorship. Public Evaluate keeps throwing under its own ordinary use. | Recovering every `GeneratedXmdError`, matching message or serialized error name, placing the retry decision in core, or changing public Evaluate's semantics to report a refusal. | -| PI11 | Compose admitted reads with branching, binding and bounded iteration — every built-in structural construct under ordinary language rules — and render selected values through Json. A prohibited component in an **untaken** branch refuses the whole fragment with zero reads, and a construct the generated root cannot supply context for fails with its ordinary structural rule rather than as an unauthorized component. | A structural allowlist, preflight that walks only the selected branch, or reporting a placement error as a missing permission. | -| PI12 | Observe default, verbose, journal and Agent prompts for success and refusal, then place a synthetic secret in a file read by an information request. Default output stays content-free; detailed findings follow settlement; the secret appears in no output, journal entry or Agent Prompt, starts no following turn, review or artifact, and ends with the existing terminal secret rejection. | Relying only on the Prompt event's later append gate, printing findings before the pre-disclosure check, inventing an unscanned side channel, or turning secret rejection into another Agent turn. | - -### Delivery order and deferred access policy - -After #776 and #786, delivery is three independently reviewed PRs, all of them -on this stack: - -1. **Shared read profile.** Canonical self-closing Glob and canonical protected - Syntax join self-closing File in the ordinary `read` table, through their - captured capabilities and the existing component-answer route, preserving - ordinary value, binding, persistence and replay behavior. The shared write - table is unchanged. -2. **Generated structural constructs.** Every built-in structural construct - becomes available inside generated evaluation with its ordinary semantics, - declarative expressions and fragment-local bindings. Preflight validates - every branch and nested body before any effect, and every effectful component - reached through a construct stays limited by the selected permission. -3. **The Plan loop.** Both command hosts install the shared profile, and Plan - gains the classifier, prompts, narrow typed recovery, counters, records, - progress, disclosure, journal behavior and the source/npm/compiled journeys. - -No temporary Plan-private evaluator bridges them. - -[#787](https://github.com/taras/executable.md/issues/787) owns question-based -selection and request-aware rewriting of relevant Syntax documentation; explicit -`names` lookup here does not wait for it. -[#788](https://github.com/taras/executable.md/issues/788) owns the general -`` component, which is why this design adds no Plan-local deadline. -[#777](https://github.com/taras/executable.md/issues/777) owns the later workspace -selection and user-approval escalation policy. Existing host containment remains -in force with its stable-namespace limitation; #227 owns concurrent -path-replacement hardening. Deferring those decisions does not defer File or -Glob reads. #679 remains separate evaluation work after product delivery. - -## Agent permissions and generated XMD +interface WorkflowLifecycleSnapshot extends WorkflowInspectionSnapshot { + readonly retrieval?: DefinitionRetrieval; +} -A workflow Agent receives no Workspace, checkout, materialized root, host path -or additional directory. Its process starts in a fresh empty directory the host -owns, configured with no MCP servers. That directory is provider-owned, never -enters logical or durable identity, and is removed with the attachment. The host -requests an empty native tool allowlist and installs a permission path that -denies every native request and fails the turn that asked, without reaching the -public permission chain. This is the V1 permission ceiling; the portable proof -that every provider exposes no ambient tool is tracked separately and does not -widen it. `xmd run` keeps its caller-selected Agent permission behavior. +interface WorkflowDeletion { + readonly removed: readonly ("run-storage" | "provider-sessions")[]; +} -What a turn was allowed to do is retained beside what it said, and the two are -recorded by different things. A permission policy decides every request inside -its scope without deferring outward, so nothing installed inside one — and -nothing installed inside the turn, below a policy a host put around the execution -— can see the decision it makes. The Agent middleware installed with the -Agent itself, outside every policy installed after it, owns both sides of this. -Each `Agent.prompt()` call is given a private ledger and the cold stream it -returns is wrapped, so every event that call produces is read with that ledger -in place and a request raised while it is read inherits that ledger and no -other. A component installs nothing to be audited. The observer reserves a -place, delegates once, and copies the outcome that comes back into that place, so -concurrent turns keep their own audits and two overlapping requests keep the -order they were asked in. A place nobody completed — a policy that raised, a turn -torn down while a request waited — is published as nothing rather than as a -denial somebody invented. The ledger names a destination and confers no authority -to answer anything. +interface XmdArtifactFrontier { + readonly sourceRunId: string; + readonly finalEventId?: string; + readonly currentWorkspaceRootId: string; +} -The only thing a workflow Agent receives is the rendered content of an authored -``. It proposes an information request, or a change, by returning XMD. The -trusted workflow host passes that exact response to the constrained -generated-XMD evaluator, which performs admitted components as ordinary durable -effects against the authoritative Workspace. +interface WorkflowArtifactIdentity { + readonly identity: string; + readonly frontier: XmdArtifactFrontier; +} -Evaluate produces exactly the fragment's ordinary rendered output. It neither -observes component return values nor invents a second result channel. A -generated fragment captures values with ordinary `as` bindings and renders any -structure it wants through ordinary pure components such as `Json`. A bound -`Fetch` response therefore reaches the next Agent turn only when the fragment -chooses to render it. An admitted mutation likewise contributes only what its -authored fragment renders; its own durable effect record remains the account of -what it did. +interface WorkflowArtifactSnapshot extends WorkflowInspectionSnapshot { + readonly artifact: WorkflowArtifactIdentity; +} -An admitted effect names its durable operation after the invocation the engine -handed it, through the invocation authenticator this execution delivered to the protected -`` body it built (*Capability-backed invocation identity*). Nothing -else carries the capability: an Effection Context is addressed by name, a contextual -Api handler installed outside an invocation answers ahead of the engine's own, -and a definition or registry answer is a value a handler may keep from one -attachment and hand back inside another. Two effect sites sharing one -durable name would each replay the other's admitted fragment, and an -implementation running under another attachment's identity would commit against -its own run's storage under that attachment's expansion. +interface WorkflowArtifactHistory { + readonly artifact: WorkflowArtifactIdentity; + readonly entries: readonly WorkflowHistoryEntry[]; +} -An admitted element runs canonical core's own ordinary component, and the -operation it performs is the ordinary one. For a while it did not: a profile -stated a *capability* whose body core closed over operations the host handed it, -so a generated read reached a filesystem nothing in the document's scope could -observe, transform or refuse — and a generated `` scoped its content -through a cursor the evaluator owned rather than through the contextual -environment. Both are gone. A fragment's operations now cross contextual -`API.Files` and `API.Fetch` like every other caller's, which means the middleware -an authored read crosses, the provider the host installed, and the effective -`Permissions` of the consuming scope all apply to them. +interface WorkflowLifecycleApi { + acquireExecutor(runId: string): Operation>; + inspect(runId: string): Operation>; + list(): Operation>; + history(runId: string): Operation>; + inspectArtifact(path: string): Operation>; + historyArtifact(path: string): Operation>; + cancel(runId: string): Operation>; + delete(runId: string): Operation>; + export(request: WorkflowExportRequest): Operation>; +} +``` -What the profile states is admission: which names, which spelling of each, which -requests an admitted `` may perform, and the execution's lifetime — an -admitted implementation refuses once the execution that captured it has ended. -A host hands the profile no operation at all, because there is no second -filesystem for it to hand. What a fragment may *do* is then the ordinary -question asked of any caller: the provider that is installed, and the policy of -the scope the element runs in. +The two artifact operations are siblings of the retained-run pair rather than a +widening of it. A run ID names live lifecycle ownership in this host's storage; +a path names immutable evidence somebody is holding, and no run has to exist for +it to answer. -The narrowing these revisions record is the one that still stands — an entry -authorizes one component under one spelling — so core's entries state revision 2 -and the bundled Git Plugin's `` states revision 3. **A run suspended under -the earlier revisions refuses to resume rather than silently receiving the -narrower grant**, which is what a revision is for: a continuation resumes only -under the exact identities it was admitted with. +`WorkflowInspectionSnapshot` is what a run says about itself, and both forms are +it. The retained snapshot alone adds `retrieval`, because where a definition +could be fetched from now is a fact about the machine holding the run rather +than about the run; the artifact snapshot alone adds which artifact answered and +the boundary it was sealed at. Neither extends the other. That is what makes an +artifact's exclusion of retrieval a property of the type rather than a habit of +one projection: there is no such member to set. The path is never part of a +result either, so two copies of one artifact answer identically. -`` is one Agent turn. The host hides no Agent loop inside it. Ordinary -authored Markdown owns iteration with ``, branches on whether a reply -is another information request or a final proposal, and makes exhaustion an -explicit document failure. `` is canonical core's own protected -component, reserved ahead of every host and author tier, so the name means the -same thing in every execution and no registration, repository file, bundle -member, declared Markdown component, import handler or second loaded copy -answers for it. A host does not supply the component; it supplies the -*ceiling*, as one `ExecutionInstallation.evaluation` profile captured by value -before any installation runs. An execution accepts one and refuses two, and one -that was offered none has no evaluation at all — which `` says when it -is written rather than at startup. +The executor lock's public fields describe it and never validate it. Lifecycle +transitions accept the exact object separately from their data +through an adapter-private provider operation, just as Workspace transaction +ownership is validated behind its provider-neutral selection. Inspection, +cancellation and deletion return immutable parsed values and no database, +stream, transaction or connection capability. `WorkflowDeletion` lists +categories actually removed; it contains no statement about external effects. -The document writes the program as `text`, or renders it as paired content: +Acquisition has three observable outcomes: acquired, already held by a live +workflow executor, or a storage/authorization failure. The initial local CLI +reports the active workflow executor rather than following it. Once acquired, +lifecycle changes are compare-and-set transitions against the retained state +and unfinished document-execution record read in the same transaction. +Beginning an execution, finishing its record and publishing the run state are +therefore lifecycle transitions, not independent writes another command can +observe between. +For a new `start`, the begin transition initializes pristine storage with the +immutable run, retrieval metadata, empty Workspace, first execution record and +`running` state in one transaction. For a resumable existing run, it validates +the requested lifecycle action and inserts one execution record while +publishing `running`. A replay of an already completed or failed root instead +inserts its execution record while preserving that terminal run state: the +replay observes an outcome that already won and does not make the run mutable +again. The settlement transition finishes the record while publishing its +terminal or resumable status. Begin and settlement are separate transactions +enclosing the document execution, and each is atomic within itself. -```mdx - +An unfinished document-execution record found after acquisition belonged to the +previous workflow executor and is proven stale. If the run says `running`, the +new workflow executor first reconciles retained root history. A root Close +restores its canonical terminal state. Without one, execution recovery closes +the execution and publishes `interrupted` before beginning another. A +cancellation command that +acquired the executor lock instead finishes that exact stale execution and +publishes `cancelled` without beginning another. If the run already carries a +terminal state preserved during replay, stale recovery closes only that +replay's execution as `interrupted` and leaves the authoritative run state +unchanged. It never guesses staleness from a PID, timestamp, timeout or status +row. - - Read the changelog and report the version. - -``` +An execution settlement has one workflow executor and one winner. Canonical +completion, failure, suspension and foreground interruption meet at the +execution-owned settlement boundary. A retained root Close proves canonical completion or +failure won before interruption or later lifecycle management. A cancellation +command never enters this competition: while another workflow executor holds +the lock it can only refuse, and after that lock is released there is no live +document scope left for another process to halt. Durability and Files +infrastructure failures retain their existing precedence. The winning execution +transition waits for +the document execution's complete child and resource teardown, then atomically +finishes its document-execution record and publishes the run state. Only after +that transaction commits may the host report the status and release the +executor lock. If settlement storage refuses, no unretained status is reported +and lock teardown leaves the run recoverable by the next proven workflow +executor. -The two are disjoint — an element stating both is stating the program twice, and -is refused rather than resolved by precedence. A paired producer renders under -the *narrowed* syntax reference, so an agent asked to write a fragment is told -what a fragment may contain rather than what the surrounding document may write, -while keeping the producer's own operational permission: narrowing what may be -*named* is not narrowing what the producer may *do*. The workflow profile also -accepts the released `source` spelling, silently, because documents were written -against it before `text` existed; the ordinary profile refuses it, and no -profile has ever had `program`. +Teardown is part of settlement evidence, not work performed after the outcome. +If teardown raises, the host does not report `suspended`: the +execution settles `failed` with a filtered journal reason when one identifies +the failure and a categorical host reason otherwise. Durability and Files +infrastructure failures still cross by identity and win by kind; if their +lifecycle transition cannot persist, no terminal status is reported. Every +finalizer is attempted before any such result is observable. -Within one run a logical Agent session is identified by the Agent/Session -expansion identity the engine derived, and by nothing else. The authored -`` is descriptive, so two sibling `` -elements are two sessions. The name travels the compositional public chain, -where a handler may observe or change it; the identity travels inside an opaque -placement the element routes, readable only through the coordinator delivered to -the installed provider. It reaches the placement from the invocation capability -the engine minted, never from the expansion context. That placement is bound to the element that opened it -and is good for one use, so a handler cannot keep the first element's placement -and route it for the second — both would otherwise resolve to the first -element's identity. +Retained lifecycle state gates every later acquisition before canonical +execution. A `failed` run carrying its root Close replays that recorded failure +for compatible `start --id`. A host-settlement failure with no root Close +reports the retained `failed` state and its categorical or filtered reason +without entering replay. A `cancelled` run reports its retained state without +entering replay under either command. None can be advanced under the same run +ID. -The mapping is a row in the run's own database, committed in the run's own -transaction: one that could commit while the run did not would describe a -session the run never had. It records the provider, the resolved agent command -and the fingerprint of the session policy the session was created under as -compatibility attributes — changing any of them refuses reattachment rather than -addressing a second mapping — together with the provider's asserted durable -identity. Cwd and provider placement are never compatibility inputs. What stays -beside the run on disk is the provider's own session store and one empty working -directory per session; both are disposable. +The executor lock remains in the trusted host's outer lifecycle scope; it is not +installed as a document provider. A completed replay may hold +the lock while recording its document-execution envelope, while the run remains +`completed` and cancellation remains refused. Canonical core returns the +retained result before preparation or root expansion. It attaches no Workspace, +Files, Service, Agent, process or external provider and performs no live durable +effect. -The order is placement, then the backend's acceptance of the session's first -turn, then the provider's canonical, tagged assertion of a durable identity, -then the mapping commit — and only then is anything that first turn produced -exposed to the document. +#### Local executor lock -Placement is inert. A `` says where a session will live and which agent -it is compatible with; it creates no provider session, contacts no backend and -writes no row. The session comes into existence when the first subscribed -`` starts a turn the backend accepts, through the one handle that -ensured it, and acceptance is reported by the adapter rather than inferred from -what the turn then produced. +The Deno lifecycle adapter acquires a non-blocking exclusive advisory lock on a +deterministic sidecar beside the run database. The open file and its lock belong +to the workflow executor's scope. The operating system releases the lock when +that process dies, which is the stale-execution proof the next acquisition +uses. The sidecar path and empty file are host arrangement: neither takes part +in run identity, the database schema, the journal or history. The exact +in-process `ExecutorLock` registered by the adapter is the private capability +validated by every mutation; no descriptor publishes it and no second process +can address it. -Occupancy of a key in the provider's own store is not an assertion: it says -something is there, not what conversation it is. A record the provider holds for -a first turn nobody accepted is exactly that — occupancy — so it asserts nothing, -no mapping is committed for it, and the next attempt creates rather than -resumes. +The lock file may remain empty after run deletion. Unlinking a locked sidecar +would let another process create and lock a different file at the same path +while the deleting workflow executor still holds its lock. An empty lock file +is not retained run state and is not reported as history or provider-session +data. -An interruption before the mapping commits is reconciled only from one canonical -provider assertion — exactly one, for that identity. A missing assertion, an -assertion conflicting with what is retained, one that replaced it, and more than -one at once each refuse. Once the mapping has committed, a restart compares the -provider's current assertion against it before continuing, and reattaches only -when the kind and the value both still agree. No refusal substitutes a new -session or a reconstructed transcript while reporting continuity. +#### Read-only lifecycle inspection -Cancellation owns both sides of a live step. It stops the Prompt stream or the -generated-XMD evaluation, completes provider and evaluator teardown, and removes -the disposable directory before the attempt settles. Already committed session, -Prompt, admission and effect records remain valid; an uncommitted read or -mutation may run again under the same retained admission, on its own -component's ordinary transaction and retry terms. A final proposal is the -retained result of its Prompt. Any later execution of its mutations crosses the -separate mutation-proposal admission rather than an Agent channel. +Inspection has its own provider-neutral immutable snapshot surface. It returns +run metadata, ordered history and current root identity rather than a writable +`WorkflowRunDatabase` or an executor lock. `status`, `list` and `history` use +only that surface. They never invoke canonical execution, read through replay, +attach a Workspace, materialize a root, import a document, contact an Agent, +process or external provider, or append. -Generated XMD is untrusted input. The evaluator preflights the complete fragment -before its first effect and admits only explicitly allowed, already-resolved -component identities. It refuses eval and exec blocks, imports, native -execution and arbitrary JavaScript execution. Ordinary `as` bindings, -structural language constructs and data expressions composed from JSON literals -and fragment-local binding references remain available; pure components such as -Json consume those values without effect permission. Generated bindings neither -import caller values nor escape the fragment. The evaluator retains the exact -filtered generated source for replay, history and deliberate training -ingestion. An incomplete evaluation can continue from retained source without -invoking the Agent again; a completed bounded information request reproduces its -rendered findings without repeating reads. +Two sources answer the same questions. `inspect(runId)` and `history(runId)` +address this host's retained storage; `inspectArtifact(path)` and +`historyArtifact(path)` address one sealed `.xmd` file. They are siblings rather +than one operation over a union: a run id names live lifecycle ownership — +something a caller may still advance, cancel or delete — while a path names +immutable evidence that left the machine which produced it, and collapsing the +two would make that difference a detail of a parameter. The artifact operations +verify the whole container before answering with any of it, reach no run store, +lock, Workspace, definition reader or external provider, and project the same +snapshot and history values plus the artifact's identity and committed frontier. +The path never enters the answer, so two copies of one artifact answer +identically. History is shared: both sources run the same projection over +ordered rows — authored source, cumulative forkability, inherited provenance — +because two mappings are how history quietly starts meaning different things +depending on where it was read. -Admission is built for the always-on trusted composition table and both effect -classes, and its ownership splits in two. -**Core** owns the mechanics behind `@executablemd/core/host`: parsing the -candidate fragment, walking all of it before the first effect, resolving each -admitted name **and authored form** to the exact pinned definition and identity -the host supplied rather than to anything a component search path, a -registration or a bundle would answer, recording one ordinary `generated_xmd` -durable admission before the first generated effect, and replaying that -admission and everything already committed without performing it again. It -reuses the same witness a component bundle does — canonical execution issues one -for the answer it produced and verifies it at the call site — so -`Component.importComponent` middleware may observe or refuse a generated import -and cannot answer, replace or mutate one. +The Deno provider first reads each retained database through its ordinary +read-only connection. A host crash may leave a hot rollback journal: that is a +healthy database one SQLite rollback away from the last committed state, but +SQLite cannot perform that rollback through a read-only connection. Only the +exact `SQLITE_READONLY_ROLLBACK` condition enters recovered inspection. Another +read-only failure is reported as itself rather than treated as crash recovery. -**The trusted host** owns the ceilings, as values it holds and passes: which -retained Workspace roots exist and which one is selected, which pinned -identities are in the `read` table and which in the `write` table, which -authored forms each is admitted for, and which exact requests each may perform. -Generated source receives none of those values, and nothing contextual carries -them. +Recovered inspection acquires inspection recovery coordination and retries the +retained database read-only. If a write-capable owner recovered it first, that +ordinary source snapshot is the answer. If it remains hot, inspection copies +the database and its rollback journal together into a private scratch directory +while it holds the coordination, then releases the retained source and lets +SQLite recover the copy. The copy keeps the candidate's exact database name and +passes through the same strict schema, retained-content, live/current and +location recognition as an ordinary snapshot. Recovery discards only the +uncommitted transaction, so the resulting recovered inspection snapshot is the +committed state the retained database describes rather than a repaired or +alternative run. -An authored `allow` selects a canonical, non-empty, duplicate-free subset of -the closed classes `read` and `write` from what the host already installed; -omitting it selects `read` alone, so a document that states no class asks for -read effects alone. It narrows and never grants: it cannot mint an identity, add -a root, authorize a destination, attach a credential or widen a request, and a -class the host stated no table for is refused before the candidate is parsed. -`allow` and bindings are independent: effect selection does not restrict the -language's ordinary fragment-local `as` behavior or pure composition. +Every write-capable connection to a retained workflow database holds the same +coordination for as long as it is open. SQLite recovers a hot journal on +whichever read first needs a page, and for a connection a host already has that +read can come at any moment, from a caller that knows nothing about any of this. +A hold that ended once the connection was open would leave exactly that read +free to roll the journal back while an inspection was copying the database and +the journal, so the two would describe different states. A connection therefore +owns the pair from the moment it opens until it closes, and a cached lookup +takes nothing because the connection it answers with is already holding it. -**A name is not a form.** A component whose two spellings do different things is -two identities, so one name holds two entries with disjoint form domains and one -shared implementation: `` resolves only to the pinned read identity and -`…` only to the pinned write identity. That choice is made in -preflight from the scan, before the first effect, so a form the selection holds -no exact entry for refuses the whole fragment. Overlapping forms or two -definitions under one name are a malformed host table rather than an ambiguity -settled by table order. +One consequence is deliberate: inspection of a crashed run waits while this host +has a write-capable connection to it open. That connection is the pair's owner +and is the very thing that may recover it. Deleting a run closes its connection +before recognizing it, so recognition never waits for a connection only the +deletion itself would close. +The coordination is separate from the executor lock, produces no public +capability, authorizes no transition and cannot make an inspector appear to be +a workflow executor. Its deterministic empty sidecar occupies a provider-owned +non-candidate namespace and may remain after run deletion like the executor-lock +sidecar; it is host arrangement rather than retained state. -Which identity was admitted is preflight's and is not answerable by anything -else, and the body the admitted component runs is held to that same decision by -the same mechanism an authored element is: canonical invocation-form dispatch -(*Capability-backed invocation identity*) enters the body for the form the scan -recorded, and the admitted invocation is checked against the form its identity -was chosen for before the component runs, refusing before any provider is -reached when the two disagree. That check reads the engine's own account of the -element — not `Component.hasContent()`, not the method on the object the -component was handed, and not the trusted wrapper the evaluator answers the -import with, which collects what a read returned and is not the form dispatcher. So -an admitted `File:read` does not become a write and an admitted `File:write` -does not become a read, however many times something is asked. +Scratch state is owner-private, belongs to the inspection scope and is processed +one candidate at a time. Every success, failure and cancellation attempts its +complete removal during teardown. A successful snapshot is observable only +after removal succeeds. A permanent removal failure instead returns a typed +inspection-recovery storage error after every cleanup attempt; it identifies the +host-selected scratch location so the operator can remove it and never repeats +retained content. Cancellation remains Effection control flow unless cleanup +itself fails. Private non-candidate scratch may therefore remain only behind an +explicit cleanup failure. Inspection never retries removal indefinitely, starts +background cleanup or reports successful inspection while such residue is +known. -The standard Deno workflow profile's write table is three entries, in the order -it states them: core's paired `File:write`, the bundled Git Plugin's paired -`Dir` under the versioned origin -`@executablemd/workflow/composition/dir-v2#Dir` — built from the same -implementation canonical core owns so the grant and the authored component -cannot drift — and core's self-closing `@executablemd/core#File.Delete`. That origin -names where released history recorded the entry, not what owns it today: -canonical core owns ``, the Git Plugin states the identity its released -runs retained, and the string stays unchanged so those runs still replay. A -host's own -extensions come after them. Selecting `write` intentionally authorizes Dir's -persistent recursive directory creation as well as the mutations File and -File.Delete perform. Each is the ordinary component and crosses `API.Files` -into the run's transaction-bound provider, so an ensured directory is retained -by the same `workspace_file` effect an authored Dir publishes. The evaluator -adds no mutation API, transaction or receipt of its own. A write-enabled -fragment that only ensures a directory renders nothing unless the fragment -authors output around it. Local Git, Git-host, issue, -process, eval and exec, native-command, credential and external-write effects -are outside the class. +Discovery remains arithmetic over the one run-storage root; there is no list +registry. Each run contributes one database candidate. Exact advisory-lock +sidecar names occupy a distinct provider-owned namespace and are not candidates. +The Deno provider applies the same strict schema, retained-content and +live/current recognition as execution to the direct or recovered snapshot. +One absent, foreign, incompatible, damaged or unparseable candidate fails the +whole list request with its distinct condition. Healthy rows are not returned +as though they were a complete list, and every candidate remains unchanged. -The versioned Dir origin is a grant boundary, not a symbol decoration. The -former `@executablemd/workflow/composition#Dir` identity authorized lexical -placement that created nothing; it does not authorize the persistent mutation. -A continuation whose retained write table names that former identity is refused -before generated execution instead of inheriting a broader grant. Read-only -admissions never selected the write table and remain unaffected. +Every public history entry is one retained protocol row: -That order is retained identity rather than presentation. A continuation is -compared against the entries position by position, so adding the deletion -identity to the table refuses a continuation that was admitted under the former -two-entry one — before any generated effect, and deliberately: a run resumed -under a wider grant than it was admitted with is the thing the comparison -exists to refuse. A read-only admission never selected the write table and is -unaffected. -`` neither prompts nor approves: approval is authored control flow -before the write-enabled element, and a branch not taken leaves no admission and -no mutation. +```ts +interface WorkflowHistoryEntry { + readonly eventId: string; + readonly event: DurableEvent; + readonly workspaceRootId: string; + readonly source?: Readonly; +} +``` -A refusal names the construct class and echoes no part of the candidate — in its -diagnostic and in what it retains alike, because the decision is recorded -either way. That holds for a request the candidate malformed as much as for one -outside the admitted set: the component reports a bad URL, header or timeout by -quoting it, so a generated request is normalized behind a boundary that keeps -the class and drops the diagnostic. The host's own ceilings are not generated -text and are normalized before the admission, so a host that states a request it -cannot mean is told which one. Admission is the line: before it nothing of the -candidate is retained, and after it the exact source is retained on purpose. +History includes Yield and Close rows, trusted preparations and root events, in +append order. The Workspace root is read from that row's existing +`workspace_root_id`. An authored durable operation carries its normalized +optional source in the stable namespaced +`EffectDescription["executablemd.source-position"]` field before the complete +event crosses the existing secret gate. History parses that field into +`source`; a Close or trusted-host event may have none. Source is non-identity +event data and never a location reconstructed from an expansion ID, current +definition or Workspace. A present field that does not parse is an unreadable +history entry, never silently treated as absent, and no value it held is quoted. -A retained admission is a grant, and a resumed run is held to it. Durable -replay matches an effect by its type and name, and what a description carries -is stored rather than compared, so the normalized policy is retained in the -admission's own result and a continuation checks it before one generated -component is invoked or one effect is performed. The check holds two kinds of -term differently, on purpose. The admission's Workspace roots and selected -root are an **as-of-admission retained basis** over a set the run's own -progress legitimately grows — every committed mutation retains another -immutable root and advances the authoritative current root — so the basis is -asked for by membership: every admission root and the admission's selected -root must still be retained, and the root the run now stands on must be a -retained one, while additional roots and an advanced current root change -nothing the admission was granted under. A run that no longer retains an -admission root has lost the history the grant was made over and is refused -before generated work. Every non-root term — the class selection, the selected -pinned identities with their admitted forms, and the normalized request -ceilings — compares whole and exactly, in canonical order. A pinned identity is -one closed tagged record — whether canonical core supplies the body or the -ordinary import chain answered for it, the origin, the component key and the -revision — rather than an assembled spelling, so a reader comparing two -admissions can say which term moved and no two hosts have to agree on a -separator. The untagged version-1 record holds one opaque string instead — -whatever the party that built the pinned entry chose, which for core was -`@executablemd/core#File:read` and for the workflow host was -`@executablemd/workflow/composition/dir-v2#Dir`, with no revision in either and -no convention to read back out. Nothing recovers four structural terms from one -of those, so nothing tries: a *capability* entry states the exact version-1 -strings it succeeds, and a retained string reconciles against that stated list -and against nothing else. Stating one is an assertion by whoever owns the entry -that the current grant is no wider than the old one; an entry that states none -refuses every version-1 record naming it. A component answer states no alias and -has nowhere to put one, because that arm did not exist when version-1 records -were written. New records always state the structural identity. The run's own -ceiling table is keyed by those terms encoded rather than joined, because a -joined key is only as unique as the separator is illegal and nothing makes a -space illegal in an origin or a component key: two entries whose terms differ -only in where a space falls each keep their own admitted requests. Only the -tables the selection reached take part, so changing a write table a read-only -admission never drew on changes nothing it was granted under. Deciding the -fragment inside that durable effect is what makes this hold: a continuation -restores what was admitted without consulting the current candidate at all, so -what expands is the source this run admitted rather than whatever a later caller -is holding. +The JSON projection is lossless: it returns every entry above with the complete +already-filtered protocol event. Human history presents each Yield as a durable +operation rather than repeating the protocol word `yield`. It renders the root +Close as the run's canonical outcome footer instead of an otherwise empty +operation row. When structured execution produces child Closes, it associates +their terminal results with those child coroutines so their ordering remains +visible. A suspended, interrupted or cancelled run with no root Close instead +states that no canonical document outcome was recorded. Presentation never +removes an event from the JSON contract or synthesizes a journal event. -The admission and every durable effect the admitted fragment performs are -yielded inline by the owning document expansion, in authored order and ahead -of the expansion's later effects, so one durable sequence accounts for the -admission, each nested generated effect, and whatever the document does next. -A partial continuation is offered that same sequence: the admission and every -completed nested read or mutation restore from their retained records without -another live execution or root publication, and the run continues to the next -unrecorded position. The existing `scoped()` around generated expansion keeps -owning its middleware, cancellation, and teardown. The evaluator is an -ordinary `Operation` and is not wrapped in `ephemeral()` — DEC-034 reserves -that adapter for intentionally non-durable Operations, and an operation that -can yield durable effects is not one — which is conformance, not the -continuation correction: exact journal order is preserved with or without such -a bridge, and the reproduced resume failure was the former exact root -comparison rejecting the run's own progress. +#### Portable XMD artifacts -An XMD-mediated HTTP read is how a network-denied Agent is given external -information: the authored document performs the read with `` and the -retained response reaches the prompt as data. That does not raise the Agent's own -ceiling. A generated fragment may name the pinned `Fetch` identity only when the -host admitted it, and then only for a request that normalizes — scheme, host, -path, method, headers and effective timeout alike — to one the host stated -exactly; anything else is refused before `API.Fetch` is reached. A repository -component that takes the name `Fetch` is not that identity and acquires no -admission from it. +An XMD artifact is a semantic export, not a renamed workflow-run database. The +exporter reads one committed retained snapshot and writes a separate sealed +container containing that snapshot, every retained Workspace root and byte it +references, and the workflow definition source closure. Host paths, lock +sidecars, rollback journals, open connections, retrieval metadata, credentials +and provider-owned session directories do not cross the boundary. The initial +container encoding is SQLite, but its tables and SQLite schema are private; the +public compatibility boundary is the versioned XMD artifact contract. -`` is admitted only in its self-closing read form. The component reads -when it has no content and writes when it has some, so the two forms are two -identities and the constraint travels with the pinned one, decided in the -whole-fragment preflight rather than inside the component after earlier elements -have run. +The source run stays authoritative for itself and is never moved into the +artifact. Export takes the executor lock before choosing the artifact frontier, +so a live workflow executor makes export refuse and no later committed event can +race the chosen frontier. Crash recovery, when required, operates on a private +copy under inspection recovery coordination. Export performs no lifecycle +transition and never changes the retained source. -Mutation-proposal admission and workflow-bundled Markdown component admission -remain unbuilt. Directory registration is not among them: a workflow Agent is -given no directory to register. +what one XMD artifact states about every logical workflow Agent session that contributed +a retained Prompt: either complete portable evidence — the session and its compatibility +attributes, the provider, the bundle kind and compatibility identifier, the source and +bundled provider-session identities, the identity-allocation mode, the ordered provider +checkpoint tokens, and the Agent session bundle's length, hash and bytes — or an +explicit unavailable marker naming why. A session that retained no Prompt has none. It +is detached retained evidence about a conversation, never a live provider session and +never control over the host that produced it -### Typed candidate recovery around Evaluate +Version 1 has two profiles. One holds no Agent portability content and is read +exactly as it always was: a session that contributed a retained Prompt may carry +no classification, and nothing is reconstructed for it. The other classifies +every such session exactly once, as Agent session portability evidence — +portable, with ordered provider checkpoint tokens and an Agent session bundle, +or explicitly unavailable. Which profile a file holds is decided only after its +manifest and identity have been verified, so a descriptor is never interpreted +out of content nothing has authenticated, and partial or mixed evidence is +corruption that yields no projection. Recognition is what exists: no run +captures a bundle, the exporter emits no such evidence, intrinsic forkability +still reports `agent-state-unavailable` for an Agent turn, and status and +history render no classification, token or bundle byte. -Plan composes ordinary `` and ``. There is no deadline, no output -budget and no bounds record: the composition adds recovery, not limiting, and an -ordinary invocation without it behaves exactly as it does today. +Semantic format, container and manifest are each version 1. Identity uses the +exact domain `xmd-artifact\0v1\0`; every other declared version is refused. +The workflow source closure is retained entry/content pairs, without a Git +closure. -The composition owns the genuine invocation and projects its content through -canonical core's private projection. Neither public content middleware nor an -operation supplied by another loaded copy can author that projection. The -generated admission and effects retain occurrence-owned identities in the -existing ordinary durable sequence. The caller supplies no context, stream, -cursor, coroutine ID or durable owner. This is execution-owned invocation -routing, not a persistence capability. +Every command that consumes an XMD artifact opens it read-only and verifies its +complete artifact manifest before returning any partial answer. That manifest +determines the XMD artifact identity. A bytewise digest of the finished file is +reported separately for transport verification; neither digest is a signature, +so authenticity still comes from the channel that published the artifact. -The composition owns the live projection with Effection structured cancellation: -it establishes cleanup before resources can outlive the operation, and it -settles the child's success or failure only after that teardown. A private -invocation settlement callback reports cleanup failure before a recoverable -outcome can escape, so a cleanup failure always wins over a candidate retry. -This is local to the composed projection and does not change ordinary -execution's failure precedence. Caller cancellation remains cancellation. +An artifact grants inspection and history-fork input only. It is never resumed, +answered, cancelled, deleted as a run, or admitted as the writable storage for +an existing run ID. A history fork from it creates a new run ID, copies retained +state rather than sharing pages, records the artifact identity with the source +run and checkpoint, and passes through the same compatibility replay and +forkability checks as a fork from local retained storage. The artifact remains +byte-identical before, during and after the fork. -Admission and generated effects use ordinary durable publication: each event is -persisted before its operation resumes. A safe refusal does not erase completed -effects. The projection and its structured teardown finish before Evaluate -returns or later parent work begins; interruption restores completed effects and -resumes at the first unrecorded effect. +This split is the concurrency boundary between environments. Arbitrarily many +hosts may inspect the same bytes or fork independently because none of them can +advance the source identity. No SQLite lock can coordinate downloaded copies, +and the design never asks one to. -Execution checks the closed evaluation records before completed-root reuse and -before public replay middleware can decide anything. Their structural -fingerprint binds the exact root source and inputs, candidate source, read -selection, ordered profile identities and forms, protected Syntax identity, -lexical reference and filesystem scope/policy identity. -Custom symbols providers state an immutable `identity`; captured Files state a -frozen `{ scope, policy }` `replayIdentity`. These identify permission, not today's -documentation, file contents or paths. Records contain data only, never providers, -live references, projection handles or callables. +#### Cancellation control -Completed replay performs fresh installation and identity attestation, then -restores historical rendered output without repeating fragment bodies, Syntax -documentation lookups, File reads or Glob traversal. Missing or invalid current -attestation refuses before those effects. It does not reuse an expired attestation -or refresh information to validate history. +Cancellation never reaches into a live document execution. The initial local +workflow runs in the foreground, so the process that owns its Effection scope +also owns the only supported way to halt it: foreground interruption. When the +advisory lock reports a live workflow executor, +`workflow cancel ` refuses without mutation and tells the caller to +interrupt that foreground process. +Ctrl-C performs orderly scope teardown, publishes `interrupted` and leaves the +run resumable; it is not explicit cancellation. -One narrow namespaced classification identifies the normalized recoverable -generated-request refusal and carries only a safe reason. Stale admission or malformed -retained history, and terminal setup, runtime, provider, persistence, secret and -cleanup failures, are not that classification and are never recovered. -Normalization reads the namespaced descriptive tag across loaded copies rather -than matching messages, and a plain `GeneratedXmdError` is not a recoverability -marker. Public Evaluate still throws; only the trusted surrounding operation -turns the one recoverable class into retry context after settlement. +Without a live workflow executor, the management host acquires the executor +lock before any cancellation mutation. The exact executor lock is validated +inside the transaction that publishes `cancelled`. A stale `running` +execution is first checked for a retained root Close: a Close restores its +canonical completed or failed outcome and cancellation refuses; without one, +the transition finishes that exact +unfinished execution as `cancelled`. A suspended or interrupted run has no live +execution to halt and becomes `cancelled` directly. Success is returned only +after the lifecycle transition commits. There is no watcher, poller, control +descriptor, cancellation-request sidecar, acknowledgement protocol or +cross-process cancellation channel in this slice. -## Local Workspace topology +Cancellation follows one status matrix: -The local workflow host owns SQLite directly in Deno and reuses Cloudflare's -DOFS filesystem layer behind the provider-neutral Workspace boundary. One -authoritative provider-owned connection entry serves each canonical workflow -database path until provider teardown. The journal and DOFS adapter use that -same SQLite connection; Cloudflare's synchronous initialization transactions -become uniquely named savepoints inside XMD's caller-owned transaction. Those -savepoints and operation-spanning savepoints share one allocator and cannot -collide or release one another. After a savepoint successfully rolls back and -releases, the shared rollback path invalidates both caches on the authoritative -DOFS wrapper before outer transaction work resumes. A second long-lived DOFS -connection is not a coherent reader because provider -caches may retain negative entries across another connection's commit. +- `running` with a live workflow executor: refuse without mutation and direct + the caller to interrupt the foreground process; +- stale `running` with no root Close: acquire the executor lock, finish the + unfinished execution and publish `cancelled`; +- `suspended` or `interrupted`: acquire the executor lock and publish + `cancelled` without starting a document execution; +- `cancelled`: idempotent success; +- `completed` or `failed`: refuse because their terminal outcome already won; + and +- absent, foreign, incompatible or damaged storage: report that condition and + change nothing. -Complete schema version 1 retains the canonical empty Workspace root and its -root-only live frontier initially. It retains arbitrary canonical roots and can -materialize one privately through the authoritative connection. Capture runs -inside the caller-owned transaction. Restoration runs in a nested savepoint, -clears the authoritative resolution and blob caches, and resnapshots to the -selected identity before release. Private Workspace transaction bodies finish -their child teardown before final live/current validation. The Workspace effect -coordinator also finishes its mutation savepoint scope before it invokes -capture. +Deletion also acquires the executor lock. A live workflow executor is refused; +a stale `running` record is proven stale before the exact run's database and +provider-owned retained records are removed. Only the empty lock file may +remain. Every non-live status may be deleted. The explicit run ID is the +confirmation boundary, so the command has no interactive prompt and no +wildcard. An absent run is reported rather than treated as an idempotent +deletion. Deletion does not report that it undid an external effect. -Separately, every unsuccessful caller-owned transaction attempts top-level -SQLite rollback and then -invalidates both caches on the provider-owned DOFS wrapper while it still holds -the serialized connection turn. This includes body failure, cancellation, -teardown or final-validation failure, and commit failure. The surviving wrapper -therefore cannot answer from uncommitted positive or negative cache entries -after SQLite has restored the prior frontier. +### Durable suspension -Retained roots, manifests and blobs remain indefinitely. Cloudflare garbage -collection is not in the production closure and is never invoked. The provider -exposes no public history selection at this layer; a fork's root copying is a -lifecycle transaction above it. Its -coordinator combines one mutation, immutable-root publication and one filtered -journal result atomically, and the transaction-bound Files provider is what -routes a document's `` and `` to it. `xmd workflow start` and -`xmd workflow resume` install that provider around each execution. +Suspension is an authoritative document-execution outcome, separate from +completion, failure, interruption and cancellation. `@executablemd/workflow` +exposes a provider-neutral operation with this contract: -The coordinator treats only errors produced through its private filesystem -adapter's documented path and mutation refusals as journalable operation -failures. It rolls their savepoint back, keeps the prior current root, and -commits one failed protocol Yield. Connection, savepoint, content, capture, -routing, filtering, insertion, teardown and commit failures roll back the outer -transaction and activate the durable run's first-failure fence. Cancellation is -control flow: it publishes no failed result. The durable coordinator contract -supplies a provider-neutral failure activator so a provider can retain that -first infrastructure failure by identity; the default coordinator does not use -it and keeps its existing behavior. +```ts +interface WorkflowSuspensionRequest { + readonly request: Json; + readonly responseSchema: JsonObject; +} -Losing the host is not one of those outcomes, because nothing runs to handle -it. No cleanup, commit or rollback happens after the process dies: the -operating system closes its connection and releases the locks it held, and the -interrupted transaction is left for the next write-capable source connection to -recover. Read-only lifecycle inspection may instead recover a private copy under -the coordination described above; it never takes recovery away from that next -source owner. The mutation, the immutable root, the current-root pointer and the -routed event have all been written by then, and recovery exposes the last -committed state and none of them. +function suspendFor(request: WorkflowSuspensionRequest): Operation; +``` -That is the same boundary every other reader already observes rather than a -second rule for crashes. A second connection sees the last committed state for -as long as the writer's transaction is uncommitted, so a crash publishes -nothing that was not visible before it. What a later process finds is that -committed state — the filesystem, the current root, the retained roots and -references, and the ordered journal with its event identities and root -associations — from which it performs no recorded effect again and can -materialize any retained root. +The request says what is awaited and the schema says what value can resume it. +The caller supplies neither a suspension ID nor lifecycle ownership. The +trusted execution derives one suspension ID from the run and durable coroutine +position, so a component or middleware reaches the same logical wait across +replay and a different logical wait cannot receive its input. -The Deno journal adapter routes an append ordinarily when no destination is -bound. A publication may instead bind one exact transaction destination for its -own lexical scope after the existing secret gate. The route validates the -database lease, connection generation, transaction identity, token and open -state before delegating to the existing `transaction.journal`; it contains no -insertion SQL of its own. A nested route for another run delegates past itself, -and `readAll()` always follows ordinary replay without routing or secret -filtering. The provider-owned ordinary destination and each publication-local -routed destination are terminal `{ at: "min" }` handlers, so an enclosing -loaded copy with the same stable contextual name cannot suppress an append or -run ahead of exact-token validation. +The operation first publishes an ordinary durable `suspension_request` Yield. +Its description contains the suspension ID, request and response schema. Its +successful Result means only that the request publication committed; it does +not establish that the wait or answer completed. The complete event crosses the +existing secret gate and receives the current Workspace-root association in the +same way as every other nonmutating event. The run's stop reason references +that already-filtered event by ID, so request text is never duplicated into +lifecycle state. No third protocol event and no pending-request table is added. -The initial topology requires neither writable FUSE nor native subprocess -access and does not bundle `workerd`. A Cloudflare-hosted or workerd-backed -provider may install the same contextual contract without changing documents or -lifecycle commands. +After live publication or replay of that Yield, the operation invokes an +execution-owned suspension controller through a stable contextual API. That +named route is what lets a component's separately loaded workflow package reach +the controller installed by the binary's bundled copy. It is composition, not +admission: middleware may refuse or suppress the route for its descendants, but +no value it returns is a suspension answer and it cannot make `suspendFor()` +continue past an unanswered wait. -Worker JavaScript is outside the initial local capability set. Worker Shell is -inside it: one `just-bash` invocation runs in a Deno Worker through Cloudflare's -Workspace filesystem adapter. The host begins one immediate SQLite transaction -and one effect-local mutation savepoint. Success releases the savepoint, appends -the already-filtered result and commits. Failure, timeout, cancellation and -Worker termination roll the mutation savepoint back, append the failed result -and commit. A host crash rolls back the still-open transaction entirely. +The shipped caller is the Elicitation provider the workflow host installs, so a +document asks for a durable wait by writing the `` it would write +anywhere. Two consequences follow from where that call has to sit. A workflow +run resolves `Elicit` to a registration of the host's own, which asks the +question and writes no durable record: the wait must be the outermost durable +operation at its position, because a request published inside an enclosing +durable operation takes the journal slot that operation never settles, and the +resumed execution replays the coroutine and finds the wrong entry waiting. And +the answer is therefore retained once, by the suspension protocol, rather than +twice. Public Elicitation middleware — an `` region, or anything else +installed nearer — may answer the question or refuse it, and in both cases the +run it affected never suspended; continuation of a suspended run is retained +delivery state and nothing else. -Every Worker filesystem request carries the effect identity and a -per-invocation token. Missing, foreign, cancelled, completed and stale requests -are refused. Cancellation must be able to forcefully terminate a CPU-bound -interpreter; graceful Worker shutdown alone is insufficient. Worker Shell -exposes no native executable or host PATH and is not described as POSIX or -native Bash. +The retained request at the execution's exact current durable position is what +authorizes entry. The controller derives the identifier for the position +immediately behind the caller's own, requires the presented one to equal it, +reads the Yield at that exact position, parses it as a suspension request and +requires it to describe what is presented. An identifier from a request earlier +or later in the journal, a reconstructed contextual API name, or a matching row +without the exact current position authorizes nothing. -## Document targets +Durable replay identity compares a type and a name, both public, so another +durable operation can reproduce the retained request and arrive at the same +position. Once the controller has validated the exact retained request, that +operation is observationally the same request to wait: it gains nothing +that calling `suspendFor()` with that request would not have. Package-instance +identity therefore does not take part in admitting a suspension. Requiring it +would make the public operation fail when the binary and a repository component +load separate copies of `@executablemd/workflow`. -A root document addresses its own sections. The outline is discovered from the -document's static Markdown alone: only headings in the root flow are targets, -and a heading is addressable only when its text is statically rendered — a -heading generated inside a component, or one carrying an interpolation, has no -stable address, and neither does anything beneath it. Discovery therefore -parses a copy in which the scanner's top-level component spans are blanked, -because a Markdown parser reading raw XMD cannot tell a component's children -from the root flow. +With no delivered input, the controller reports the request to the workflow +executor and remains pending while that executor initiates an orderly halt. +`suspendFor()` also accepts no returned value as an answer in this state, so a +middleware handler that suppresses the controller can only leave its descendant +pending, as document code can already do; it cannot publish `suspended`, supply +an answer or continue past the wait. The operation throws no ordinary error and +produces no value; the halt leaves the root without a Close. The executor +observes the validated suspension notice as its separate settlement candidate, +waits for complete scope teardown, then atomically finishes the +document-execution record and publishes `suspended` before releasing the +executor lock. -A document's preamble is what precedes its outline — the source before the first -root-flow heading, whatever its depth. A document may open at a deeper level than -the one supplying its title, and an earlier section is a target in its own right -rather than preamble every other target carries. +A root without Close is valid partial history. On resume, earlier durable +effects and the request-publication Yield restore without executing or +appending. Ephemeral structure is rebuilt only as the definition reaches it; +provider attachments stay lazy, and a provider is never contacted for a +replayed effect. At the same suspension ID, absent input reports the same +notice, halts again and adds no second request event. The new document-execution +record may settle `suspended`; the request remains singular. -Selection resolves exactly once, before the document expands and before any -authored effect runs. A selector may glob, but it must name exactly one catalog -entry: naming none and naming several are both failures, and two sections that -canonicalize to the same path stay two entries so the ambiguity is reported -rather than resolved arbitrarily. +#### Typed answer delivery -The selector and the target it resolves to are different things, and only one of -them is identity. +Issue #300 builds typed answer delivery. Its boundary is the suspension ID and +the retained response schema. Delivery is an operation separate from execution: +`xmd workflow answer ` retains one value for that +exact wait and takes no executor lock, fetches no definition, attaches no +Workspace, inserts no document-execution record, appends no journal event and +changes no run status. A host offers a value only after the run is `suspended`, +its stop reason names a retained `suspension_request` event carrying that ID, +and the value satisfies the schema that request retained. The retained state and +the answer event it would become cross the same secret gate durable journal +persistence uses; `--no-secret-detection` disables that gate for one delivery. +Every refusal — duplicate, consumed, wrong run, wrong request, late, invalid, +cancelled-run and deleted-run — leaves the database unchanged, and neither the +rejected value nor a secret match appears in a diagnostic. -**The exact canonical target is definition identity.** It is what ran, so it is -what a document execution records durably, what a targeted workflow definition -carries, and what a resumed run is checked against. +On a resumed execution, only authenticated retained answer state lets +`suspendFor()` publish a separate ordinary durable `suspension_answer` Yield and +return its value; a contextual handler's return value never publishes an answer. +Consuming the retained answer and appending that Yield are one database +transaction, so a crash before it commits leaves the answer pending and a +transaction that does not commit publishes nothing. Replay after that +transaction commits restores the recorded answer event without reaching the live +controller and without consuming or publishing again. -**A caller's glob is non-authoritative invocation metadata.** It describes what -a caller asked for — two callers may spell one request differently — and it -never substitutes for the exact target: it does not occupy the recorded -exact-target field, it never enters a workflow definition, and it is never -re-resolved against a newer checkout to decide what a resumed run means. A glob -is retained in exactly one place, a failed selection's structural record, and -only so that an ordinary failed execution can be reproduced. +Scheduling — automatic resume, watchers, unattended iteration and remote host +selection — is #300's and is not part of this behavior. Nothing here waits on +it: a suspended run continues through `xmd workflow answer` followed by an +explicit `xmd workflow resume`. -The command line is the first consumer of that split. `xmd run` resolves a -selector while inspecting the document, then asks execution for the exact target -that resolved — so a file replaced between the two reads fails on the target the -run chose, rather than silently running whatever the glob would name now. +This contract folds issue #322's suspension effect into #367. Issue #322 no +longer supplies a separate implementation prerequisite; its typed correlated +answer delivery remains with #300. -`xmd workflow start` is the second, and it is stricter because its answer is -retained. It resolves the selector once, against the exact bytes it is about to -retain, and stores only what core answered. A resume re-enters that section -without resolving anything again: the glob is gone, and the bytes it was -resolved against are the run's. +Issue #367 changes no durable schema shape. Single-executor enforcement uses only the +provider-owned advisory-lock sidecar, suspension uses existing filtered journal +and run-state records, and history reads the existing event-to-root association. +Issue #300's retained answer state does add a durable record, and it amends the +one schema in place on the same terms as every other amendment: one +`workflow_suspension_answers` table, no second version, no migration, and strict +recognition refusing every other shape. -The workflow definition is the second. It optionally carries that exact target, -compares it with the rest of the descriptor, and validates it through core's own -canonical-target predicate rather than a rule the workflow package restates — -identity two packages define separately is identity they can disagree about. A -run of one section, a run of another, and a run of the whole document are three -different runs. +Issue #293 amended that exact shape in place rather than introducing a second +version, and Repository and Worktree creation records joined it the same way. +This pre-release carries no persistence compatibility obligation: there is one +inventory, no migration and no upgrade path, so a database an earlier +development build produced is refused as damage rather than recognized as an +earlier shape. Strict recognition compares the amended declaration exactly. -A resumed run re-resolves the current selector against the *recorded* content -and refuses to continue unless the outcome is the one recorded. A failed -selection is an outcome too, and is recorded and compared as one — otherwise a -journal left by a selector that matched nothing would answer a later request for -a section that does exist. +A Repository record holds a Workspace-local name, the admitted credential-free +locator and its fingerprint, the requested base or its absence, the commit +pinned once at creation, the initial primary branch, the object format and a +stable Workspace-relative checkout path. A Worktree record holds its +Repository's name, its own name, the requested branch, the requested base or +its absence, the creation commit and its checkout path. Both are immutable +creation identity. Current HEAD, refs, index, checkout and linked-worktree +administration live in the versioned Workspace filesystem, so one selected +Workspace root recovers the Git state belonging to that frontier without any +unversioned "current commit" column to disagree with it. The locator is stored +beside the record rather than inside it: compatibility needs the bytes, while +the journal and the document receive only the fingerprint. -Validating that identity is **execution-owned**. Replay guards are composable -policy — a handler installed further out may decline to delegate — so identity, -which must not be negotiable, is not decided there. The execution owns the -journal it replays through and validates the recorded selection inside the read -itself, ahead of public guard policy, of a retained terminal result being -reused, of authored work, and of any append. The retained history it owns covers -every event that takes part in that decision, a recorded completion included, so -no event can present one identity to the validation and another to the run. +### Replay and effect transactions -Three views of that history stay distinct. The **authoritative** one is what -admission validated and replay consumes, and it is immutable to policy. -Replaceable public policy reads an **isolated observation** of it, so a handler -composes freely without acquiring the enforcement the gate exists to withhold. -What a document receives is a **fresh mutable copy**, because a resumed binding -is ordinary data its own continuation writes to. Reusing a terminal result also -requires exactly one recognizable root import belonging to the coroutine whose -result is being reused. +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 and reattach an Agent provider only when live work needs one. A completed root +result returns without attaching any provider. -Every public path validates in one order: parse the source, resolve the target, -compile schemas, then build the projected definition. A caller who named a -section the document does not offer hears that, rather than a complaint about a -schema they never reached. +The Workspace stores the current frontier state. The journal stores the +historical 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. -A recorded selection is a closed protocol, and a record that does not satisfy it -is refused rather than delegated. "This event is not the root import" and "the -root import, malformed" are different answers: one continues, the other fails -before the recorded terminal result can be reused, without executing authored -work or appending history. The record carries the content it was taken from, so -the selection is verified against that content rather than merely parsed, and -reading it is total — every way journal-controlled data can refuse to be read is -the same refusal, carrying nothing the record supplied. +#### Live effect coordination -## Expansion identity +Structured durable operations accept an explicit provider-neutral live +coordinator. The default coordinator executes once, converts execution success +or failure to the existing durable protocol `Result`, calls the existing Yield +publication continuation once, and returns that same result only after +publication completes. The continuation publishes through the durable stream's +ordered append fence. A backing append failure activates fail-stop state and +raises `DurablePersistenceError` with the adapter error as its cause; it writes +no compensating `Close`, and catching it cannot admit later durable work. A +marked pre-persistence policy rejection remains an ordinary policy failure. +There is no generic durable validation hook: validation owned by a caller or +provider occurs before it constructs the durable effect. Replay, including the +replayed prefix of a partial run, bypasses the coordinator, execution and live +publication. Callback-based durable effects keep their existing path. -Core describes the executable element currently being expanded: - -```ts -interface Expansion { - readonly id: string; - readonly name: string; - readonly position?: Readonly; -} -``` +The shared Workspace durable-operation wrapper reads a contextual provider +selection explicitly. That replaceable API carries only a non-operational +selection identity: execution, publication, failure activation and journal +provenance never pass through that middleware. Provider selection +creates a one-use route and a separate opaque credential. The live call keeps +that credential out of its contextual invocation request and places the exact +executor, publisher and failure activator behind an execution-owned capability +that accepts only the credential. A provider installed by any loaded package +copy terminally consumes the route and invokes that capability directly. It +does not send inspection, execution, publication, failure or completion through +the contextual continuation. Enclosing middleware at either priority can +therefore route or refuse the initial request but cannot observe or acknowledge +an operational phase. No module registry or replaceable structural value +resolves provider or invocation ownership. -`getExpansion()` returns the same frozen snapshot throughout one live -expansion. `name` is the authored tag name, independent of which repository, -registered or built-in component resolves it. `position` is the opening tag's -source position, including its workspace-relative path when known; dynamically -rendered markup may have no position. A nested expansion temporarily replaces -the contextual expansion and returning restores the enclosing one. Calling the -operation outside an executable element expansion throws. The snapshot exposes -no props, bindings, projected content, selected component definition or live -scope. +The execution-owned capability accepts execution and publication once and +records the exact `Result` only after the provider returns it from the completed +publication. The live operation ignores the contextual call's response as +completion evidence and resumes from that recorded result alone. A short +circuit or altered response therefore activates fail-stop before resume, while +an exception or substituted response after authoritative completion cannot +replace the published result. Foreign or substituted selectors and reused, +completed or stale calls fail before transaction work. Installing no provider +cannot leak execution or publication, and selecting Workspace coordination for +one operation does not enlist unrelated durable operations in the same scope. -An expansion ID is derived from the root document and structural expansion -path. It uses no process-global counter, time, randomness or scheduling order. -Replay, continuation, retry attempts and restoration of the same loop -iteration preserve it. Different authored elements, loop iterations, -projections, component expansions and root documents receive different IDs. -The ID is opaque and supports equality only. Two workflow runs may contain the -same expansion ID; a durable effect that needs workflow-wide identity uses the -run ID and expansion ID together. Replay preserves the snapshot's fields, not -JavaScript object identity. +The Deno adapter establishes the canonical module's journal provenance for each +WorkflowRun journal and retains that exact witness. The generic pre-persistence +guard is policy-neutral and returns an unproven wrapper; the trusted +secret-filter and execution-owned admission wrapping sites preserve provenance +explicitly, so a filtered journal — including one wrapped more than once — still +carries the witness its source carried. Before it opens a transaction, the adapter requires both the +exact proof executor and the journal provenance from the consumed invocation to +belong to the selected WorkflowRun. An in-memory stream, another run's journal, +a copied property, an ordinary guard, a custom look-alike, or a wrapper another +loaded copy tried to prove is refused before mutation or publication. +Inside the transaction, the provider's publication operation calls the +execution-owned publisher directly and returns only after that publisher records +the exact Result. The transaction body cannot return or commit on a contextual +publication acknowledgement. -`Expansion` and `getExpansion()` belong to `@executablemd/core`, so ordinary -document execution receives expansion identity without installing workflow -middleware. +#### Workspace-local transactions -Selecting a document target does not disturb any of this. Projection retains -the original source ranges and scans each one under its own origin, so a -retained element keeps the offset and line it was authored at, and with them -its expansion ID. Two runs of the same document under different targets -therefore agree on the ID of every element they both retain, and may share IDs -without sharing effects — run identity and workflow-definition identity are -what tell those runs apart. +Every Workspace-local expansion publishes one effect through one effect +transaction: -## Two layers +```text +BEGIN + apply the filesystem, Git or metadata mutation + publish the resulting logical Workspace root + append the filtered journal result +COMMIT +``` -Error handling has two layers: +All three changes commit or none does. Nested effects finish their transactions +before their parent's effect begins. Declarative Git operations, including +staging, switching and committing, operate on the same transactional Workspace +rather than invoking an untracked native Git side effect. -1. **Lexical structure** — the document applies context, in two forms: - context values (`` sets the `output` error mode) and middleware - (`` installs retry middleware). `` uses both: it sets - `print` for its region and installs printing middleware. -2. **Runtime execution** — execution runs under the context applied by the - enclosing structure. +Successful effect coordination finishes the mutation scope, including child +cleanup, before capturing the resulting root. The Deno provider installs that +ordering for its Workspace effect operation: the mutation +savepoint, root publication, filtered routed Yield, and caller-owned transaction +commit form one boundary. The Workspace filesystem uses the pinned synchronous DOFS +entry points for its string and byte-array surface, so cancellation leaves no +eager promise or stream pull able to reach the connection after transaction +ownership ends. The transaction-bound Files provider selects that operation for +every document filesystem read, write and search, and the workflow lifecycle +commands install that provider. -Code inside a region doesn't see the context that governs it. A block inside -`` doesn't know it's being retried. Behavior reads from the enclosing -structure, not from the affected code. +#### External effects and Git-host mutations -## Probabilistic behavior is visible in the structure +An external provider cannot join that transaction. Prompt, Git push and pull +request effects derive a stable identity from the run and expansion, ask the +provider to perform or reconcile that identity, then append one local result +transaction. Replay adopts proven compatible completion, performs a proven +absence and refuses conflicting or permanently ambiguous state. Temporary +provider unavailability may be retried or suspended by explicit middleware; it +never authorizes a duplicate effect while completion is unknown. -Every source of probabilistic behavior is visible in the document's structure -(`sample`, `exec`, ``) — at the construct that applies it, not at the -code it affects. Everything else is deterministic: same inputs, same behavior. +Git-host mutations — Git push and pull-request upsert — share one +provider-neutral reconciliation of that boundary, so a new Git-host effect kind +supplies a request and a provider rather than a replay policy of its own. Prompt +is not one of them: it keeps its own Agent-provider contract. A live Git-host +attempt observes before it mutates, adopts a proven compatible completion, +performs a proven absence exactly once, and refuses conflict and permanent +ambiguity. A Git host need not implement every kind; one that does not refuses +from observation, before any remote work. -Known exception, to close: eval can reach `Date`, `Math.random` and the -network directly. +Reconciliation is reached through exactly one stable contextual operation, and +that is a deliberate correction rather than a simplification. An earlier design +selected a provider on one surface and coordinated the invocation on a second; +public middleware could read a credential from the first, take an invocation +capability from the second, and answer a phase itself, and the journal then +retained a completion no provider had produced. Two public surfaces that are +individually harmless composed into the power to settle a completion. -## Error propagation +The single operation is request-only. Public middleware receives one frozen, +one-use routing request describing the complete detached request; it may read +it, refuse it, install narrower policy, or delegate it, and the value it returns +is ignored. It receives no credential, no capability, no answer operation and no +phase evidence, so there is nothing to combine. Each phase builds its own API +descriptor under that same stable name: sharing the name shares the middleware +chain, while owning the descriptor owns the default, and that default is the +invocation's own terminal. The selected provider's handler is installed at the +terminal end of the chain, so its continuation — a parameter of its own +generator — is the only route to that terminal. Through it the handler asks the +terminal to inspect the exact request, calls the provider, and submits the +answer. A handler that short-circuits, forges a return, substitutes or replays +the request, or reconstructs the stable name reaches the provider's handler or +the public refusing default, never the terminal. -An error, once decided, is one of two things: +The durable operation is named by a fingerprint of the complete detached +request, so a changed kind, input or natural key diverges rather than consuming +the result retained at the same position, and the provider's answer is parsed at +the terminal before the durable executor returns, so a raw payload becomes a +fixed protocol failure rather than journal content. -- **printed error** — printed into the document; data -- **failure** — propagates until middleware handles it or the document - execution fails +Some effects need live local state the frozen request cannot describe, and that +travels as an object-source attachment rather than as an input. A push needs the +Git objects its commit is made of and the destination it may reach; both are +built by the trusted host for one reconciliation, authenticated against the +retained identity they belong to, and held in the selected provider's closure. +Nothing about an attachment reaches the durable request, the natural key or the +routing surface, so what discriminates the effect stays the filtered identity a +journal may hold while what performs it stays where no replaceable code can +address it. -### 1. Middleware wraps the work +What an attachment grants access to is authenticated separately from where it +came from, because local state can name further local state: a Git object +database names further ones through its alternates chain, and a symbolic link +under it redirects a read before Git reports anything about it. Such a graph is +read in the consuming tool's own grammar rather than as text: Git unquotes a +C-style quoted alternates entry before resolving it, so a containment proof that +compared spellings could be satisfied by a decoy of that literal name while the +traversal went elsewhere. The graph a +provider could traverse is therefore proven contained on the live provider's +first use of the attachment — before any external observation, and on no other +path, so a completed replay reaches no provider and performs no such proof. A +graph that leaves what the run authenticated is a boundary failure: it publishes +no outcome, repeats no authored path, and activates fail-stop. It is rejected +rather than repaired, because editing authored state to make it acceptable would +publish from state the run wrote on the author's behalf. -Error handling, retry and suspend behavior is controlled by middleware applied -by the lexical structure. Middleware holds the operation, so it can run it -again. When the operation errors, middleware may: +That same contained source is what decides whether a branch may advance. A +destination naming another commit is not by itself somebody else's work: a +supervised run publishes one branch repeatedly, and the ordinary case is a +remote tip the commit being published was built on top of. So ancestry is proved +where the objects already are — inside the authenticated source, out of the +commits it already holds, by one local question whose only answers are ancestry +and divergence. A proven ancestor other than the commit being published is a +performable pre-state, published over by the same exact non-force refspec a +proven absence gets; an equal destination is adopted with nothing performed, and +is never an attested advance, because a commit is its own ancestor and observing +the destination at exactly this commit is what adoption is; and compatibility +that was not proved — a divergent commit, or one the source does not hold — is a +conflict. Nothing is +fetched to prove it, because an object retrieved to answer the question would be +evidence the question created, and a proof that reaches neither of its two +answers is infrastructure rather than a finding about the remote. The relation, +not the traversal that found it, is what the record keeps. -- retry it — including after fixing what failed (a new token, a changed input) -- suspend, wait for input, then retry -- bind it — `` converts the failure into a bound `{ok: false, error}` -- decline — the error emerges at raise +The Workspace transaction such an effect opens is read-only and is +closed before the host is observed: no transaction is held across the +Workspace and the external service, and reconciliation rather than a distributed +transaction is what makes an interrupted attempt reach the host once. -Middleware converts an error into success and prevents the raise: the output -shows no error at all, and the attempts live in the journal. The set of -actions is closed so every one is journalable. +A provider that needs a program must also not inherit configuration the run +merely stores. A checkout's `.git/config` is retained document data, and several +ordinary settings in one name a destination or a program — a push URL, a URL +rewrite, a hook, a credential helper, an SSH command, a signing program. The +Deno push provider therefore runs its transport in a disposable repository of +its own that reads the selected checkout's object database through a read-only +alternate, and aims it at the exact private retained locator rather than at a +configured remote. The two settings that would otherwise choose how a remote +operation authenticates — `credential.helper` and `core.sshCommand` — are fixed +on the command line, where they outrank every configuration file, for every +invocation that transports to a remote. -```md - - - - - -``` +#### Invocation-owned authentication -### 1a. One element's expansion can be surrounded +Authentication itself is the one live host input such an invocation borrows, and +it is not the same question as inherited configuration. Retained state says +which repository an effect belongs to; it must never say who the run is, because +an identity belongs to the machine a run is standing on and a run resumed +elsewhere authenticates as whoever is there then. So one live provider +invocation — a Repository creation, a Push reconciliation, a pull-request or +Issue call — opens one authentication session lazily, for its own exact retained +locator, after the admission checks that operation requires. Every native +command that invocation runs attaches that same session, so an observation and +the mutation it decided go out under one identity rather than two, and the +session is disposed with the invocation. A later attempt on an interrupted +request opens its own. -`Component.expand` offers an observer the whole of one executable element's -expansion: before resolution and props validation, through the accepted body -and its owned cleanup, to the acceptance of what it produced. Core issues a -request and the public chain composes around it; a handler delegates that exact -request once with `next(request)`. +The initial GitHub PullRequest adapter obtains its live credential from +`GH_TOKEN`, then `GITHUB_TOKEN`, then the host's `gh` login. -The authority is the shape bound execution already uses. The request is branded -with a private field, so a copy carrying the same members is not it; the request -is spent once, so a repeated delegation runs nothing; and the settlement is -readable whether or not the chain returned normally, so a handler that catches -what canonical expansion raised has not rescued it. A handler that returns -without delegating refuses the work, and what a handler returns is ignored. +The trusted Deno host adapter retains one HTTP credential in memory for one live +provider invocation and exposes it only to that invocation's own Git and helper +children, through private child-process environment. A provider-owned helper is +what Git runs, and it answers one exact locator, so a redirected transport +receives nothing and fails closed — and the transport declines to follow a +redirect at all, since Git would otherwise carry a credential it already holds +to a destination this run never authorized. `store` and `approve` reach no ambient helper +or store; `erase` for the exact locator writes a fixed, nonsecret marker, which +is how a refused identity is told from an absent one. Both are authentication +unavailability, never an invalid locator. The containment is only worth what the surface around it allows: a substituted +Git host sees every invocation, and an authenticated one carries the attachment +the credential travels in. So the published Deno entrypoint offers a Workspace +attachment that accepts what a host owns and nothing that could observe or +become that host; the broad options stay inside the package. -Observation reaches no outcome. Phases carry `Result` and nothing else — -what the element produced, what it bound and which error identity core is -holding stay private — and a failure is reported as a detached, frozen `Error` -preserving the selected name, message and explanatory causes. Completion is -published only after the whole dispatch, middleware cleanup included, has -unwound and canonical acceptance is reconciled. +Infrastructure is only what the adapter +watched fail: starting acquisition, installing the launcher, reading the marker +for any reason but its absence, and removing what it made. The absence of a +signal from the helper proves nothing — there is no readiness, preflight or +channel to it — so an acquired credential with no rejection keeps the ordinary +transport classification rather than acquiring a new one. -Each subscription is registered with the element's latest phase and then told -each change in order, ending after exactly one terminal observation. A reader -that is slow, absent or cancelled delays nothing and cancels nothing: execution -never waits for an observer, and a consumer that must see the terminal phase -belongs to an owner outliving the dispatch rather than to the handler's frame. +A session opened for one locator never authorizes another, nothing about one is +retained, and a completed replay opens none at all because it performs no remote +operation to attach one to. A missing or rejected credential is ordinary live +refusal: it proves no absence and adopts no completion, and a transport that +carries an identity attempted with no mechanism to prove one is refused under +its own word rather than as a locator that names nothing. -Every path that expands something an author wrote crosses this once: the -component paths, and the structural constructs ``, ``, ``, -`` and ``, plus the selected ``, the `` that runs and -each `` an `` starts. An unselected branch expands nothing and is -told nothing. +Only an answer the terminal accepted decides the effect. A conflict, a permanent +ambiguity and a temporary unavailability so accepted publish the effect's failed +result and replay as the same fixed local failure, rebuilt from its closed name +rather than recognized by identity across loaded copies. Everything else that +can raise before an answer is accepted — routing middleware, the provider +handler, the provider's own body, an unsupported effect kind — publishes nothing +and activates fail-stop under one fixed cause-free failure that repeats none of +what was raised. The attempt decides which of the two it has by the identity of +the failure it authored itself, never by a name or a class received from +replaceable code: otherwise a handler could retire an effect as conflicted +without a Git host ever being asked. Fail-stop rather than a bare refusal, +because a live operation that appended nothing would leave the next one to +append at its position. -### 1b. Where a text's elements are, without running it +#### Issue effects -`inspectSource(text, kind)` answers where each recognized element's delimiters -are and what it is called. It is the scanner's own reading, collected by the -same walk that decides what a fence, an inline code span, a quoted `>` and a -tag-like expression are, so there is no second grammar to disagree with the -first — and a nested collector is committed only with the parse that found it. +An authored PullRequest `number`, when present, is a positive integer. Its +[feature contract](specs/workflow-workspace-spec.md#75-pull-request) owns the +remaining props, result and publication-evidence refusals. -A document's body boundary is read lexically, matching the installed -extractor's envelope rule without calling its value parser, so inspecting a -document interprets neither its header nor its body. Definition parsing asserts -the two boundaries agree. +An Issue read returns `{ url, title, description, tags, assignee }`; an upsert +returns exactly `{ url }`. These are the [closed feature results](specs/workflow-workspace-spec.md#103-issue-effects), +not Git-host reconciliation records. -### 2. Decide once +Issue creation is a separate boundary, and not that one. Everything above is the +Git-host contract: a shared state machine, one request-only surface, phases, and +a private terminal that is the only thing able to author an outcome. `` +has none of it, and none of it is missing. -An error no middleware handles is decided exactly once, where it is raised, -by the region's error mode — and the two outcomes move differently: +An Issue provider does not necessarily own a Git repository — Atlassian issue +tracking owns none — so an Atlassian issue could not truthfully execute or +persist as a Git-host effect. `` therefore reaches its own stable +contextual operation, `executablemd.git.issue`, whose members are +`read(url, options)` and `upsert(issue, options)`, and journals its own durable +effect type, `issue_effect`. What is Issue-owned, and therefore an Issue +compatibility boundary, is that API name, the two normalized requests, the +idempotency key an upsert derives, the results and the effect type. -- A failure propagates by throwing up the scope tree, tearing down as it - goes, until middleware handles it or the document execution fails. -- A printed error does not propagate. It is printed into the document — data. +The difference is not only which name is called. A Git-host provider is selected +by the reconciliation and answers through a terminal it cannot address directly, +because the state machine is shared and the journal may hold only what that +terminal accepted. An Issue provider is ordinary middleware: it composes +`IssueApi.around(...)`, reads the request it was handed, and either handles it or +delegates it untouched. It answers that question independently for each member, +so recognizing a read is not recognizing an upsert. There is no registry, no +resolver, no host-side URL resolution, no phase protocol and no terminal. What would have been the state machine's job is +the middleware's instead, because knowing whether an issue already exists is +knowledge about one service rather than about external effects in general, and a +shared machine would have to be told it by every adapter that disagreed. -A decision is final in both directions: nothing turns a printed error back -into a failure, and execution does not continue past a stop — re-executing a -whole region starts a new attempt. Reacting is allowed: anything that can see a -printed error may raise a new failure in response, decided once at its own raise -point. +So the Issue boundary keeps little for itself: it derives an idempotency key +from the canonical target and the run's own effect identity, wraps one call +durably, validates that what came back is a URL and nothing else, and retains +it. Replay answers from the journal without calling the operation at all. What +it does not promise is that the service was touched once — observing before +mutating, adopting a compatible issue, creating once, recovering an interrupted +creation and refusing conflict and ambiguity all belong to the middleware, and +the key is what this side hands over so the middleware can make it true. -### 3. Error mode is lexical +An IssueTracker URL is canonicalized before use. Credentials, a query and a +fragment are refused rather than stripped, so normalization cannot silently +turn the authored target into another request. -The nearest error mode in the scope tree governs an undecided error. A mode -is a context value, so it reads from the document's structure (see Two -layers). +Which external system an issue is requested in is lexical context, and whether +that target is authorized is the provider's. The nearest tracker supplies a +canonical target URL and an optional provider discriminator; the adapter-private +ceiling installed beside the credential decides whether that target may be +reached at all. A tracker narrows within the ceiling and can never widen it, +which is what keeps a replaceable context from being a grant. -| Mode | An undecided error… | Installed by | -| --- | --- | --- | -| `print` | is printed; the document execution continues | ``; `printErrors(fn)` | -| `output` | fails the document execution; `` can print instead | every text root; every `` region | -| `throw` | fails the document execution, even inside `` | documentation; value roots | +Without a discriminator each provider matches its own URLs; with one, only the +provider of that name may act, which is how a self-hosted deployment is +addressed. Once middleware matches it owns the answer: it never delegates +afterwards, and nothing catches its refusal to try somebody else, because a +search is how a document that named one service quietly reaches another. A +destination every provider delegated reaches the operation's own base error. -Printing is asked for. A text root installs `output` for its whole body, so an -uncaught, undecided failure is the run's own outcome whether or not the document -declares ``. `print` is the low-level default the context carries -outside root execution — where direct expansion and component-level harnesses -read it — and inside a run it is what a printing boundary establishes for its -region. +Every committed journal event references the current logical Workspace root. +Only committed event boundaries are checkpoints. A history fork copies the +selected root and the roots the inherited prefix names into the new run, replays +the inherited prefix under a compatible modified definition and appends new +history under a new run ID. It never rewinds external branches, pushes, pull +requests or provider state. -The mode's name decides nothing on its own. `` selects which regions render, -and it selects nothing at a root that declares none; root settlement decides -what an uncaught failure means. That the two share one internal mode says they -settle a failure the same way, not that `` owns the decision. +#### Git-host evidence reads -Projections carry the caller's error mode; they never set their own. +A Git-host evidence read addresses one canonical pull-request URL, without +inferring a Repository. It returns a bounded collection only after every page +has been read and parsed; transport, pagination and shape failures never become +an empty or partial array. The provider may authenticate its request, and the +host's read ceiling is checked before opening that authentication session. -### 4. `` prints a failure +Reading what a Git host holds is not reconciling what it owns. A reconciliation +exists to mutate a remote at most once across interruption, and everything it +carries — the natural key, the pre-state, the observe-then-perform pair — is +there to decide whether an attempt already landed. A read decides nothing of the +kind: repeating it is safe, and a completed one restores from the journal. So a +Git-host evidence read is an ordinary durable effect, and the machinery that +makes a mutation exactly-once is deliberately absent rather than reused where it +would only add a second identity to keep in agreement. -The nearest `` or `printErrors(fn)` turns a propagating -failure into exactly one printed error whose `cause` is the complete original -failure, and sets `print` for its region. `printErrors(fn)` is keyed to the -exact function object — nothing is shared by name. Inside documentation a -failure still ends the document execution — a hidden region gives the author -nothing to read. +What such a read must not do is answer partially. A collection the host +completed and a collection nobody could finish reading are opposite facts, and +only one of them is `[]`; a truncated list would report the first while meaning +the second, so an incomplete walk fails the read and retains no array at all. +Both questions about a pull request — what it holds, and what it should say — +are asked of one surface, and a provider is ordinary middleware around it. That +is the Issue surface's topology, adopted deliberately: an adapter recognizes the +URLs it can act on and delegates the rest, and once it matches, its validation +and its refusal are final. A refusal ends the request rather than starting a +search for another provider, because a search is how a document that named one +service quietly reaches a different one. -A component's declaration speaks for the component. `` is written -by the author of the region it encloses and decides everything written there; -`printErrors(fn)` is written by the author of one component and decides what -that component does, never what a caller wrote inside it. Content a caller -projects keeps the mode of the region it is written in (rule 3), and a failure -of it the component does not recover from passes outward wherever that region -does not print. +The bound here is the operator's `allowed` list, checked before a session is +opened, a credential is read or anything is sent. It bounds reads alone: an +upsert names a branch this run published rather than a URL a document wrote, and +what admits it is this run's own matching Push evidence, which no configuration +grants or withdraws. "Permission ceiling" is the word for the bound; what an +operator writes is the list of places this host is allowed to reach. -**Describing a failure is not owning it.** A component that could not use its -caller's content may still have something worth saying about what it therefore -did not do — a file that was not written. Saying it does not move the decision: -the region that decided the failure still settles it, and the component's -sentence travels as an account of that decision rather than in place of it. -Only a decision the region has not made is the component's to make, which is -why content that merely *printed* its errors leaves the component free to -refuse on its own terms. The distinction is carried explicitly by the failure a -component throws, never inferred from what a cause graph happens to contain: a -component that recovers and then fails on its own terms owns that failure, -however much of the content's wreckage is still reachable beneath it. +A replayed effect that has to name what it asked for reads the moment it asked +from. A Push names its request out of the checkout, and a run that publishes one +branch repeatedly has moved the Workspace on by the time it is resumed — so each +completed Push is reconstructed from the root its own journal event was appended +against, which is the association the run made when it wrote that event, rather +than from the frontier the run reached. That reconstruction is a read: the +historical root is materialized, exported from, and discarded on every path out, +leaving the current root, the retained roots, the metadata and the journal +exactly as they were. It decides only which question is asked, because the +request it produces still has to be the one that position asked before the +retained record answers it. -### 5. Raise decides by value +#### Retained effects and history forks -An error that emerges from the middleware is raised where it stands. Raise -has exactly two outcomes, decided by the region's error mode: printed, or -failed. At raise, middleware observes only — it sees each error exactly once, -where it was raised, and changes nothing. Control at raise belongs to the -mode, never to middleware. +An external effect is judged by its retained record rather than by its type. A +completed Git-host reconciliation record replays without contacting a provider, +so a fork inherits it and the remote is mutated once; the record keeps the +identity it was written under, and the effect at a position the history already +holds a record at is named by that identity, while every live attempt is named +by the run performing it. The identity is established when the engine +admits the run's retained snapshot and is published beside the run, so every +physical copy of the workflow package reads one answer. What authorizes live +execution is not that answer: a coroutine that walked away from retained history +it never consumed performs no Git-host effect for the rest of its run — the +engine's own account decides it, at every position after the divergence — so +removing or replacing the transported identities can only make the effect replay +correctly or refuse. It belongs to the exact retained event it +came from, at the position that event occupies: records are offered in order, +a call may take only the next untaken one and only when it asks exactly what +that record answers, and a request named by a retained record that reaches live +execution performs nothing rather than performing under another run. A Git-host event that settled into no such record +leaves the remote's state unestablished, and that checkpoint is not forkable. -Nothing becomes a printed error or a failure without being raised: whoever -creates an `ErrorSegment` raises it. +A fork inherits the source's normalized props as its baseline; explicit +generated arguments add to or override them, and the merged set is validated +against the candidate definition before anything is created. Those props are +what the compatibility replay runs under, what the run retains, and one of the +terms a reused fork ID is compared on. -### 6. An error is never a value +A fork writes two records for itself and inherits the rest. Its own +`workflow_run` record is what its journal is held to, and its own root import is +what makes the run a run of the definition the caller named — that record holds +the document's text, so inheriting the source's would run the source's document +whatever was asked for. Everything after them travels unchanged, keeping its +public event ID, its filtered result, its authored position and its Workspace +root, and carrying the source run and source event it came from. Retention moves +with the fork: once the fork commits, deleting the source leaves its prefix and +its selected root exactly as readable as they were. -A binding (`as=`, ``, ``) refuses a body holding -a printed error. Holding a failure as data is asked for explicitly: -`` binds the Effection `Result` shape — `{ok: true, value}` with -the value by reference, or `{ok: false, error}` — converting the failure -before any raise. Private buffers never merge into document output. +Compatibility is decided before any storage the host would recognize as the new +run exists. The candidate replays the inherited prefix against a staged copy of +the fork — the whole run, assembled where nothing discovers it — and the replay +ends at the checkpoint, before the first live effect, rather than after it. A +divergent candidate leaves no fork. After it succeeds, one transaction commits +the run, its lineage, the inherited prefix, the copied roots, the selected +current root and the first document execution together. -A command's exit status is not an error until something decides it is one. -`exec as=` binds the settled process outcome — the status and both channels the -per-exec boundary received — so a nonzero status is ordinary data and there is -no error to raise, in any error mode. What never reaches a settled status is -unaffected: a process that could not start, a timeout, cancellation, a -durability failure and a Files infrastructure failure are each the failure they -already were, and bind nothing. Buffering both channels for that binding is a -document-owned buffer and not a retention decision; what a run keeps durably -stays the trusted host's. +### Local Workspace topology -### 7. A blocker is not a failure +The local workflow host owns SQLite directly in Deno and reuses Cloudflare's +DOFS filesystem layer behind the provider-neutral Workspace boundary. One +authoritative provider-owned connection entry serves each canonical workflow +database path until provider teardown. The journal and DOFS adapter use that +same SQLite connection; Cloudflare's synchronous initialization transactions +become uniquely named savepoints inside XMD's caller-owned transaction. Those +savepoints and operation-spanning savepoints share one allocator and cannot +collide or release one another. After a savepoint successfully rolls back and +releases, the shared rollback path invalidates both caches on the authoritative +DOFS wrapper before outer transaction work resumes. A second long-lived DOFS +connection is not a coherent reader because provider +caches may retain negative entries across another connection's commit. -A workflow run waiting for input — auth, a human answer, days if needed — has -not failed. Suspension is a durable effect: the journal records up to the wait, -the process may terminate, and a later document execution arrives back at the -same wait. An error that reveals a blocker (an expired login) reaches suspension -through middleware; waiting itself is never raised. +Complete schema version 1 retains the canonical empty Workspace root and its +root-only live frontier initially. It retains arbitrary canonical roots and can +materialize one privately through the authoritative connection. Capture runs +inside the caller-owned transaction. Restoration runs in a nested savepoint, +clears the authoritative resolution and blob caches, and resnapshots to the +selected identity before release. Private Workspace transaction bodies finish +their child teardown before final live/current validation. The Workspace effect +coordinator also finishes its mutation savepoint scope before it invokes +capture. -### 8. Durability and Files infrastructure failures are outside the model +Separately, every unsuccessful caller-owned transaction attempts top-level +SQLite rollback and then +invalidates both caches on the provider-owned DOFS wrapper while it still holds +the serialized connection turn. This includes body failure, cancellation, +teardown or final-validation failure, and commit failure. The surviving wrapper +therefore cannot answer from uncommitted positive or negative cache entries +after SQLite has restored the prior frontier. -A durability failure (§6.11) says the journal no longer describes the document -execution. No middleware sees it; it is never the document's own outcome. A -backing-journal append failure is the same kind of boundary failure: it -preserves the adapter error as its cause, resumes no consumer with an -unpersisted success, and never triggers a compensating `Close`. The first -durability failure is shared by the root and every durable child. From that -point, durable entry fails with that exact error before replay or execution, -and the ordered append boundary rechecks it immediately before storage. Work -already executing may finish. Concurrent work that began earlier cannot be -undone, but its not-yet-started append is fenced. An unmarked backing-stream -rejection is a persistence failure regardless of the error class the adapter -throws. A pre-persistence policy rejection remains the policy's ordinary -document failure; the guarded stream marks that boundary before the backing -append and does not activate the fail-stop state. +Retained roots, manifests and blobs remain indefinitely. Cloudflare garbage +collection is not in the production closure and is never invoked. The provider +exposes no public history selection at this layer; a fork's root copying is a +lifecycle transaction above it. Its +coordinator combines one mutation, immutable-root publication and one filtered +journal result atomically, and the transaction-bound Files provider is what +routes a document's `` and `` to it. `xmd workflow start` and +`xmd workflow resume` install that provider around each execution. -A Files infrastructure failure is outside the model on the same terms. A -missing provider, a refused operation, and a provider that broke its own -contract are none of them things the document did or can act on, and printing -one would let every step after the file work run as though the file work had -happened. No middleware converts one, and no printing boundary prints one. +The coordinator treats only errors produced through its private filesystem +adapter's documented path and mutation refusals as journalable operation +failures. It rolls their savepoint back, keeps the prior current root, and +commits one failed protocol Yield. Connection, savepoint, content, capture, +routing, filtering, insertion, teardown and commit failures roll back the outer +transaction and activate the durable run's first-failure fence. Cancellation is +control flow: it publishes no failed result. The durable coordinator contract +supplies a provider-neutral failure activator so a provider can retain that +first infrastructure failure by identity; the default coordinator does not use +it and keeps its existing behavior. -Both are discovered through one cycle-safe traversal of the whole cause graph, -and precedence is decided by kind rather than by position: a durability failure -first, then a Files infrastructure failure, then a documentation failure. The -selected failure comes back by identity, because a fail-stop that records "the -first error" has to record the one that happened. +Losing the host is not one of those outcomes, because nothing runs to handle +it. No cleanup, commit or rollback happens after the process dies: the +operating system closes its connection and releases the locks it held, and the +interrupted transaction is left for the next write-capable source connection to +recover. Read-only lifecycle inspection may instead recover a private copy under +the coordination described above; it never takes recovery away from that next +source owner. The mutation, the immutable root, the current-root pointer and the +routed event have all been written by then, and recovery exposes the last +committed state and none of them. -The two kinds are *recognized* by different mechanisms, and the difference is -deliberate. A durability failure is recognized exactly as it always has been — -by class, against the failure types durable-streams exports — and that is -unchanged. A **Files** infrastructure failure is recognized structurally: a -stable tag on frozen data, the fixed diagnostic for its kind, and no cause, -never `instanceof`. It has to be, because the Files boundary is the one a -separately loaded copy of a package reaches across, where `instanceof` answers -false. Recognizing one is also a decision to let that exact object travel -onward, so a candidate carrying anything else — a raw message, a cause chain, -an extra field — fails the contract and is replaced by a fresh invariant rather -than preserved. +That is the same boundary every other reader already observes rather than a +second rule for crashes. A second connection sees the last committed state for +as long as the writer's transaction is uncommitted, so a crash publishes +nothing that was not visible before it. What a later process finds is that +committed state — the filesystem, the current root, the retained roots and +references, and the ordered journal with its event identities and root +associations — from which it performs no recorded effect again and can +materialize any retained root. -The traversal reads values it did not create, so every read in it is total: a -thrown Proxy or a failing accessor narrows what discovery finds instead of -replacing the failure being classified. +The Deno journal adapter routes an append ordinarily when no destination is +bound. A publication may instead bind one exact transaction destination for its +own lexical scope after the existing secret gate. The route validates the +database lease, connection generation, transaction identity, token and open +state before delegating to the existing `transaction.journal`; it contains no +insertion SQL of its own. A nested route for another run delegates past itself, +and `readAll()` always follows ordinary replay without routing or secret +filtering. The provider-owned ordinary destination and each publication-local +routed destination are terminal `{ at: "min" }` handlers, so an enclosing +loaded copy with the same stable contextual name cannot suppress an append or +run ahead of exact-token validation. + +The initial topology requires neither writable FUSE nor native subprocess +access and does not bundle `workerd`. A Cloudflare-hosted or workerd-backed +provider may install the same contextual contract without changing documents or +lifecycle commands. + +Worker JavaScript is outside the initial local capability set. Worker Shell is +inside it: one `just-bash` invocation runs in a Deno Worker through Cloudflare's +Workspace filesystem adapter. The host begins one immediate SQLite transaction +and one effect-local mutation savepoint. Success releases the savepoint, appends +the already-filtered result and commits. Failure, timeout, cancellation and +Worker termination roll the mutation savepoint back, append the failed result +and commit. A host crash rolls back the still-open transaction entirely. + +Every Worker filesystem request carries the effect identity and a +per-invocation token. Missing, foreign, cancelled, completed and stale requests +are refused. Cancellation must be able to forcefully terminate a CPU-bound +interpreter; graceful Worker shutdown alone is insufficient. Worker Shell +exposes no native executable or host PATH and is not described as POSIX or +native Bash. + +### Replay-safe live services + +An attached service belongs to the document execution that attaches it. The +host owns port selection, process spawning, XMD service handshake +authentication, supervision and teardown. Shared runtime code reaches that +behavior through the provider-neutral `API.Service`; it never imports a host +process or networking API. + +The service publishes a frozen loopback endpoint as a live binding. Live +bindings form an overlay on the component's durable eval bindings: `ephemeral +eval` reads both and may add live bindings atomically, while ordinary `eval`, +code-block interpolation and journal serialization read durable bindings only. +A live binding cannot shadow a durable binding, and a durable value cannot be +replaced by a live one. + +Service attachment and `ephemeral eval` execute again during partial replay so +the current process and middleware chain are reconstructed. A completed +document replay returns its recorded result without expanding the document and +therefore starts no service. Ordinary durable eval transforms a block and +validates its declared exports against the live overlay before constructing its +durable effect. A collision therefore has no eval `Yield`; compatible partial +replay remains aligned, while retained history containing a now-incompatible +successful eval is rejected by the existing replay guard or divergence path. +Every root and child termination checks its retained subtree before appending a +`Close`. The replay index tracks which coroutine identities the current +definition uses, so a completed retained child is not mistaken for aligned +history merely because it already has a `Close`. Durability failures take +precedence over ordinary termination even after the last retained `Yield` was +consumed. If collision handling terminates immediately, +`TerminalDivergenceError` retains the collision as its cause and the history +receives no terminal event; restoring the compatible definition can still +replay it. + +`xmd workflow start` and `xmd workflow resume` install the non-delegating +`useWorkflowServiceDenial()` provider inside each execution scope, before the +root document is imported — in the same place `xmd run` installs its host +service adapter. The provider prevents a workflow from reaching an inherited +host adapter, because a run-owned durable service requires stable identity and +reconciliation rather than an execution-owned live process. + +## Files and repositories -## The document filesystem boundary +Document filesystem operations cross a provider boundary. Ordinary managed checkouts have a different lifetime from a workflow filesystem checkpoint. + +### The document filesystem boundary Contextual routing is not permission. `API.Files` decides *which* provider answers a document's file operations; what a provider is allowed to do is @@ -3091,6 +2825,8 @@ restores the enclosing cwd on success, failure and cancellation; restoration never deletes the directory. The Repository selection in scope is unchanged — Dir places content in a directory and selects no Repository. +The mandatory `API.Files.deleteFile` operation owns removal. + Removal is one of those acts, and it is mandatory: a provider does not omit it and let a document reach the host instead. Its success is Effection's `Unit`, carrying no value at all, which is what makes "a deletion hands back no receipt" @@ -3161,577 +2897,1360 @@ message, errno code, resolved path, temporary name, or symlink target crosses. Consumers parse that data before reading a field; a write whose data does not validate is a provider-contract failure rather than a commit state to invent. -## Attempts +### Ordinary repository ownership + +An ordinary execution borrows persistent checkout bytes through invocation-owned +resources. Releasing the execution releases its locks and evidence; it preserves the +checkout. These rules complement the [repository +interface](specs/workflow-workspace-spec.md) and [workflow effect +transactions](#replay-and-effect-transactions). + +#### Managed checkout reuse + +An ordinary Repository or Worktree selects a managed checkout under `~/.xmd/repositories`, +addressed by a digest of its whole identity and described by a closed version 1 sidecar +written by exclusive temporary sibling plus atomic rename only after the checkout is +complete and verified. The slot is entered under an exclusive non-blocking advisory lock +held for the whole document execution, so a second process is refused rather than made +to wait and a self-closing Worktree captured with `as` stays protected while a +later sibling `` and an interactive Session use it. Reuse compares creation +identity alone — the immutable request, the recorded creation facts, the canonical +checkout and common directory, the object format, the admitted `origin` and the +creation commit still being present — and never HEAD, the current branch, the index or +the working tree, which are the mutable work the checkout exists to preserve; a conflict +refuses and leaves every byte where it was, and nothing resets, switches, cleans, +fetches, moves, replaces, repairs or deletes. A metadata-free slot is adopted only after +the stricter pre-exposure state is proved — exact owner and locator, the branch and base +this request resolves to, the creation commit still being HEAD, the object format, +linked-worktree registration where applicable, and nothing in the slot but the checkout +— and refuses otherwise. Written outside a lexical ``, a Worktree belongs to +the ambient Repository; outside a Git checkout it refuses locally and names how to run +inside one. + +#### Local transitions and Git identity + +Ordinary local Git operations perform the same authored transitions the workflow +performers perform — named branches only, explicit Add pathspecs, index-only Commit, no +implicit stage or push, and the same fixed provider Git configuration that disables +hooks, signing, file-system monitors and repository-supplied helper programs — directly +against the authenticated selected checkout. A commit records the invoking user's own +effective Git identity, captured from the trusted host by the first `` that +needs it and read back off the written object — a run that commits nothing asks the host +nothing about who it is; a host that can name no identity refuses `` and names +the commands that fix it rather than writing the workflow identity into somebody's +repository, and every other component stays usable. They enlist in no transaction, roll +back nothing and replay nothing, and a failure promises neither. Which checkout one runs +in is decided by the Repository selection in scope and the contextual working directory, +resolved through the provider's own invocation-owned checkout registry rather than +through anything the selection says about itself. + +#### Publication evidence + +An ordinary Git.Push keeps the same observe/adopt/fast-forward/refuse rules and the same +isolated transport aimed at the checkout's admitted `origin`: a destination proven +absent is published once, one already naming this exact commit is adopted, one holding a +proven ancestor is published over by the same exact non-force refspec, and a divergent +or unreadable one is a conflict, with an unreachable host never read as absence. It +reconciles no Git-host effect and retains nothing. After a verified performed or adopted +publication it stores one private evidence entry — the authenticated Repository +identity, canonical checkout root, origin, named branch, destination ref and exact +commit — in the provider instance's own closure. A checkout with no admitted +`origin` refuses before a credential, a session or a transport exists. + +#### Pull-request admission + +Ordinary PullRequest upserts and evidence reads share the URL matching, host ceiling, +response normalization and low-level GitHub reconciliation, and differ in lifecycle and +admission. A read is performed afresh every execution and retained nowhere. An upsert +authenticates the Repository selection and the contextual checkout, reads the current +named branch and commit, and requires the exact matching entry this provider instance +already holds — a Push for another checkout, Repository, origin, destination, branch or +commit is irrelevant, a later Push of the same destination supersedes the earlier entry, +and missing or conflicting evidence is a local refusal before a credential is opened. +Nothing crosses executions: a new run and a new `--journal` run each start with a new +invocation identity and empty evidence, and copying a Context value, a component result +or a previous trace file grants nothing. Within one invocation the attempt happens at +most once; across a process interruption there is no exactly-once guarantee. + +#### Issue identity + +An ordinary Issue reaches the same configured transport under the same host ceiling, +with no durable envelope: identity is this execution's own opaque invocation identity +together with the engine's expansion identity, so an upsert presents an idempotency key +a provider can carry and a second run is a new request rather than a resumption. Absent +or out-of-ceiling configuration installs no matching provider and sends no credential +and no request. + +#### Lazy provider resources + +The Deno source entrypoint and the compiled binary install the live provider for +`xmd run`, parameterized by the same credential-helper assembly the workflow host +uses. It reads no configuration: `XMD_WORKFLOW_GITHUB_ISSUES` and `XMD_WORKFLOW_GITHUB_PULL_REQUESTS` belong to the GitHub +adapter inside `@executablemd/git`, which reads and validates one when an invoked GitHub-backed +operation needs it, so a run that opens no pull request and files no issue reads +neither. Installing the provider acquires nothing at all — no managed root, no lease, no +Git session, no identity, no ambient discovery; each of those is acquired by the first +operation that needs it and kept for the rest of the execution. A nested `` +child receives a fresh instance — its own invocation identity, leases and Push evidence +— so nothing it publishes authorizes its parent or a sibling. The outer `xmd test` +command installs none — it is not a run profile, and a nested `` child +assembles one for itself rather than inheriting what its parent declined. Node and Bun +register the vocabulary and install no operational provider, so every repository +operation reports an absent provider before a lock, a credential, a subprocess or a +request exists. + +#### Ambient discovery + +The ambient Repository is the repository an ordinary `xmd run` was started inside, +discovered when the first element asks for one — never at installation — from the +invocation's starting directory, and remembered for the rest of the execution including +when there is none. Its identity is the canonical common Git directory and its selected +checkout is the canonical checkout root, so starting in a linked worktree names the same +repository as starting in the primary checkout while Git operations still act on the +worktree. A workflow run has none. + +#### Managed bytes + +A managed checkout is a Repository or Worktree an ordinary `xmd run` created under +the host root `~/.xmd/repositories`, addressed by a digest of its whole identity, described by a +closed version 1 sidecar written beside it, and held for one document execution by an +exclusive non-blocking advisory lock. It survives every execution: nothing deletes, +resets, cleans, fetches or repairs one. + +#### Commit identity + +Ordinary Git identity comprises the invoking user's effective Git author and committer +name and email, read from the trusted host's own environment and configuration by the +first `` that needs one — never at installation — and kept in the provider's +closure for the rest of the execution, including when the host can name none. A run that +commits nothing never asks. It is used for an ordinary `` and nothing else, +because that commit lands in the caller's own checkout; a workflow run keeps its one +fixed identity, whose whole purpose is that retained Git state does not depend on whose +machine made it. It is not a prop, a Context value, a component result or a middleware +answer, and nothing else about the environment is borrowed with it — hooks, file-system +monitors, signing programs and repository-supplied credential helpers stay disabled by +the same fixed command-line configuration. A host where Git can name no identity refuses +`` with an actionable sentence rather than substituting the workflow one; +every other component is unaffected. + +#### Invocation identity + +An ordinary invocation identity is a fresh opaque random value the document execution's +repository provider mints for itself and keeps in its own closure. It is not a prop, a +Context value, a component result, a middleware answer, a lifecycle ID or a retained +record, and it is neither addressable nor reusable; live Issue and pull-request +idempotency and reconciliation keys are derived from it together with the engine's own +expansion identity. + +## Agents and generated work + +An Agent proposes source; trusted execution admits and performs it. Session continuity, generated admission and Plan recovery have distinct owners. + +### Native Agent sessions + +A native handoff gives one exact retained conversation to the provider UI. Its +construction identity, executable compatibility and live owner are separate facts. Read +these rules together with the [native launch +interface](specs/native-agent-session-launch-spec.md) and [ACP provider +contract](specs/acp-client-spec.md). + +#### Handoff, construction and executable compatibility + +A native session launch prepares one durable coding-agent session from the rendered body +of `` and hands the provider's native UI the terminal for that exact session, +then continues the document after it exits. The body renders completely first and only +what it rendered crosses as the instruction layer; the launch performs no model turn +except an adapter's declared materialization turn, which runs at most once, only for a +conversation the launch just created, and only with that adapter's fixed prompt; it +takes the run's foreground-terminal lease before an agent is resolved. A host with no +applicable terminal refuses without probing for an installed CLI. A session is +constructed once, by one of two mechanisms, and its create-once construction route says +which. + +Where the provider returns the identity, the ACPX provider creates the session, installs +the layer at creation, releases ACP ownership before the spawn, and marks its handle +stale so a later `` reattaches. + +Where the adapter names its own sessions, it allocates the identity inside ownership +before any process exists, the native process creates the session under that name from a +private mode-0600 instruction file, and ACP creates nothing — the instruction text +reaches neither argv nor environment, and the file is removed on success, failure and +cancellation alike while ownership is still held. + +Neither route converts into the other, and which one governs is chosen by the first +operation that consumes the placement rather than by the `` that made it: a +fresh `` publishes no route and establishes nothing, so a `` nested +inside one constructs the session it placed, while a first subscribed `` +publishes ACP-first before it ensures and keeps that account even if the turn that +follows is never accepted. An established route is validated eagerly by a later +``, and a launch meeting a published ACP-first route refuses before an +identity exists. A `` or `` meeting a bound client-allocated route +attaches under the route's exact identity; a legacy unbound route or an unavailable +attachment capability refuses before a turn and creates no substitute conversation. + +Phases are retained as `agent_session_launch` records under one expansion identity — `prepared` +before ownership is released, then `materialized` where an adapter required a +materialization turn, then `detached`, then `exited` — so a completed replay +launches nothing, a replay holding only `prepared` proves the handoff never began and +may still create under the retained identity, and one holding `detached` resumes and +never falls back. The public route carries an opaque one-use launch request and answers +nothing; the right to run and retain a phase is delivered to the installed provider +directly, so neither a returned completion nor a rebuilt request authors a launch. + +Every operation that can act on an advertised session takes exclusive ownership under +one natural key first, through a coordinator the host built and passed in; contention +refuses instead of queueing, and an owner that never proved it stopped leaves a recovery +tombstone. + +A host that cannot establish session ownership refuses every advertised operation. Every +advertised build-bound adapter also requires the construction route store and executable +observer, regardless of who chose the identity; missing host assembly refuses before +provider work. Every private setup or child-creation failure is normalized to +`process-creation-failed` with fixed provider-owned text, carrying no path, argv, environment or host +message. No launch path discards persistent provider state. + +A session whose adapter pins a build is bound to that one build, whichever side named +it: the build is observed inside ownership before an identity is allocated or a provider +session is created, the binding is published with the bound route — V2 for +client-native, V3 for ACP-first — and retained beside the prepared record, the native +child runs the exact observed path in place of the launcher name, and every later +create, resume, attachment and incomplete replay reobserves and compares before a +process, an ensure or a turn. + +A `` or `` meeting a bound client-native route attaches to it: it +reobserves the build, requires any retained provider arrangement to assert that same +conversation, calls ensure with the route identity as `resumeSessionId`, and requires the +provider to report that identity before a turn — refusing on missing capability, build +drift, missing history or a differing assertion without creating a substitute +conversation. + +ACP runtimes are partitioned by resolved agent command and binding, each handle is +closed by the partition that created it, and a bound partition is torn down when its +last handle closes. + +A legacy V1 client-native route keeps exactly the released native-only behavior and +never attaches, and a legacy V1 ACP-first route refuses an agent whose adapter binds a +build rather than gaining the current executable. + +Native launch is available under the host scope described here. Its initial delivery was +recorded on 2026-08-22 in `5542c921bb8145458dc5f2446a61a1082a916f41` (#517/#518), extended by #519 and #561; Deno and +the compiled binary assemble the host — coordinator, route store and executable observer +— and Node and Bun keep the same advertised names while assembling none of it, so every +advertised operation refuses before provider work; `claude` is advertised for native +launch after passing the client-allocated gate at Claude Code 2.1.241 on macOS arm64 +(#520) and separately for client-native attachment after passing the native-to-ACP +marker gate (#561); `codex` is advertised for native launch after passing the +provider-returned gate at `codex-cli 0.153.2` on macOS arm64 with the vendored `@agentclientprotocol/codex-acp` +1.6.2 snapshot that asserts the App Server thread on the response `_meta` (#755), +which costs one fixed `codex-materialization.v1` turn because Codex writes the rollout `codex resume ` +reads at a thread's first turn. That tuple is the compatibility point the advertisement +stands on rather than an allowlist: admission accepts one canonical `codex-cli x.y.z` line, +so another canonical version is admitted while bare, malformed, ambiguous or multi-line +output refuses; exactness belongs to the session, whose accepted string becomes its +retained executable binding and is reobserved and compared before every later create, +resume, attachment and incomplete replay, so continuing a session across releases is not +offered. Reading the ACP session id as the native identity stays forbidden; `Agent.AddDir` +is unbuilt. + +#### Session configuration and inspection + +A `` says which model and effort level its conversation runs under, in exact +provider ids, and no other element does. `Agent.session()` continues to route only a name or +opaque placement; configuration is not a middleware argument. Each provider installation +owns one private registration closure for its exact lifetime. An opaque placement +carries an unreadable reference to its operation-local issuance, including optional +frozen configuration and durable component identity; `with({ name })` preserves it, another +placement substitutes its own, and a raw string or directly returned raw Session selects +no configuration. + +The directly delivered provider coordinator accepts the final live placement once and +completes it with the provider's exact Session plus either fresh state or one stable +established-session configuration operation. Fresh completion performs no configuration +work; established completion applies and verifies eagerly, and the returned canonical +value is sealed into one opaque frozen session use selected lexically for nested prompts +and launches. + +The owner refuses a different operation for an already registered exact Session, +registration or lookup after teardown, and copied or foreign placements and uses; +teardown drops its registrations after provider finalizers. No Context, module registry, +public Agent operation, middleware-readable member, return value or Session property +exposes the owner or operation, and the provider retains no desired-settings +association. + +A string passed to `useConfiguredSession()` creates one operation-local placement with no durable +component identity. + +Middleware may observe, refuse or reroute whole Sessions. The final routed value decides +the operation: an authentic configured use applies and verifies its own settings; +another authentic use applies its settings; a raw Session, name or absence is +unconfigured; and a copied or foreign configured session use refuses before provider +work. The journal records only the verified configuration the final route actually +started under, never an earlier lexical request. + +Settings are applied under the Session's existing serialization and ownership — read, +apply model, refresh for that model's effort choices, apply effort, verify exact +equality — before a turn or native UI. + +Omission performs no configuration read or write. + +An unavailable option, unsupported capability, rejected write, mismatching readback or +unverifiable restoration fails before the turn, restoring model first and reporting the +original rejection; a restoration nobody can verify makes that exact Session unusable +for the rest of the run. + +A prompt that started and a successful launch preparation retain one canonical +`configuration`; a configuration refusal and an unconfigured operation retain none. + +Completed replay contacts no provider, and an incomplete configured launch reapplies +that canonical value to the exact retained Session or fails. + +Released launch `requestedModel` and observational `model` strings are +compatibility-reader inputs only: valid values are discarded, malformed values refuse, +and neither can configure replay. + +A final configured provider-returned launch verifies before materialization, detach and +native UI; a final configured client-allocated launch refuses before route, identity, +private file or process, while middleware that routes an unconfigured Session receives +the unchanged unconfigured contract. + +`Agent.options()` reports provider-ordered, grouped choices through one scoped unretained +inspection session that sends no prompt and creates no XMD execution, journal or durable +session, and `xmd agent options` renders them as text or version-1 JSON. + +Session configuration was delivered on 2026-09-21 in `c77a4b882fb528663137cb04552cec90a569d18a` (#828/#829); ACP +translation lives entirely in `@executablemd/acp`, and no ACP or ACPX type crosses into core. + +#### Provider identity + +A provider-native session identity is the identity an agent asserts for its own durable +session state, distinct from the logical XMD session key and from the ACP/ACPX record +identity. Only an asserted one crosses a handoff; XMD never infers that a string a +native CLI would accept is one. + +#### Inert placement + +A pending session placement is an exact provider-issued `Session` capability carrying +where a logical session will live and which agent it is compatible with, and nothing +more: no construction route, no provider handle, no resumable identity. A fresh +`` produces one, and the first consuming operation chooses how the session is +constructed. + +#### Materialization + +Session materialization is the transition that makes a placement's chosen route and its +backend history resumable. ACP-first materialization happens only when the backend +reports that it accepted the session's first turn; client-native materialization is the +native launch's existing retained construction. Nothing else promotes a placement — not +a returning ensure, a first output, a terminal result, a checkpoint token, an error code +or a diagnostic. + +#### Established sessions + +An established session is a placement whose immutable construction route and durable +provider or native identity both already exist, and which is therefore validated +eagerly: reattached, compared against its retained history, and refused when either is +missing or names another conversation. + +#### Instruction placement + +The instruction layer consists of the provider-native session, system or developer +instructions a launch installs before the native UI accepts its first user turn. It is +not a user message, and it is not conversation history. + +#### Foreground ownership -- The journal records every attempt in full; replay restores the outcome - without re-executing. -- Rendered output shows the winning attempt plus a brief summary of failed - ones; verbose rendering expands the detail. -- On final failure, the last attempt's partial output renders, then the document - execution fails. +A foreground-terminal lease is the one exclusive hold on a document execution's +foreground experience. A root native launch holds it for one inherited terminal. A host +with no terminal refuses it, and no second root launch can hold it concurrently. -## Partial output +#### Launcher -- A failing region keeps what it rendered: everything rendered before the - failure stays in the document and reaches the output stream, not only the - journal. -- Only work the document was going to render can reach the output: a binding, - a value, or documentation that fails never adds its content. +A native launcher is the host-owned seam that reserves the foreground terminal, flushes +what that terminal has pending, starts one native UI there, and reports its terminal +status and nothing else. It is not `exec`, whose children are piped, captured and +journaled. -## Outcomes and the journal +#### One-use requests -- A document execution that fails is still a complete record: replaying it - restores the output and the failure without re-executing anything. Only a - durability failure means the journal no longer describes the document - execution. -- Object identity never crosses the journal: a live document execution reports - the failure it caught; a replay reports what the record describes. The - journal is parsed, never trusted — an unreadable record is refused, not - coerced. +A launch request is the frozen, one-use value public launch middleware routes. It +carries the facts of one launch and `with()`, and nothing that can settle one. +Identity is object identity: a rebuilt look-alike describes the same ask and is admitted +nowhere. -## Replay-safe live services +#### Launch coordination -An attached service belongs to the document execution that attaches it. The -host owns port selection, process spawning, XMD service handshake -authentication, supervision and teardown. Shared runtime code reaches that -behavior through the provider-neutral `API.Service`; it never imports a host -process or networking API. +Core delivers an agent launch coordinator to the provider factory it installs, as an +argument that factory closes over. It validates the routed request, runs each absent +phase once, cross-checks and retains what comes back, and derives the result. There is +no reader for one, no context holding one, and no request member carrying one. -The service publishes a frozen loopback endpoint as a live binding. Live -bindings form an overlay on the component's durable eval bindings: `ephemeral -eval` reads both and may add live bindings atomically, while ordinary `eval`, -code-block interpolation and journal serialization read durable bindings only. -A live binding cannot shadow a durable binding, and a durable value cannot be -replaced by a live one. +#### Session ownership -Service attachment and `ephemeral eval` execute again during partial replay so -the current process and middleware chain are reconstructed. A completed -document replay returns its recorded result without expanding the document and -therefore starts no service. Ordinary durable eval transforms a block and -validates its declared exports against the live overlay before constructing its -durable effect. A collision therefore has no eval `Yield`; compatible partial -replay remains aligned, while retained history containing a now-incompatible -successful eval is rejected by the existing replay guard or divergence path. -Every root and child termination checks its retained subtree before appending a -`Close`. The replay index tracks which coroutine identities the current -definition uses, so a completed retained child is not mistaken for aligned -history merely because it already has a `Close`. Durability failures take -precedence over ordinary termination even after the last retained `Yield` was -consumed. If collision handling terminates immediately, -`TerminalDivergenceError` retains the collision as its cause and the history -receives no terminal event; restoring the compatible definition can still -replay it. +A session coordinator is the host-built capability that answers who owns one logical +agent session right now, across processes. It is passed directly into the provider that +needs it and is deliberately not contextual: a decision document middleware could +replace is not one. Acquisition never waits. -`xmd workflow start` and `xmd workflow resume` install the non-delegating -`useWorkflowServiceDenial()` provider inside each execution scope, before the -root document is imported — in the same place `xmd run` installs its host -service adapter. The provider prevents a workflow from reaching an inherited -host adapter, because a run-owned durable service requires stable identity and -reconciliation rather than an execution-owned live process. +#### Construction routes + +A construction route records how one logical agent session was first constructed — +through ACP, or under an identity XMD chose — as strict create-once durable state beside +the coordinator's own records. It answers a different question from ownership and grants +no right to act: the coordinator remains the one live owner. A route never converts, and +a session ACP already established is recorded as ACP-first rather than reclassified. +Three closed schemas are readable: legacy `session-route.v1`, build-bound client-native +`session-route.v2`, and build-bound ACP-first `session-route.v3`. New bound construction publishes +V2 for client-native or V3 for ACP-first. Legacy V1 records are never upgraded in place: +a V1 client-native route admits native resume only and never ACP attachment, while a V1 +ACP-first route refuses an adapter that requires build binding. + +#### Build identity + +An executable build binding records which build of a provider executable accepted a +session, as a schema, an adapter-canonical version and the lowercase SHA-256 of the +canonical executable target. Equality is exact over all three, so a matching build +reached at another path is the same build and a changed build at the same path is not. +It contains no path: a path says where a build was, which stops being true, and names +host layout besides. + +#### Executable observation + +An executable observer is the host-built capability that resolves a launcher command +through the host's real execution environment, canonicalizes the target, requires an +executable regular file, hashes that target and asks that same file its version. The +host passes it directly into the provider like the coordinator beside it. It is +deliberately not contextual: executable validation decides which retained history may be +accepted, and a resolver document middleware could replace could point the observation +at one binary while the run spawns another. + +#### Attachment + +Client-native attachment joins a conversation a native process constructed, through ACP, +under the identity the route already names. It is attachment, never conversion: the +route stays client-native, `resumeSessionId` receives that exact identity, and the provider's +canonical assertion must equal it before the first turn. Attachment is advertised +separately from native launch, because handing a session to a native UI and later +joining it prove different things. + +#### Identity provenance + +Identity provenance records who chose a provider-native session identity: the provider +returned it, or the client allocated it before the provider existed. This provenance is +retained rather than inferred, because after the fact both are just a string in the +record. A released record written before the distinction existed reads as +provider-returned, which is the only compatibility inference. + +#### Proving release + +Quiescence is the one-use acknowledgement an ownership body gives. It does not mean "I +finished" — it means nothing this owner started can still act on the session, which is +why a handoff that could not release its handle never gives it. + +#### Unproven release + +A recovery tombstone is an ownership record left active because its owner never proved +it stopped. A crash releases the kernel lock and not this, and no pid, elapsed time, +released lock or empty transcript clears it. + +#### Provider partitions + +A provider partition is one complete, independently owned agent-provider state — +runtime, store, managed sessions, queues, coordinator, teardown — selected by the one +installed factory at each dispatch. Production is the single-partition case of the same +path; holding a partition grants work, never permission. + +### Agent permissions and generated XMD + +A workflow Agent receives no Workspace, checkout, materialized root, host path +or additional directory. Its process starts in a fresh empty directory the host +owns, configured with no MCP servers. That directory is provider-owned, never +enters logical or durable identity, and is removed with the attachment. The host +requests an empty native tool allowlist and installs a permission path that +denies every native request and fails the turn that asked, without reaching the +public permission chain. This is the V1 permission ceiling; the portable proof +that every provider exposes no ambient tool is tracked separately and does not +widen it. `xmd run` keeps its caller-selected Agent permission behavior. + +What a turn was allowed to do is retained beside what it said, and the two are +recorded by different things. A permission policy decides every request inside +its scope without deferring outward, so nothing installed inside one — and +nothing installed inside the turn, below a policy a host put around the execution +— can see the decision it makes. The Agent middleware installed with the +Agent itself, outside every policy installed after it, owns both sides of this. +Each `Agent.prompt()` call is given a private ledger and the cold stream it +returns is wrapped, so every event that call produces is read with that ledger +in place and a request raised while it is read inherits that ledger and no +other. A component installs nothing to be audited. The observer reserves a +place, delegates once, and copies the outcome that comes back into that place, so +concurrent turns keep their own audits and two overlapping requests keep the +order they were asked in. A place nobody completed — a policy that raised, a turn +torn down while a request waited — is published as nothing rather than as a +denial somebody invented. The ledger names a destination and confers no authority +to answer anything. + +The only thing a workflow Agent receives is the rendered content of an authored +``. It proposes an information request, or a change, by returning XMD. The +trusted workflow host passes that exact response to the constrained +generated-XMD evaluator, which performs admitted components as ordinary durable +effects against the authoritative Workspace. + +Evaluate produces exactly the fragment's ordinary rendered output. It neither +observes component return values nor invents a second result channel. A +generated fragment captures values with ordinary `as` bindings and renders any +structure it wants through ordinary pure components such as `Json`. A bound +`Fetch` response therefore reaches the next Agent turn only when the fragment +chooses to render it. An admitted mutation likewise contributes only what its +authored fragment renders; its own durable effect record remains the account of +what it did. + +An admitted effect names its durable operation after the invocation the engine +handed it, through the invocation authenticator this execution delivered to the protected +`` body it built (*Capability-backed invocation identity*). Nothing +else carries the capability: an Effection Context is addressed by name, a contextual +Api handler installed outside an invocation answers ahead of the engine's own, +and a definition or registry answer is a value a handler may keep from one +attachment and hand back inside another. Two effect sites sharing one +durable name would each replay the other's admitted fragment, and an +implementation running under another attachment's identity would commit against +its own run's storage under that attachment's expansion. + +An admitted element runs canonical core's own ordinary component, and the +operation it performs is the ordinary one. For a while it did not: a profile +stated a *capability* whose body core closed over operations the host handed it, +so a generated read reached a filesystem nothing in the document's scope could +observe, transform or refuse — and a generated `` scoped its content +through a cursor the evaluator owned rather than through the contextual +environment. Both are gone. A fragment's operations now cross contextual +`API.Files` and `API.Fetch` like every other caller's, which means the middleware +an authored read crosses, the provider the host installed, and the effective +`Permissions` of the consuming scope all apply to them. + +What the profile states is admission: which names, which spelling of each, which +requests an admitted `` may perform, and the execution's lifetime — an +admitted implementation refuses once the execution that captured it has ended. +A host hands the profile no operation at all, because there is no second +filesystem for it to hand. What a fragment may *do* is then the ordinary +question asked of any caller: the provider that is installed, and the policy of +the scope the element runs in. + +The narrowing these revisions record is the one that still stands — an entry +authorizes one component under one spelling — so core's entries state revision 2 +and the bundled Git Plugin's `` states revision 3. **A run suspended under +the earlier revisions refuses to resume rather than silently receiving the +narrower grant**, which is what a revision is for: a continuation resumes only +under the exact identities it was admitted with. + +`` is one Agent turn. The host hides no Agent loop inside it. Ordinary +authored Markdown owns iteration with ``, branches on whether a reply +is another information request or a final proposal, and makes exhaustion an +explicit document failure. `` is canonical core's own protected +component, reserved ahead of every host and author tier, so the name means the +same thing in every execution and no registration, repository file, bundle +member, declared Markdown component, import handler or second loaded copy +answers for it. A host does not supply the component; it supplies the +*ceiling*, as one `ExecutionInstallation.evaluation` profile captured by value +before any installation runs. An execution accepts one and refuses two, and one +that was offered none has no evaluation at all — which `` says when it +is written rather than at startup. + +The document writes the program as `text`, or renders it as paired content: + +```mdx + + + + Read the changelog and report the version. + +``` + +The two are disjoint — an element stating both is stating the program twice, and +is refused rather than resolved by precedence. A paired producer renders under +the *narrowed* syntax reference, so an agent asked to write a fragment is told +what a fragment may contain rather than what the surrounding document may write, +while keeping the producer's own operational permission: narrowing what may be +*named* is not narrowing what the producer may *do*. The closed schema accepts only `text` and `allow`; +every profile refuses `source` and `program`. + +Within one run a logical Agent session is identified by the Agent/Session +expansion identity the engine derived, and by nothing else. The authored +`` is descriptive, so two sibling `` +elements are two sessions. The name travels the compositional public chain, +where a handler may observe or change it; the identity travels inside an opaque +placement the element routes, readable only through the coordinator delivered to +the installed provider. It reaches the placement from the invocation capability +the engine minted, never from the expansion context. That placement is bound to the element that opened it +and is good for one use, so a handler cannot keep the first element's placement +and route it for the second — both would otherwise resolve to the first +element's identity. + +The mapping is a row in the run's own database, committed in the run's own +transaction: one that could commit while the run did not would describe a +session the run never had. It records the provider, the resolved agent command +and the fingerprint of the session policy the session was created under as +compatibility attributes — changing any of them refuses reattachment rather than +addressing a second mapping — together with the provider's asserted durable +identity. Cwd and provider placement are never compatibility inputs. What stays +beside the run on disk is the provider's own session store and one empty working +directory per session; both are disposable. + +The order is placement, then the backend's acceptance of the session's first +turn, then the provider's canonical, tagged assertion of a durable identity, +then the mapping commit — and only then is anything that first turn produced +exposed to the document. + +Placement is inert. A `` says where a session will live and which agent +it is compatible with; it creates no provider session, contacts no backend and +writes no row. The session comes into existence when the first subscribed +`` starts a turn the backend accepts, through the one handle that +ensured it, and acceptance is reported by the adapter rather than inferred from +what the turn then produced. + +Occupancy of a key in the provider's own store is not an assertion: it says +something is there, not what conversation it is. A record the provider holds for +a first turn nobody accepted is exactly that — occupancy — so it asserts nothing, +no mapping is committed for it, and the next attempt creates rather than +resumes. + +An interruption before the mapping commits is reconciled only from one canonical +provider assertion — exactly one, for that identity. A missing assertion, an +assertion conflicting with what is retained, one that replaced it, and more than +one at once each refuse. Once the mapping has committed, a restart compares the +provider's current assertion against it before continuing, and reattaches only +when the kind and the value both still agree. No refusal substitutes a new +session or a reconstructed transcript while reporting continuity. + +Cancellation owns both sides of a live step. It stops the Prompt stream or the +generated-XMD evaluation, completes provider and evaluator teardown, and removes +the disposable directory before the attempt settles. Already committed session, +Prompt, admission and effect records remain valid; an uncommitted read or +mutation may run again under the same retained admission, on its own +component's ordinary transaction and retry terms. A final proposal is the +retained result of its Prompt. Any later execution of its mutations crosses the +separate mutation-proposal admission rather than an Agent channel. + +Generated XMD is untrusted input. The evaluator preflights the complete fragment +before its first effect and admits only explicitly allowed, already-resolved +component identities. It refuses eval and exec blocks, imports, native +execution and arbitrary JavaScript execution. Ordinary `as` bindings, +structural language constructs and data expressions composed from JSON literals +and fragment-local binding references remain available; pure components such as +Json consume those values without effect permission. Generated bindings neither +import caller values nor escape the fragment. The evaluator retains the exact +filtered generated source for replay, history and deliberate training +ingestion. An incomplete evaluation can continue from retained source without +invoking the Agent again; a completed bounded information request reproduces its +rendered findings without repeating reads. + +Admission is built for the always-on trusted composition table and both effect +classes, and its ownership splits in two. +**Core** owns the mechanics behind `@executablemd/core/host`: parsing the +candidate fragment, walking all of it before the first effect, resolving each +admitted name **and authored form** to the exact pinned definition and identity +the host supplied rather than to anything a component search path, a +registration or a bundle would answer, recording one ordinary `generated_xmd` +durable admission before the first generated effect, and replaying that +admission and everything already committed without performing it again. It +reuses the same witness a component bundle does — canonical execution issues one +for the answer it produced and verifies it at the call site — so +`Component.importComponent` middleware may observe or refuse a generated import +and cannot answer, replace or mutate one. + +**The trusted host** owns the ceilings, as values it holds and passes: which +retained Workspace roots exist and which one is selected, which pinned +identities are in the `read` table and which in the `write` table, which +authored forms each is admitted for, and which exact requests each may perform. +Generated source receives none of those values, and nothing contextual carries +them. + +An authored `allow` selects a canonical, non-empty, duplicate-free subset of +the closed classes `read` and `write` from what the host already installed; +omitting it selects `read` alone, so a document that states no class asks for +read effects alone. It narrows and never grants: it cannot mint an identity, add +a root, authorize a destination, attach a credential or widen a request, and a +class the host stated no table for is refused before the candidate is parsed. +`allow` and bindings are independent: effect selection does not restrict the +language's ordinary fragment-local `as` behavior or pure composition. + +**A name is not a form.** A component whose two spellings do different things is +two identities, so one name holds two entries with disjoint form domains and one +shared implementation: `` resolves only to the pinned read identity and +`…` only to the pinned write identity. That choice is made in +preflight from the scan, before the first effect, so a form the selection holds +no exact entry for refuses the whole fragment. Overlapping forms or two +definitions under one name are a malformed host table rather than an ambiguity +settled by table order. + +Which identity was admitted is preflight's and is not answerable by anything +else, and the body the admitted component runs is held to that same decision by +the same mechanism an authored element is: canonical invocation-form dispatch +(*Capability-backed invocation identity*) enters the body for the form the scan +recorded, and the admitted invocation is checked against the form its identity +was chosen for before the component runs, refusing before any provider is +reached when the two disagree. That check reads the engine's own account of the +element — not `Component.hasContent()`, not the method on the object the +component was handed, and not the trusted wrapper the evaluator answers the +import with, which collects what a read returned and is not the form dispatcher. So +an admitted `File:read` does not become a write and an admitted `File:write` +does not become a read, however many times something is asked. + +The standard Deno workflow profile's write table is three entries, in the order +it states them: core's paired `File:write`, the bundled Git Plugin's paired +`Dir` under the versioned origin +`@executablemd/workflow/composition/dir-v2#Dir` — built from the same +implementation canonical core owns so the grant and the authored component +cannot drift — and core's self-closing `@executablemd/core#File.Delete`. That origin +names where released history recorded the entry, not what owns it today: +canonical core owns ``, the Git Plugin states the identity its released +runs retained, and the string stays unchanged so those runs still replay. A +host's own +extensions come after them. Selecting `write` intentionally authorizes Dir's +persistent recursive directory creation as well as the mutations File and +File.Delete perform. Each is the ordinary component and crosses `API.Files` +into the run's transaction-bound provider, so an ensured directory is retained +by the same `workspace_file` effect an authored Dir publishes. The evaluator +adds no mutation API, transaction or receipt of its own. A write-enabled +fragment that only ensures a directory renders nothing unless the fragment +authors output around it. Local Git, Git-host, issue, +process, eval and exec, native-command, credential and external-write effects +are outside the class. + +The versioned Dir origin is a grant boundary, not a symbol decoration. The +former `@executablemd/workflow/composition#Dir` identity authorized lexical +placement that created nothing; it does not authorize the persistent mutation. +A continuation whose retained write table names that former identity is refused +before generated execution instead of inheriting a broader grant. Read-only +admissions never selected the write table and remain unaffected. + +That order is retained identity rather than presentation. A continuation is +compared against the entries position by position, so adding the deletion +identity to the table refuses a continuation that was admitted under the former +two-entry one — before any generated effect, and deliberately: a run resumed +under a wider grant than it was admitted with is the thing the comparison +exists to refuse. A read-only admission never selected the write table and is +unaffected. +`` neither prompts nor approves: approval is authored control flow +before the write-enabled element, and a branch not taken leaves no admission and +no mutation. + +A refusal names the construct class and echoes no part of the candidate — in its +diagnostic and in what it retains alike, because the decision is recorded +either way. That holds for a request the candidate malformed as much as for one +outside the admitted set: the component reports a bad URL, header or timeout by +quoting it, so a generated request is normalized behind a boundary that keeps +the class and drops the diagnostic. The host's own ceilings are not generated +text and are normalized before the admission, so a host that states a request it +cannot mean is told which one. Admission is the line: before it nothing of the +candidate is retained, and after it the exact source is retained on purpose. + +A retained admission is a grant, and a resumed run is held to it. Durable +replay matches an effect by its type and name, and what a description carries +is stored rather than compared, so the normalized policy is retained in the +admission's own result and a continuation checks it before one generated +component is invoked or one effect is performed. The check holds two kinds of +term differently, on purpose. The admission's Workspace roots and selected +root are an **as-of-admission retained basis** over a set the run's own +progress legitimately grows — every committed mutation retains another +immutable root and advances the authoritative current root — so the basis is +asked for by membership: every admission root and the admission's selected +root must still be retained, and the root the run now stands on must be a +retained one, while additional roots and an advanced current root change +nothing the admission was granted under. A run that no longer retains an +admission root has lost the history the grant was made over and is refused +before generated work. Every non-root term — the class selection, the selected +pinned identities with their admitted forms, and the normalized request +ceilings — compares whole and exactly, in canonical order. A pinned identity is +one closed tagged record — whether canonical core supplies the body or the +ordinary import chain answered for it, the origin, the component key and the +revision — rather than an assembled spelling, so a reader comparing two +admissions can say which term moved and no two hosts have to agree on a +separator. The untagged version-1 record holds one opaque string instead — +whatever the party that built the pinned entry chose, which for core was +`@executablemd/core#File:read` and for the workflow host was +`@executablemd/workflow/composition/dir-v2#Dir`, with no revision in either and +no convention to read back out. Nothing recovers four structural terms from one +of those, so nothing tries: a *capability* entry states the exact version-1 +strings it succeeds, and a retained string reconciles against that stated list +and against nothing else. Stating one is an assertion by whoever owns the entry +that the current grant is no wider than the old one; an entry that states none +refuses every version-1 record naming it. A component answer states no alias and +has nowhere to put one, because that arm did not exist when version-1 records +were written. New records always state the structural identity. The run's own +ceiling table is keyed by those terms encoded rather than joined, because a +joined key is only as unique as the separator is illegal and nothing makes a +space illegal in an origin or a component key: two entries whose terms differ +only in where a space falls each keep their own admitted requests. Only the +tables the selection reached take part, so changing a write table a read-only +admission never drew on changes nothing it was granted under. Deciding the +fragment inside that durable effect is what makes this hold: a continuation +restores what was admitted without consulting the current candidate at all, so +what expands is the source this run admitted rather than whatever a later caller +is holding. + +The admission and every durable effect the admitted fragment performs are +yielded inline by the owning document expansion, in authored order and ahead +of the expansion's later effects, so one durable sequence accounts for the +admission, each nested generated effect, and whatever the document does next. +A partial continuation is offered that same sequence: the admission and every +completed nested read or mutation restore from their retained records without +another live execution or root publication, and the run continues to the next +unrecorded position. The existing `scoped()` around generated expansion keeps +owning its middleware, cancellation, and teardown. The evaluator is an +ordinary `Operation` and is not wrapped in `ephemeral()` — [DEC-034](packages/durable-streams/specs/DECISIONS.md#dec-034-ephemeral--explicit-escape-hatch-for-non-durable-operations-in-workflows) reserves +that adapter for intentionally non-durable Operations, and an operation that +can yield durable effects is not one — which is conformance, not the +continuation correction: exact journal order is preserved with or without such +a bridge, and the reproduced resume failure was the former exact root +comparison rejecting the run's own progress. + +An XMD-mediated HTTP read is how a network-denied Agent is given external +information: the authored document performs the read with `` and the +retained response reaches the prompt as data. That does not raise the Agent's own +ceiling. A generated fragment may name the pinned `Fetch` identity only when the +host admitted it, and then only for a request that normalizes — scheme, host, +path, method, headers and effective timeout alike — to one the host stated +exactly; anything else is refused before `API.Fetch` is reached. A repository +component that takes the name `Fetch` is not that identity and acquires no +admission from it. -## State ownership +`` is admitted only in its self-closing read form. The component reads +when it has no content and writes when it has some, so the two forms are two +identities and the constraint travels with the pinned one, decided in the +whole-fragment preflight rather than inside the component after earlier elements +have run. -All state is scoped to the operation that owns it, so it is torn down when -the operation is torn down: created inside the operation it describes, -provided via context. No module-scoped registries — not as collections, not -hidden inside library objects that accumulate. One exception: metadata an -author declares at module evaluation, about a value the author owns, may live -on that value. +Mutation-proposal admission and workflow-bundled Markdown component admission +remain unbuilt. Directory registration is not among them: a workflow Agent is +given no directory to register. -A callback registered with something outside the process — a listener on a -socket, a child process, a stream or a DOM target — is state of exactly this -kind, and the source it is attached to knows nothing about the operation that -attached it. The event arriving is not teardown: a cancelled wait is precisely -the case where it never arrives, and a losing race arm is one that will never -be told. So the owner detaches its handlers, on completion, failure, halt and -race loss alike, before it is considered closed, and an event delivered after -that reaches nothing and changes nothing. `local/require-scope-bound-event-registration` -holds source to it. +#### Sealed evaluation entries + +A paired Evaluate producer uses an execution-owned one-shot projection that +bypasses public `content()` and `tryContent()` middleware. It renders under the +narrowed syntax reference while retaining its own operational permission. +Evaluate declares no `printErrors` boundary; its public refusal keeps throwing. + +An entry states what is behind a name in one of exactly two ways, and neither carries a +function. A *capability* names the component canonical core supplies for it: capture +answers with core's own ordinary definition — the same ``, ``, +``, ``, `` or `` an author writes — held to the execution's +lifetime, with the component's own form dispatcher travelling beside it so canonical +selection enters the body the scan chose. The operation it performs is therefore the +ordinary one, through contextual `API.Files` and `API.Fetch`, under the provider the +host installed and the effective `Permissions` of the consuming scope; the host hands +the profile no operation of its own. + +A capability states what it binds wherever the ordinary component does because it *is* +the ordinary component, so an admitted `` is the value component the ordinary +one is — sharing its props, return contract, source rules and sanitized failure +sentence, refusing a missing `as` and an unusable pattern before the search, and +resolving its paths against the contextual working directory. + +One name runs one component, so a name admitted for capabilities owned by two different +components refuses at capture. An admitted `` is the one entry with a ceiling: +the element is classified against the entry's exact admitted requests, performs the +admitted one rather than its props, and that ceiling is installed as an ordinary +`Permissions` restriction for the invocation, so a contextual transformation that moves +the request off it is refused by the terminal rather than sent. + +A *component answer* names an implementation the ordinary import chain resolves, and +states only the identity a provider must have registered for it; canonical execution +resolves that name once — before the root import and before any document code, through +the complete ordinary `Component.importComponent` chain and a private terminal that +writes no record — and asks the identity owner about the exact final answer in one call. +That call answers with the registered identity *and* core's own copy of what was +registered, taken when the identity was registered: the check and the thing kept are one +result, so nothing downstream reads the chain's object a second time and an answer whose +members read differently on each read cannot pass a check with one reading and be sealed +with another. The identity is then compared whole, and the copy — not the answer — is +sealed behind the same revocation. + +One name states one identity, however many forms and tables hold it: two entries under +one name are the two spellings of one component and share one lookup and one sealed +implementation, while a second identity for that name, or a name held as both a +capability and a component answer, refuses at capture rather than letting assembly order +decide. + +Provider installation and the right to answer one occurrence are separate: installation +receives a registrar that installs import middleware, and every invocation of that +middleware receives a fresh request fixed to the exact name, position, provider +installation, origin and resolution-window object it was asked under. Only that request +may register an identity, and it states the answer plus key and revision without +restating the name. Canonical execution closes the request synchronously when its +handler returns, fails or is cancelled; an outer request remains live while it delegates +and may register a replacement after the inner handler returns. Enclosing resolution +still closes in its own `finally` on success, fallback, failure or cancellation. A +registration therefore requires the execution, request and exact current window to +remain active, the request's fixed name to match that window, and that provider +installation not to have stated a different answer in the window. + +Identification takes the expected window explicitly and accepts only a registration +recorded for that exact object and name, so a stale request cannot answer a later +same-name resolution or retag itself while another name is live. + +The installation is reusable: one provider may answer several admitted names and +repeated resolutions through distinct requests. All of this state is held in ordinary +private closures — no Context, shared symbol, public brand or module-global registry — +and an unidentified middleware replacement remains a valid ordinary import answer +outside fragment evaluation. + +A capability entry also states the exact version-1 identity strings it succeeds, which +is the only thing a released untagged record reconciles against; a component answer +states none. A capability-only profile performs no such lookup at all. + +Resolution happens at capture and never again: a fragment runs the sealed snapshot, a +continuation resolves once more in its own capture and reconciles before any effect, and +a provider still answering when a fragment resolves its admitted name is answering a +generated import, which only canonical execution answers. + +A trusted layering control may enter one package bootstrap through an inherited layer +and an execution-local one — documentation, registrations and the provider together — +and the repeat is additive: the outer entry observes and delegates rather than +registering a second identity for an implementation that already states what it is, and +a non-identical documentation overlap on one owner and component still refuses at the +child collection boundary, before the root import and therefore before the provider is +asked at all. + +#### Workflow Agent capability selection + +The workflow profile selects ACP-only capability explicitly: it advertises +neither native launch nor client-native attachment. It supplies no machine +session coordinator, construction-route store or executable observer. A workflow +session belongs to a run; the machine-wide ownership account describes a +different boundary. Deleting a run removes its mapping row and provider-session +directory and reports the categories removed. + +#### Typed candidate recovery around Evaluate -A Repository selection is composition data and is therefore replaceable: a -document may bind one, render one, hand one to a child, and construct one that -looks exactly like it. Nothing a repository provider does is authorized by the -value it was handed. What stays provider-owned, in the provider's own closure -for one document execution, is everything a selection is *not*: the advisory -locks on managed checkouts, the canonical Git identity each selection resolves -to, an ordinary run's live Push evidence, its invocation identity, and every -reconciliation key derived from them. A selection that the provider did not -mint, or one whose name, checkout path or identity was edited after it did, is -refused before Git or a service is touched. +Plan composes ordinary `` and ``. There is no deadline, no output +budget and no bounds record: the composition adds recovery, not limiting, and an +ordinary invocation without it behaves exactly as it does today. -### Definition-owned return state +The composition owns the genuine invocation and projects its content through +canonical core's private projection. Neither public content middleware nor an +operation supplied by another loaded copy can author that projection. The +generated admission and effects retain occurrence-owned identities in the +existing ordinary durable sequence. The caller supplies no context, stream, +cursor, coroutine ID or durable owner. This is execution-owned invocation +routing, not a persistence capability. -A value body — a value root, or a Markdown component that declares `returns` — -selects its value with a `` its own flow reaches, rather than with a -direct top-level child. What decides whose declaration an element satisfies is -the body it was written in, which is the ownership `` already establishes -for the loop it exits. +The composition owns the live projection with Effection structured cancellation: +it establishes cleanup before resources can outlive the operation, and it +settles the child's success or failure only after that teardown. A private +invocation settlement callback reports cleanup failure before a recoverable +outcome can escape, so a cleanup failure always wins over a candidate retry. +This is local to the composed projection and does not change ordinary +execution's failure precedence. Caller cancellation remains cancellation. -One state exists per execution of one value body. ``, ``, ``, -`` and `` keep the ambient one; a component invocation hides the caller's from the -invoked body, so a component's own `` satisfies its own declaration; a -`` passes none either, and the shared rule refuses the `` that -would have reached for one, so concurrent children cannot race to reserve a value -owned outside their ``; a -nested value body installs a separate one, and both executions stand; and -content the caller projected restores the caller's, through a Markdown -`` and a function component's `content()` alike, with every -projection of one authored element restoring the same state. Dynamically scanned -Markdown and a foreign body receive none, so a `` there stays reserved -and never resolves a component named `Return`. +See [component invocation settlement](#component-invocation-settlement) and +[whole-document execution ranking](#capability-backed-execution) for the two +surrounding boundaries. -The first executed `` reserves the state before evaluating its expression, -so two returns racing through one body cannot both believe they were first. The -second fails the body, evaluates nothing, and the first value is not published — -there is no first-wins or last-wins fallback. A body that executes none settles -as a missing selection: the body ran and its flow reached no ``, which is -a structural outcome and carries nothing about what the author concluded. A -document that deliberately aborts, or that exhausted a bounded search, expresses -that as authored control flow with `` and the sentence its author wrote. +Admission and generated effects use ordinary durable publication: each event is +persisted before its operation resumes. A safe refusal does not erase completed +effects. The projection and its structured teardown finish before Evaluate +returns or later parent work begins; interruption restores completed effects and +resumes at the first unrecorded effect. -**Selection is not a decision a document makes about itself.** A document's own -eval block reaches further than it looks: a named context by name, because -`createContext(name)` names the slot the engine would publish on, and an internal -module by file URL, because an eval block's imports are hoisted and resolved like -any other module's. Anything published contextually is therefore readable, and -any exported function acting on what was published is callable — a sealed carrier -in a context plus an exported `claim(carrier)` is forgeable however carefully the -carrier itself is sealed, because the document calls the engine's own helper with -the engine's own carrier. +Execution checks the closed evaluation records before completed-root reuse and +before public replay middleware can decide anything. Their structural +fingerprint binds the exact root source and inputs, candidate source, read +selection, ordered profile identities and forms, protected Syntax identity, +lexical reference and filesystem scope/policy identity. +Custom symbols providers state an immutable `identity`; captured Files state a +frozen `{ scope, policy }` `replayIdentity`. These identify permission, not today's +documentation, file contents or paths. Records contain data only, never providers, +live references, projection handles or callables. -So selection uses neither. The body that owns a value is a plain object whose -reservation, selection and settlement are closures over state nothing else holds, and -it reaches the `` that reserves it as an ordinary parameter of expansion — -down the call stack, which document code cannot read or replace. Ownership is -explicit at every site rather than ambient: a component invocation passes none, -so an invoked body is isolated; caller projection passes the caller's, so -projected content still satisfies the declaration its author could see; and -dynamically scanned markdown and generated fragments pass none. Both settlement -sites read the body their own expansion created and held as a local. Importing -the module that defines one gains a document nothing — it gets a body of its own, -connected to no execution — so a value still crosses its declared schema on the -way out. +Completed replay performs fresh installation and identity attestation, then +restores historical rendered output without repeating fragment bodies, Syntax +documentation lookups, File reads or Glob traversal. Missing or invalid current +attestation refuses before those effects. It does not reuse an expired attestation +or refresh information to validate history. -Selection is not a permission and not durable state: it appends no journal event, a -partial replay reconstructs it while completed effects replay, and a completed -document replay reuses the retained result without expanding the body. `` -does not end the body, so later documentation runs and a later failure, -cancellation or teardown failure keeps its existing precedence. +One narrow namespaced classification identifies the normalized recoverable +generated-request refusal and carries only a safe reason. Stale admission or malformed +retained history, and terminal setup, runtime, provider, persistence, secret and +cleanup failures, are not that classification and are never recovered. +Normalization reads the namespaced descriptive tag across loaded copies rather +than matching messages, and a plain `GeneratedXmdError` is not a recoverability +marker. Public Evaluate still throws; only the trusted surrounding operation +turns the one recoverable class into retry context after settlement. -### Structural branch selection +### Plan information requests -`` and `` are the engine's own syntax, not components, so -core owns what they mean and no repository file, registration or bundle can -supply either name. +Write `…` to author source in an ordinary +document. The shared writer frame uses `~/.xmd/plan/sessions` as its default +host root and one structural check, `validateDocumentStructure`, for draft, +admission and final-host validation. [The Plan contract](specs/plan-command-spec.md#the-plan-writer-profile) +owns directory placement, lifetime and the three validation times. -A switch decides its whole structure from source before it evaluates anything. -Which props were written, that every substantive direct child is a paired -``, that each case carries exactly one of a matcher or the bare `default` -word, and that at most one default is written and written last — all of it is one -shared rule that expansion and non-executing validation read. A malformed branch -therefore stops the selector too, including a branch no comparison would have -reached. +#### Delivery status and standing of this amendment -Only then does selection run. The selector is evaluated once, each non-default -matcher at most once in source order, and the first `===` match wins. Operands -are arbitrary JavaScript values evaluated through the expression evaluator rather -than through prop resolution, so `undefined` stays `undefined`, `NaN` matches no -case including one written `NaN`, `0` and `-0` are one value, and two objects -match only when they are the same object. With no match and no default, the -switch renders nothing and reports nothing. +The current contract is below. Its earlier delivery sequencing is retained as +[Plan information-request history](specs/decisions.md#dec-017-plan-information-requests--delivery-history). +The [Plan specification](specs/plan-command-spec.md) owns the authored interface +and product verification. -The selected case is transparent. It expands inline into the region that holds -the switch, opening no binding scope, no observation boundary, no error mode and -no loop boundary: a `` it creates stays available after ``, a -`` reserves the value body that owns the switch, a `` exits the -loop that lexically encloses it, and an error inside it is observed exactly where -inline content would be. Fallthrough does not exist, because expansion enters one -child list once. +#### One authorship workflow, one ordinary profile -The selected `` contributes its own expansion frame even though the -`` consumed it, so corresponding children of two branches cannot share -one identity. Neither construct is durable state: they append no journal event, a -partial replay rebuilds the selection through ordinary expansion while completed -effects in the chosen branch replay from their own records, and a completed -document replay reuses the retained result without expanding anything. +Plan authorship obtains component documentation, file contents and matching file +paths through read-only generated XMD. The loop belongs to packaged ``: +`xmd plan` and `` in an ordinary `xmd run` use the same workflow. A complete +Plan remains inert source throughout classification, review and approval. +Reading information is not execution of the program being authored. -### Concurrent spawned children +The command hosts supply the same ordinary evaluation profile before their +executions begin. Its always-on composition table admits core's exact ``. +Its read table admits canonical self-closing `` and ``, and exact +canonical protected self-closing ``. Its write table remains paired +File and self-closing File.Delete. The ordinary run's +write-enabled evaluations retain their existing behavior. Plan information +requests select only read: -`` and `` are the engine's own syntax, not components, so core owns -what they mean and no repository file, registration, bundle or Plugin can supply -either name. `` runs its direct `` children at the same time and -waits for all of them. It takes no props, produces no value, and requires at -least two spawns — one alone is the sequential document it was already written -as. +```mdx + +``` -The whole structure is decided from source before a child is constructed: the -paired forms, the absent props, the two-spawn minimum, that only blank text sits -between the spawns, that no `` is written outside its direct ``, and -that no `` or `` inside a spawn reaches for an owner outside it. -One shared rule states all of it, and expansion, non-executing validation and -the generated-fragment preflight read the same result — so a malformed construct -starts no child anywhere, including in a fragment an Agent generated. +The complete Agent response is the generated program text. A literal child of +Evaluate instead runs while producing that text and is not this request +boundary. No Syntax-specific parser replaces generated-XMD admission. -**Concurrent expansion shares immutable inputs and nothing else.** Every child -gets a binding environment and live overlay snapshotted from the one that -reached the ``, an eval scope of its own, a fresh block counter, a private -output buffer, its own checked-failure ledger, a copied hide set and an -expansion path extended by its authored ordinal. Nothing merges back, so a name -one child binds or rebinds is invisible to its siblings and to the work after -``, and a resource it retains is released when it ends. The outer -`` and `` owners are cleared at the boundary; a loop or value -component created wholly inside a child establishes its own. +The host installs the profile, not Plan. A component cannot install, replace or +widen its execution's captured profile. Shared Plan consumes the enclosing +profile; it introduces neither a Plan-private profile nor a child document +execution. Another trusted host's explicit profile remains authoritative and is +never replaced by Plan. Missing required installation is a host setup failure, +not a recoverable Agent mistake. This amendment changes the ordinary command +hosts, not the workflow host's independently installed profile. -**Durable identity comes from the source, never from the schedule.** The -existing durable structured-concurrency substrate is the implementation: in -source order, before any child starts, each `` receives one child -coroutine of the one that reached the ``, and nesting produces the existing -hierarchical identity. Effects inside a child keep their ordinary descriptions -under that coroutine, so two children may derive the same local block id without -colliding, and work after `` continues on the parent counter it would have -had without any child. Each successful child closes with the ordered `{ text, exact }` runs it -rendered. Exact presentation is a provenance recorded against the segment -objects expansion marked, and those segments do not cross the join — so the runs -are what carries it, live and on replay alike, and a retained close that does -not read as that shape fails the run instead of rebuilding part of a document. +The constrained coding-Agent session remains separate. It receives prompt data, +not native filesystem tools, a caller checkout, additional directories or a +permission escalation channel. An admitted XMD read is the ordinary `` or +`` on the ordinary contextual route — the installed provider, the +middleware around it and the effective `Permissions` of the consuming scope — +and it uses the ordinary run's contextual working-directory rules rather than +the Agent's empty session directory. -**Output is authored order; the journal is completion order.** A child emits -into its private buffer, and `` appends the renderings to its caller's -region only after every child has succeeded. Which child finished first may -decide when its records append and never what the document renders. A complete -replay reads the child closes and runs no spawned work; a partial replay -restores the children that closed and resumes only the unrecorded ones under the -same identities. Inserting, removing or reordering spawns is a definition change -and takes the existing divergence path rather than being reassigned by position -or completion time. +The writer frame refuses no read. The packaged workflow is trusted source and +reading is how it decides what to propose, so `checkFilePath`, `readTextFile` +and `globFiles` are absent from its refusal table rather than denied and +re-permitted for one phase: a read before, during or after an information +projection, and one from a concurrent trusted workflow task, are the same +ordinary operation, and an enclosing read restriction still denies all of them +before target I/O. What the frame does refuse is every way of changing +something — write, delete, directory ensure and temporary acquisition — plus +Process, Fetch and Service, wherever they are asked from. What bounds the text +an *agent* supplies is the generated admission it runs under: a draft is +unexecuted text, and an information program enters public read-only +`` under the host's admitted vocabulary and complete preflight. -**Failure is the existing fail-fast join.** The first child failure fails the -``, cancels every unfinished sibling and does not return until all of them -have finished tearing down. No private buffer is emitted, records acknowledged -before the failure stay durable, and interrupted work is never recorded as -complete. Cancelling the parent takes the same ownership path, and no task, -subscription, retained resource or buffer survives its spawn. No scheduler, no -new record family and no document-visible concurrency control is introduced. +#### Protection, admission, selection and documentation -## Foreground commands +Canonical protection fixes which implementation answers for a name canonical +core owns. Five names are in that tier: Syntax, Evaluate, File, File.Delete and +Dir. Selection answers them above the repository, default and host tiers, and +the final `Component.importComponent` answer is verified against the copy +canonical execution retained — so a registration, a repository file, a bundle +member, a declared Markdown component, a middleware replacement and a +definition from a second loaded copy are each refused where they are made. -An executable block is a foreground child process. Its output reaches the reader -as the child produces it, before it exits, and each byte is displayed once: what -was forwarded is never rendered into the document again at completion. +The two groups differ only in where the implementation comes from. Syntax and +Evaluate are built per execution from an invocation authenticator that execution +minted, because each is handed a site canonical execution built for it. File, +File.Delete and +Dir are ordinary components whose bodies reach the filesystem through +contextual `API.Files`, so each is one definition built once per loaded copy of +core and answered with as it stands — which is also the definition the +generated-XMD write table pins, so an authored element and an admitted +generated one invoke one implementation. Owning those three names fixes no +provider: Files middleware still answers, transforms, delegates and refuses +every operation they perform. -A command's output passes through several hands, and where a run observes it -decides what "the command's output" means: +The host profile admits that exact implementation and self-closing form to +generated XMD. The authored `allow` selects an already-installed class as a whole; it +neither selects individual entries nor grants anything absent from that class. +Plan's read selection never selects write. A generated request cannot widen it +by naming Evaluate, writing another `allow`, or requesting documentation. -``` -child → enclosing host stdio middleware → the per-exec boundary → this -document's display policy → terminal -``` +The enclosing lexical SyntaxReference carries the future-run documentation +index. Evaluate narrows availability against the union of the profile's trusted +composition table and the selected effect tables without discarding that index. +The ordinary profile's composition table contains the exact core Json +definition. It is always available inside generated XMD and carries no effect +operation. Bare generated Syntax lists Json beside the admitted read vocabulary: +Syntax, File and Glob under the ordinary profile. Named Syntax returns only the +requested documentation and marks components outside that vocabulary unavailable. +Availability of File does not admit its paired write form. Describing a +component neither imports it nor grants its effects. -**Middleware enclosing an execution is treated as preprocessing.** The process -stdio chain is a documented extension point: middleware installed around an -execution may consume, transform, redact, or redirect a channel before the run -receives it, and a host that does so is exercising its own ownership of its -own child processes. What it forwards is what exists as far as the document is -concerned. +Bindings and structural constructs are engine-owned language syntax and require +no profile entry. Every built-in construct is available inside a generated +fragment with its ordinary semantics — `Content`, `Output`, `Return`, `Let`, +`Each`, `If`, `Else`, `Switch`, `Case`, `Loop`, `Break`, `All`, `Spawn`, +`PrintErrors`, `Answers` and `Answer` — decided by the same source rules ordinary validation +and expansion read, so a construct means one thing whether a person or an Agent +wrote it. Preflight dispatches a reserved name to those rules before the +admitted table is consulted, which is why a construct is never reported as a +component the host withheld; a construct the generated root supplies no context +for, a stray branch, and a `` outside every `` fail with their +ordinary structural rule instead. -What a run retains is therefore what its **configured execution environment** -forwarded. `xmd run --journal` is diagnostic persistence: it records what that -environment forwarded, not what a child wrote to its pipe, which nothing here -observes and nothing here guarantees. A workflow's process results are durable -within the environment the workflow authorized. Middleware enclosing an -execution — the host's own, and anything the host composed into it — may -preprocess that output before either sees it. +Preflight walks every branch, every ``, every iteration body and every +nested region before the first effect, so a prohibited component in a path the +run never takes refuses the whole fragment with no read performed, while every +effectful component a construct reaches stays held to the selected permission. +What a value decides stays at runtime: a condition, a matcher, a computed `max` +and a failing read are evaluated where they always were. -*Current limitation.* `Process.join()` may settle before the pumps and their -`Stdio` middleware finish, so output a child writes as the pumps settle may -never reach the boundary and may therefore be absent from what a run retains. -effectionx #244 owns that, and until its solution is integrated nothing here -guarantees pump-complete tail delivery or retention. -So transformed text is what a run captures and journals, consumed output is -absent from both, and output an enclosing handler forwards on the other channel -is recorded as that other channel. +Binding ownership is stated per region. Component children do not escape. An +`` body is a fresh scope holding the enclosing bindings and the item, and +nothing it binds survives it, because that body may run no times at all — only +`` joins the enclosing scope. The alternatives of an `` and the +`` branches of a `` are checked from one incoming snapshot, so no +alternative supplies a binding to another; the union of what they can produce +becomes visible afterwards, which keeps reading a binding only one arm makes a +runtime failure rather than a preflight refusal. Each `` under an `` +is checked from that same incoming snapshot and contributes nothing back, which +is the binding contract the children actually run under. ``, +`` and `` bodies read and write the enclosing environment. Constructs keep their +exact spelling in the retained source, never join the retained named-component +list, and change no record version. -**The per-exec boundary is where a run observes.** One wrapper is installed -before `Process.exec` begins, so a chunk forwarded while the child is being -started is received like any other rather than raced for. The snapshot is taken -when the current `Process` operation settles, and that is as strong as the tail -guarantee gets today: `Process.join()` may settle before the stdout and stderr -pumps and their `Stdio` middleware have finished, so output written as the pumps -settle may never reach this boundary. effectionx #244 owns the stronger -guarantee. "Exactly what the command produced" means, throughout this -repository, *exactly the bytes received here* — never a guarantee about what the -child wrote to its pipe, which no execution observes, and never a guarantee of -pump-complete tail delivery. +A trusted host may add another pure component only by placing +its exact definition and stable identity in the composition table captured +before execution. It is not inferred to be pure from its name, documentation or +absence from an effect table. Names and forms across composition and effect +tables obey the same conflict checks as effect entries, and continuation +reconciles the selected composition identities with the rest of the admitted +surface. -**A run's own display policies are downstream and change nothing.** `silent`, a -`` region's suppression of terminal stdout, quiet runtime output for -a caller whose subprocess output is an answer, the command line's choice of -which stream a value root's diagnostics land on, and the terminal writers all -sit below the boundary. They decide what a reader sees; they never decide what -the run keeps. Not showing something and not knowing it are different decisions. +Each evaluation receives a fresh narrowed protected-body route. It closes with +the evaluation, before another Agent turn. Continuation verifies current +canonical identity and retained structural identity before rebuilding a route. +Same-name replacements, copied functions, middleware substitutions, another +loaded core copy and expired routes do not inherit protection. No protected +function, live profile, provider or capability handle enters durable history. -Three questions about a command are separate, and answering one never answers -another: +#### Classify the response before doing anything with it -- **Routing** is what a reader sees. An ordinary block forwards both channels; - a `` region takes stdout into the captured value and leaves - stderr diagnostic; `silent` displays neither. Routing answers "does this - channel reach the terminal, and does anything else want it" — never "which of - the host's streams does it land on". It is visible in document structure, - never selected by a root-level presentation declaration or by which runtime is - executing. -- **Retention** is what the run keeps. The host path that starts a run states - it; nothing infers it from which stream implementation a journal happens to - use, or from whether a pathname was named. A run that keeps no diagnostic - record keeps the exit status alone, and the transient path never accumulates - the bytes on their way past — a command that writes a gigabyte costs a - gigabyte of nothing. A run that retains keeps both received channels in - addition to whatever routing the document chose, because a display policy does - not weaken an explicit audit record. -- **Failure** is neither. A nonzero exit is a checked failure: a printing - boundary may decide how it is reported, never that the run succeeded anyway. - Later executable work does not start, and the run fails without any - `` declaration. `` governs rendered document selection only. +Initial drafting, structural repair and review revision share one non-executing +classifier. A response is a draft when its first body block, after optional +lexically closed frontmatter and whitespace, is a non-empty level-one heading. +Both ATX and Setext headings qualify. Closed but invalid YAML followed by that +heading remains a draft; structural validity belongs to CheckDraft and repair. -Recovery is the one exception, and it has a single origin: an -explicit error-handling construct written in the document. Nothing else confers -it: not `printErrors(fn)`, not a built-in printing boundary like ``, -not ordinary printing mode, not component failure middleware, and not a -component catching the `ContentError` its projected content raised. Those decide -how a failure of their own is reported; whether a command that exited nonzero -failed the run is not theirs to decide. A run that suffered an unauthorized -checked failure records it in an execution-owned ledger passed by value through -core's own calls, so an enclosing boundary that prints and returns cannot make -the run succeed, and no later work in any frame begins. `` and `` -are covered by that rule like any other printing boundary: their own cleanup and -their own report still happen, and neither writes, replaces, or continues. +An unterminated frontmatter envelope, no first H1, or a body block before the H1 +makes the entire response an information candidate. Admission still decides +whether it is executable. Review-explanation responses are outside this loop. +No classifier strips fences, repairs source, executes a draft or replaces the +exact bytes delivered after approval. -`` *recovers*: a region that says the failures inside it are -printed and the document continues. It is the only thing that says it. A root -asks for nothing, so a root gets the failure. +#### Compose reads and render findings -One other construct *contains* a failure instead, which is a different thing. -An invocation of `` keeps its checked failures to itself: the failure -becomes that test's failed result, the run's own record stays clear, the tests -after it still run, and the testing completion policy is what fails the run. -Keeping them is the whole of it — a contained failure is not re-raised into the -region the element was written in, because handing it outward is exactly what -the containment exists to prevent. +One information response can bind several admitted reads and explicitly render +the result it wants to return: -Canonical core owns that construct. `` is one of core's registered -defaults, and containment is granted while core expands that definition and at -no other time. Nothing nominates it: not a function another package supplies, -not a component name or registration origin, not a context, marker or metadata, -not the options or the request, not an installation. There is no public API that -accepts a component and confers containment, so there is nothing for a handler -to replace, counterfeit, or hand to itself. +```mdx + + + + +``` -What core does not own is testing. Activation, isolated bindings, the timeout, -how a failure is classified, what is recorded and how it is reported are -`@executablemd/testing`'s, supplied through a contextual operation core calls -from inside the invocation. That surface accepts no function, component name or -container, and cannot make another component contain a failure: it says what a -test does, while canonical core says which element is one. Its name is stable, -so a testing package loaded beside a second copy of core reaches the copy -expanding the document without either comparing the other's private function. +Glob keeps its existing required `include`, optional `exclude` and value-returning +contract. With `as` it binds a sorted, deduplicated array of relative file paths +and renders no text. Generated evaluation supports ordinary fragment-local +bindings, structural constructs, declarative expression props over those +bindings, and pure components. It imports no caller bindings and exports none. +Executable fences, +imports and effectful components outside the selected permission remain refused. -Being a default is what makes the rule hold at the edges. A repository `Test`, -or a package that registers the name, is selected ahead of core's — so core's -definition was not expanded, and the definition that was receives the ordinary -disposition, whatever it is called and whoever registered it. +Core exposes Glob through the same captured Files-capability path as File, +including captured glob operations, identity, authored form and revocation. It +does not call ambient Files middleware as a fallback. No Plan-specific discovery +parser or replacement Glob component is involved. -Recovery travels by execution-owned structure: the element hands it to the -expansion of its own body as an argument, and expansion carries it to everything -the region causes — the branches, iterations, captures and answers written -inside it, the bodies of the components its elements invoke, and the content -those invocations project back, which the document wrote inside the region. It -reaches nothing else: a sibling after the region closes, a later invocation, or -a root all start outside it. Ambient replaceable state never decides it. Contexts resolve by name, so a separately -created context with the same name is the same binding, and anything able to -name one could otherwise turn a failed run into a successful one from outside -the document — which is precisely the decision a document's own text is -supposed to hold. +Evaluate renders the generated fragment exactly as ordinary XMD renders it. It +does not inspect component results or synthesize an observation collection. A +component written without `as` contributes its ordinary output; a component +written with `as` binds its result inside the generated fragment and contributes +no output of its own. Bindings are ordinary language constructs, remain local to +the fragment, and may be composed through pure components. An Agent that wants +structured findings authors that structure explicitly: -Testing is owned whole. Activating it means owning where results are collected, -when the last staged one is flushed, and what the run settles to — so the two -things that establish it are the two that own all three: a `useTesting()` -session for one root execution, and a `` element for a lexical subtree. -Registering the components is not one of them. +```mdx + + + +``` + +`Json` is a pure renderer, not an effect class. `allow` restricts effectful +component permission; it does not remove language constructs, fragment-local +bindings, or pure composition. Plan captures the fragment's rendered output with +ordinary `Let` and sends that text to the next Agent turn. Empty Glob matches are +an ordinary successful `[]` binding; they reach the next turn only if the +generated fragment renders them. -The public `testing` boolean and the recording middleware around it remain -composable policy, and stay useful as policy: a handler may narrow testing to -false, observe a result, refuse, and wrap recording. They are not activation -activation, and neither is a stable contextual or Api name — a name is the -routing every loaded copy shares, and treating it as the authentication would let -anything able to spell it declare a complete boundary. What a `` requires -instead is a conjunction of a private one-use request, a private live boundary -credential, and the acceptance record of a terminal created for that one call: -the same request-and-terminal division *Authoritative behavior* describes, -scoped to the smaller boundary. It is scope-owned and expires with the session -or the element that established it, so a retained credential from a finished -boundary activates nothing. +Generated expression props use a declarative data-expression subset rather than +the ordinary trusted-document JavaScript evaluator. The subset contains null, +string and boolean literals; finite JSON numbers, including a leading minus +directly attached to a numeric literal; fragment-local binding identifiers; +arrays and objects recursively formed from those values; and object shorthand +for a binding. Exponent overflow refuses. The leading minus belongs to the +number grammar; every other unary expression, call, constructor, operator, +assignment, spread, computed property, template and global access refuses in +whole-fragment preflight. Core parses this existing expression syntax with Acorn +and interprets the accepted nodes against the fragment's current binding +environment; it does not invoke `new Function()`. This is one reusable +untrusted-data boundary, not behavior special to Json. -Canonical core enforces it, and the placement is the point. What a test *does* -arrives through public middleware a handler may legitimately answer without -delegating — that is how a second loaded copy supplies behavior — so a -requirement asked inside that chain is a requirement a handler can skip by never -letting it be asked. The decision is therefore taken by the invocation itself, -before the harness is minted and before the behavior chain is dispatched, -through a seam the testing package installs its guard on. Core supplies no -policy there: it dispatches through a private per-invocation terminal and -refuses an invocation whose decision was never taken, so blocking that chain -refuses the test rather than admitting it. With nothing installed the terminal -is reached immediately, which is what keeps core-only use of the behavior -surface working. +Complete-fragment preflight rejects an unauthorized effectful name or form and +an unsupported executable construct before any read begins. File writes and +deletion, network, credentials APIs, tools, processes, Agents, elicitation and +services are not in the selected read table. +A later runtime read failure can follow an earlier successful read, but the +failed request supplies no partial findings to a later turn. -Every refusal raised while that decision is being taken is a configuration -failure of the composition rather than a test outcome — activation unproved, a -handler on the seam refusing with an error of its own, or a chain answered -rather than delegated. No result is staged, journaled, recorded or reported, the -body causes no effect, and the ordinary component-failure path fails the -document. +#### Recovery composes around Evaluate -The identity cannot be the error's type, because refusing by throwing is -supported and what a handler throws is its own. It is applied by position -instead: core marks whatever escapes the decision dispatch, keeping the message -it arrived with — the refuser's own sentence is usually the actionable one — and -carrying the original as `cause`, so the search that walks causes still finds a -fatal infrastructure failure underneath. +Evaluate keeps its existing public props and its existing throwing behavior. It +gains no `limits` prop, and the evaluation profile gains no deadline and no +output budget. A profile states permission and nothing else. -Containment is what makes the distinction load-bearing rather than tidy. A -contained test failure becomes a document failure only through a session's -completion policy, and a composition that never established one has no such -policy — so a refusal absorbed as a test outcome would leave a run that ran no -test reporting success. +Information requests add **no local deadline and no rendered-output size limit**. +Ordinary component behavior and the host's own limits are what bound the work, +and the command's existing `--timeout` continues to bound the whole planning +invocation. A request that reads a large file is an ordinary large read. +[#788](https://github.com/taras/executable.md/issues/788) owns a general +`` component as independent future work; this design deliberately adds +no one-off Plan timer and does not depend on that issue. -Canonical expansion keeps a refused decision away from every failure handler -itself, rather than asking one to decline it. That is a loaded-copy consequence, -not a preference: the mark is private, so only the copy that applied it can see -it — and that copy is the one whose own `` was expanding. A package -composed against a second copy of core would answer "not a refusal" about a -wrapper it did not apply and hand it to a handler, which is exactly the -containment being prevented. So nothing outside that copy is asked, no predicate -crosses a package boundary, and the refusal travels on unchanged, after the -fatal search and before any handler is dispatched. +What Plan composes around the public component is recovery, not limiting: -Retained output crosses the pre-persistence secret gate exactly as any other -journaled field does, and the gate examines what the boundary received — after -enclosing middleware, not before. Two consequences follow, and both are -intended: a host may redact a credential upstream so that the material never -reaches the gate at all, and a host that introduces credential-shaped text -upstream is refused by the gate like any other run that would persist one. -Output that is never retained is never scanned, because there is nothing to -persist and nothing was accumulated for a scanner to inspect. +```mdx + + + +``` -Which host paths retain is a property of the path, stated where the run starts: -`xmd run` and `xmd test` retain when the caller asked for a diagnostic trace and -keep nothing otherwise; a workflow retains, because it owns its journal and a -resumed procedure reads its process results back rather than re-running the -commands that produced them; programmatic execution retains unless its trusted -caller selects transient execution. A shared runner never decides this from what -it was given. +Plan owns one fixed recovery policy: after the child's structured teardown it +turns the one narrow, typed generated-request refusal into retry context and rethrows +every other failure unchanged. That policy is Plan's alone. It is not another +evaluator, another document root, a special Evaluate result, or a new public +XMD component, and it adds no public ``, `` or `` surface. -A channel is the operation the boundary receives bytes on, and nothing below it -reclassifies anything: no path hands one channel's bytes to the other channel's -operation, so the received channel is never restated and never has to be -recovered from a byte payload, from contextual state, or from state held for a -run whose channels are forwarded concurrently. A value root's stdout carries its -JSON result, so the command line shows a command's stdout on the stream the -result leaves free — visible, never mistaken for the result, and still recorded -as stdout, because stdout is what reached the boundary. +One Plan invocation permits eight information requests total across drafting, +repair and revision. Successes and safe refusals each spend one. They neither +consume nor reset the ten-draft and three-repair budgets. The ninth candidate is +not evaluated and no next Agent turn starts. Review explanations do not spend +this budget. -Forwarding, completion, cancellation and teardown belong to the executable -block's own Effection scope. Cancelling the block stops the child and the tasks -forwarding its output before the block settles. +#### Failure and settlement -## The root document a run is given +Public Evaluate continues to throw. One narrow exported classification lets the +surrounding composition separate a generated-request refusal from +everything else. It carries only a safe normalized reason, is recognizable +across loaded package copies through the repository's stable namespaced +descriptive tag, and confers no permission to recover. Matching message text or +treating every `GeneratedXmdError` as recoverable is invalid. -An `xmd run` starts from exactly one root document, and it comes from one of -three places: a path, standard input, or one `--eval` value. Which one is fixed -grammar — decided from the command form the caller wrote, before anything is -read — so a run never discovers its source part-way through preparation. +After successful teardown, malformed candidate XMD, unauthorized names or forms, +declarative expression, binding, construct, form and prop errors, invalid Syntax +requests, invalid glob patterns, and ordinary read failures such as missing or +unreadable files become actionable refusal context. No refusal masquerades as +successful Evaluate output. Reasons contain only safe normalized diagnostics, +not raw host errors, resolved paths or secret material. -The command form is what separates the three, because a path and standard input -can be written the same way. `-` is the one filename the option grammar leaves -unwritable, so it reaches the reference grammar through the parser's own -unconsumed tokens: bare `xmd -` names that file, `xmd run -#Section` selects a -section of it, and only an invocation that explicitly names `run` with a -document argument of exactly `-` means standard input instead. +Caller cancellation, the command's `--timeout`, stale input or admission, +corrupt history, absent or broken providers, a Files provider that throws or +answers with malformed infrastructure data rather than an ordinary `Err`, +installation failure, journal failure, secret rejection and unexpected runtime +failure stop authorship without another turn. A failure during cleanup is +terminal even when the request refusal would otherwise permit a retry, so +settlement must expose that distinction before Plan normalizes a refusal. +Existing execution reconciliation, which can preserve an ordinary document error +ahead of ordinary teardown failure, is not evidence that this narrower contract +already holds. -Standard input is the one of the three the shared CLI cannot reach for itself. A -process's own stdin is host-specific state, so each runtime-named entrypoint -supplies the operation that reads it, as a value passed into the CLI beside the -service installer, the upgrade assembly and the repository installer. It is not -a Context, a contextual Api, a public export, a syntax entry, a component or a -document capability: nothing an authored document can name reaches it, replaces -it, or asks it for a second read. The CLI calls it at most once per invocation, -and only for the one command form that selected it. +The request owner acquires resources only after their cleanup is established, +owns every task and listener, and closes its capture and protected route before +returning success or refusal. No detached evaluation survives a later Prompt. +Cleanup of the whole Agent session still belongs to the enclosing Plan frame, +not to each information request. -What comes back is admitted through the existing supplied-source protocol rather -than a form of its own. The origin `` and the exact text travel together, -which is what source positions, diagnostics, the root binding and the durable -root import all carry — so replay restores the retained source without reading -anything, and two different programs are two different roots without a second -identity field. `` is an origin and never a path: no file of that name is -read or created, and every relative operation still resolves from the contextual -working directory. +#### Durable ownership and continuation -Acquisition is the first thing the run does. The complete input is in hand -before document inspection, target and property preparation, Agent-stack or -provider setup, the secret-detection announcement, journal creation, root -admission and execution — so a host that cannot deliver a whole document reports -one fixed sentence, exits nonzero and has reached none of them. It happens -inside the run's existing deadline rather than opening a lifecycle of its own, -and a waiting read is cancellable: cancellation tears the reader down and stays -cancellation, never a read failure. +The Plan invocation owns request ordinals. The existing Agent response record +owns the exact candidate source, ordinary durable component records own the work +the fragment performed, and the authored `Let` binding owns the rendered +findings supplied to the next Prompt. Evaluate introduces no parallel +observation or result record. + +Evaluation and its internal admission/read records use occurrence-owned +identities in the existing execution's ordinary durable sequence. This is +journal composition, not a child document execution or profile installation. +Canonical execution supplies invocation identity and protected routes; no +caller-controlled DurableContext, stream, cursor, coroutine ID or durable owner +participates. Plan does not install a transient execution around its content. +Each admission or effect record is appended before its operation resumes and +becomes ordinary replay history immediately. Evaluate does not stage, hide, +publish or discard another component's records and changes no persistence +contract. Two Evaluate occurrences cannot consume one another's retained work. -## Contextual run configuration +A completed request replays without another Agent turn, fragment body, Syntax +lookup, File read or Glob traversal. Re-entry needed to validate structural +identity is not permission to repeat those effects. Interrupted work resumes +through ordinary replay: completed effects restore and the first unrecorded +effect runs. The generated projection and its structured teardown complete +before Evaluate returns and before later parent work begins. No incomplete +rendered findings are presented to the next Agent turn. -Nothing has a timeout by default. Three contextual values bound three different -things, and each has exactly one consumer: +Replay admission verifies both successes and refusals before any completed +invocation or root is reused. The fingerprint binds source, selected classes, +ordered admitted identities and forms, lexical documentation-reference identity, +and filesystem scope/policy identity. There are no bound values to compare. +It uses stable structural identity, never function serialization or replaceable +context state. Extra effect-description fields alone do not enforce comparison. +Changing any bound fact is stale before another turn or read. Absent, malformed +or inconsistent records fail closed, including a retained refusal for different +source. Authoritative checks cannot be skipped by public replay middleware. -- the **run deadline** bounds one whole `xmd run`, preparation and execution - together, and only the outer run boundary reads it. Its expiry is - cancellation, so structured teardown still runs; -- the **exec default** is what an exec block resolves and hands to the Process - operation, and only exec blocks and the built-in `timeout` modifier read it; - and -- the **Fetch default** is what a Fetch resolves when its caller named none — - including ``, which resolves it when the element declares no `timeout` - and retains the effective milliseconds as part of the request it recorded. +Unchanged completed findings remain historical information. Replay does not +refresh documentation, file contents or directory listings to compare them with +today's world. A different admitted provider/reference identity is stale; a +historical answer is not silently recomputed. The command's `--journal` remains +write-only JSONL and starts fresh; continuation belongs to enclosing durable +uses of the shared Plan component. -An absent value is no timeout rather than a number the runtime chose, and no -operation falls back to a value another operation owns — an exec block never -inherits the run deadline, and a Fetch never inherits the exec default. What a -document declares locally overrides only the default it names, for the region -that declared it. A per-call value always outranks a configured one. +#### Product interfaces -Layers outside these three keep their own defaults where a subtree needs one: -an agent prompt inherits what its enclosing provider declared, through the -agent layer's own Api rather than through the run deadline. +The [Plan interface owner](specs/plan-command-spec.md#information-request-messages-and-disclosure) +contains the exact Agent instructions, follow-up/refusal messages, progress and +help disclosure. Admission and settlement above explain why those messages +follow teardown and the execution-owned secret check. -Verbosity is a fourth contextual value in the same place, and it bounds -nothing. It says whether the scope reading it renders verbose-only content, it -is false until something says otherwise, and the command line seeds it for the -run from the `--verbose` it already resolved. `` reads it where the -element was written, so a component that installs a nearer value changes what -its own content renders and nothing else: the ancestors around it and the -siblings after it read the value that encloses them, restored by ordinary -Effection teardown whether the content finished, failed or was cancelled. A -value that is not a boolean fails where it is read, before the body it would -have decided expands. +#### Product verification -What the command line prints is not this value. The journal file, the event -echo on stderr, the observational output of a value root, and the testing -report's own verbosity stay with the host that owns those streams and read the -flag directly — so a document can decide what it renders and cannot decide what -the process presents. Contextual verbosity decides nothing: it takes no -part in durable identity, authorization, replay admission, retained-effect -ownership or journal identity, and no execution is admitted, substituted or -settled differently because of it. +The [Plan acceptance matrix](specs/plan-command-spec.md#acceptance) owns PI1–PI12 +and their incomplete-implementation controls, alongside the command and +component journeys. The current architecture defines the invariants those +journeys demonstrate; it carries no second product-verification matrix. -## Authoritative behavior +#### Delivery order and deferred access policy + +The delivery sequence is [historical](specs/decisions.md#dec-017-plan-information-requests--delivery-history). +The current implementation uses one shared ordinary profile, ordinary generated +structural constructs and the packaged Plan loop described above. Later +question-based Syntax selection (#787), a general Timeout (#788), workspace +selection and approval policy (#777), path-replacement hardening (#227) and +separate evaluation work (#679) do not change the current contract. + +## Resolution and extension + +Composition selects behavior, while canonical execution retains the decisions that middleware cannot replace. The following boundaries spell out that division. + +### Authoritative behavior **Authoritative** behavior decides what an execution *is*: whether it runs at all, what history it may replay from, what options it runs under, and what it @@ -3744,7 +4263,7 @@ Public middleware is non-authoritative by construction. `Execution` and may answer without delegating — so anything they could decide would be decided by registration order. They may refuse; they may not complete. -### Capability-backed execution +#### Capability-backed execution Canonical core is authoritative for document execution. It invokes the stable `Execution` middleware through a private, per-invocation same-name Api whose @@ -3795,7 +4314,7 @@ fresh, cause-free `ExecutionProtocolError`, before any journal read, expansion or append. Reaching the end of the chain without consuming the request is the same refusal. -### Capability-backed document expansion +#### Capability-backed document expansion The same division holds for `Execution.document`, with one structural difference: the `execute` chain records the options and **unwinds before** the @@ -3939,7 +4458,7 @@ normally — there is no canonical outcome for them to be ranked against. Cancellation keeps structured teardown semantics and is never reported as an ordinary policy failure. -### Capability-backed bound commands +#### Capability-backed bound commands `exec as="name"` turns a command's exit status into data, so two facts decide what a document ends up believing: that the block is bound — which is what @@ -3974,7 +4493,7 @@ A registry answers a name with whatever was registered under it, so admitting middleware a binding contract names. Ordinary blocks are unaffected: a document still reaches a registered modifier by name, a replaced `timeout` included. -### Capability-backed invocation identity +#### Capability-backed invocation identity A component that names a durable operation after itself decides which retained record a replay restores, so two invocations arriving at one name each replay @@ -4163,7 +4682,7 @@ name resolves to, keep an implementation, and hand a whole registration record back inside another execution, and what an invocation may name still comes from the execution that minted it and the resolution that selected it. -### Capability-backed nested execution +#### Capability-backed nested execution A Markdown test may run another document as a real root — its own root import, its own journal, its own scope and teardown. Nothing else may, and the boundary @@ -4290,12 +4809,17 @@ those components to be selected. A host that needs a closed first-party orchestration establishes that separately as part of that host's own execution contract. +A controlled `` may author an exact scenario label that the test host +alone maps to Plan's derived conversation identity. Declaration selection uses +the label; runtime state remains keyed by the opaque identity and child. This +adds no matcher or fallback to ordinary TestAgent sessions. + Scenario source, provider-private behavior journals and answer configuration do not enter the child journal. A selected first-party Prompt or Elicit still records its ordinary durable result through the child's production journal and secret gate, so replay reaches neither deterministic provider. -### Trusted host orchestration +#### Trusted host orchestration A **trusted host** is the code that decides what an execution is for — a CLI entrypoint, a workflow runner — as distinct from the document, the components it @@ -4331,7 +4855,7 @@ own `` is declared this way rather than registered with the other agent words, so a document run by a host that never mentioned it has no `` at all. -### Trusted durable preparation +#### Trusted durable preparation An installation may also carry a `prepare` hook — `DurablePreparation`, the trusted durable work a host performs *inside* the durable root. It is read @@ -4349,7 +4873,7 @@ or partial continuation they run and record through the ordinary durable protocol — an effect an earlier preparation already completed is restored from its retained record rather than performed again. -### The weak journal-provenance association +#### The weak journal-provenance association Journal provenance is the one further exception, and it is deliberately narrow. The canonical durable-streams module keeps the association between a stream and @@ -4391,88 +4915,7 @@ This exception authorizes only the canonical `JournalProvenance` association described here. It creates no general eligibility test for module-scoped state; any other exception requires its own explicit architecture decision. -## State across loaded copies - -The binary bundles its own package code while components and integrations may -load another copy of the same package. Those loaded copies must be able to -compose: one can provide shared state that another reads. - -Shared composition has two replaceable forms: - -- Composition data uses an Effection Context with a stable, namespaced string - name and a plain structural value. Independently constructed descriptors with - the same name intentionally address the same binding. -- Contextual operations use a contextual Api with stable, namespaced operation - names. Lexical middleware replacement is part of their contract. - -Neither form is a permission boundary. Security enforcement, durable-effect -identity, and reconciliation never trust a same-named binding merely because -its value has the expected structure. They derive from execution-owned state -established before untrusted work begins or from parsed durable records. A -contextual observation may describe an execution; it cannot authorize it or -name its durable effects. - -A private context name is not an exception to this. A descriptor built under a -name nobody else knows — a module-local one, a name carrying a fresh identifier -— is still a context, and reaching a capability through one is still reading -replaceable state to decide it. The same holds for a public answer that carries -an unreadable value: a registry record whose contents a handler cannot read is -still a record that handler can keep and hand back somewhere else, so what -cannot be read can still be transferred. A capability that must not move is held by -the execution and delivered as an argument, never looked up. - -Metadata covered by State ownership's module-evaluation exception uses a -stable, namespaced string property when another loaded copy must read it. A -module-local symbol is unreachable to another loaded copy. A registry symbol is -reachable but unowned, and offers nothing a namespaced string does not. - -A descendant may replace a same-named Context binding for its own descendants -under ordinary lexical rules. Private fields, constructors, symbols, -module-local identity, and private sentinels remain valid when no second loaded -copy needs to read the value. String names and metadata keys that cross loaded -copies are compatibility contracts; changing one is a breaking change. - -Unreachable to another loaded copy and unreachable to a holder of the object are -different properties, and only the first is what a module-local symbol gives. -A symbol-keyed property is an ordinary property: `Reflect.ownKeys()` returns it, -a descriptor read returns its value, and `Object.getOwnPropertyDescriptors()` -copies it onto a look-alike — non-enumerable and frozen change none of that. So -a symbol names state safely and hides none. State a value must carry but its -holder must not reach belongs in a private field, or in membership of a set the -owner keeps; both are absent from every key list, descriptor, and copy. A -capability boundary that a handler holding the object could read across is not -one, whichever of the two properties it was reasoned from. - -## The ordinary component prop boundary - -An ordinary component's props are JSON, and one resolver decides that for both -Markdown and function components — before validation, and before anything -durable is written. - -- **A successful `undefined` means the prop is absent.** The resolver leaves it - out rather than giving it a value, so the schema answers it: an optional prop - stays unset, a declared default is supplied, and a required one fails as a - missing property with no body run. That is how one authored invocation can - carry an optional identity a preceding result supplies later. -- **`null` is a value the author wrote.** It crosses the boundary as itself, and - only a schema that accepts it accepts it. Absence is never widened to it, and - never widened to a root a JSON round trip cannot describe: a function, a - symbol and a serialization that threw stay the failure they were, as do an - expression that threw and a name that is not bound. -- **The rule stops at the root.** Everything under it is the same native - `JSON.stringify` round trip as before — an `undefined` object member is - dropped, an `undefined` array entry becomes `null` — and no recursive - normalizer of ours replaces it. -- **Deliberate bypasses stay bypasses.** A capture is separated before this - resolver runs and receives the exact operand, `undefined` included; `` binds by reference; a construct that evaluates an arbitrary operand - keeps its own contract. -- **Absence is not durable state.** Prop resolution records nothing of its own. - No resolved prop, validated prop, retained event or replay-restored prop holds - `undefined`, and a replay reconstructs the same validated prop set through - ordinary expansion. - -## The Plugin boundary +### The Plugin boundary A **Plugin** is trusted code installed before an execution imports a root document. XMD bundles exactly one, `@executablemd/git`, and an operator adds more with a @@ -4576,7 +5019,30 @@ An isolated nested `` inherits none of it and installs the same Plugin *values* again in its own scope, with command `run` — which is what makes a run child of `xmd test` the run profile while the test root is not. -### The three contextual APIs a Plugin composes through +#### Ordinary repository vocabulary + +The bundled Git Plugin declares twelve ordinary shadowable defaults: +`Repository`, `Worktree`, `Git.Switch`, `Git.Add`, `Git.Commit`, `Git.Push`, +`PullRequest`, `PullRequest.Reviews`, `PullRequest.Comments`, +`PullRequest.Checks`, `IssueTracker` and `Issue`. Dir is core-protected and +remains available without this Plugin. One declaration array serves execution, +syntax inspection, Plan validation/generation and executing workflow actions. +A Workspace attachment installs providers and durable state, not vocabulary. +Declaration alone starts nothing: no provider, repository discovery, lock, Git +process or credential read. A repository component may shadow any of these +ordinary defaults. + +#### Command declaration ownership + +Command declarations contain everything one command declares, in one order and under one +discriminant: the exact Markdown components *and* the structural syntax the selected +Plugins declared, then whatever the calling surface declares for itself. Both arms, +because both are what a name means here — a catalog holding only the Markdown half would +let `xmd syntax` and ``'s validation describe a language a run does not have. +The command builds the value once so that an ordinary run, a nested `host="run"` child, +inspection and ``'s validation cannot describe four different vocabularies. + +#### The three contextual APIs a Plugin composes through Each is a value-returning contextual Api: the terminal answers with what the execution would use on its own, and a Plugin wraps that answer. @@ -4635,7 +5101,7 @@ and every existing encoding are byte-identical, and a Plugin whose future middleware must reject an incompatible retained history supplies an explicit `JournalAdmission` rather than having one inferred from its name. -### Where a consumer imports from +#### Where a consumer imports from `@executablemd/core/api` publishes exactly the consumer surface: `Plugin`, the three contextual APIs and their direct operations, and the contract types the @@ -4677,7 +5143,7 @@ four are the trusted-infrastructure boundary, and a Plugin reaches them there because a Plugin is trusted code. Both packages keep every root export they had; `/api` is additive. -## The declared Markdown boundary +### The declared Markdown boundary A trusted host may ship first-party Markdown, name it, and hand it to one execution. Everything about it is decided before the root document is imported. @@ -4703,7 +5169,8 @@ Admission reads the discriminant before it reads any other member, because everything else a declaration carries is a statement *about* exact Markdown — reading one of those from a value that never said it was Markdown is already treating it as Markdown. A declaration stating a kind this version does not -know, or stating none, is refused before the root document is imported. +know, or stating none, is refused before the root document is imported. The earlier +`"markdown"` discriminant is unknown here, not a compatibility alias. **The declaration is held to its own bytes.** The host states the kind, the name, the origin, the source and its SHA-256, and may state the props schema, @@ -4806,7 +5273,7 @@ and a name is not a secret, so a component could build one, reach the record and answer that everything is exact. It is released with its execution, reaches no public entrypoint, and marks nothing on the segments themselves. -### The review graph is the mechanism's first whole consumer +#### The review graph is the mechanism's first whole consumer `` declares one component. The code-review agent declares thirty-five, and reserves six registrations beside them, which is what the boundary looks like @@ -4877,7 +5344,12 @@ graph a review runs is complete in one package. A regression holds the copies byte-identical to core's, which makes a future divergence an explicit decision in the code-review package instead of something that happens to one of them. -## The installed structural syntax boundary +### The installed structural syntax boundary + +The [declared structural syntax interface](specs/executable-mdx-spec.md#declared-structural-syntax) +owns `Structural` and `ExpansionRequest`; [SR conformance](specs/executable-mdx-spec.md#tier-sr--expanding-installed-structural-syntax-53) +provides its behavioral evidence. This section owns installation, demand and +producer lifetime. A trusted host may declare a structural construct and the regions written directly inside it — syntax the engine has no branch for — on the same @@ -4885,6 +5357,13 @@ directly inside it — syntax the engine has no branch for — on the same list. Those declarations are the one source selection, inspection and non-executing validation read. +Admission validates every declaration before a root document is read: a valid +name and non-empty origin, canonical forms, a compilable schema, syntax examples +and description, and content meaning stated as prose or `null`. It also refuses +engine-owned or protected names, existing reservations, duplicates and private +closure collisions. The construct/region and handler rules below apply to the +whole admitted batch. + **A pair is one installation, and a host is held to that too.** An installation that declares structural syntax supplies the handler that expands it, and one that supplies a handler declares syntax for it. Canonical execution refuses @@ -5009,7 +5488,7 @@ segment appended before failing partway through, which is the reader's whether o not the segment that produced it finished. Cancellation stays cancellation and never becomes an ordinary failure. -## The canonical protected tier +### The canonical protected tier Resolution already had a protected tier — a reserved registration, or exact Markdown a host declared. Both of those are *a host's* name reservation, made by whoever @@ -5017,8 +5496,8 @@ assembled the run. Above them sits the engine's own, and a name in it means the same thing in every execution: whichever host built it, whichever package registered what, and whatever the repository holds. -`` and `` are in it. `` describes the vocabulary of the site it is -written at, and a description of a run's vocabulary that anything in the run +Five names are in it: Syntax, Evaluate, File, File.Delete and Dir. +`` describes the vocabulary of the site it is written at, and a description of a run's vocabulary that anything in the run could answer for is a description of nothing — the value of the answer is exactly that nobody but core produced it. @@ -5036,9 +5515,12 @@ declared Markdown and a workflow bundle member are each refused at admission before the root import, and a repository `Syntax.md`, `Syntax.ts` or directory candidate is never probed for because selection has already answered. -**Invocation is canonical.** The execution mints one identity domain per -protected component, calls its factory with that domain's invocation authenticator, and registers -what comes back nowhere. Import is closed for exactly those names, on the terms a +**Invocation is canonical.** For Syntax and Evaluate, the execution mints one +identity domain per component, calls its factory with that domain's invocation +authenticator, and registers what comes back nowhere. File, File.Delete and Dir +instead use the fixed, deeply frozen ordinary core definitions built once per +loaded copy. Their bodies still reach contextual Files and effective Permissions; +protection fixes the implementation and grants no filesystem permission. Import is closed for exactly those names, on the terms a declaration's names are closed: `Component.importComponent` middleware may observe the import, delegate it and refuse it by throwing, and what it cannot do is answer one — a witness is issued where canonical execution produced its answer @@ -5081,6 +5563,12 @@ route. Durable records keep their existing structural identities, forms, source, profile inputs and results; they contain no callable permission. Continuation rebuilds routes from the resuming execution's own verified answers. +Syntax accepts only a self-closing form and one optional closed `names` prop. +A paired spelling, unknown prop, empty or duplicate list, or non-string member +refuses before authentication or a read. Ordinary `as` captures the rendered +text and emits nothing. A cancelled symbols read completes teardown and commits +nothing. + **The named form is a second question.** Bare `` answers *what may I write here*. `` answers *how do I use this one*, and the two read different inputs on purpose. The reference therefore carries a @@ -5194,7 +5682,7 @@ component failure, and refuses before output or binding. Two authored occurrences are two identities and two reads, and repeated reads of one binding read nothing again. -## The syntax symbols boundary +### The syntax symbols boundary `xmd syntax` answers what a document may write here, and answering must cost nothing. The boundary that makes that true is one operation that decides nothing. @@ -5323,7 +5811,7 @@ nothing. First-party declarations hold themselves to a stricter rule — every o states a description and decides explicitly whether `as` and `context` apply — which is a discipline inside those packages, not a requirement on anyone else. -## The document validation boundary +### The document validation boundary Validate a supplied document as authored program structure before any part of it runs. The operation receives one `RootDocumentSource`, root props, contextual @@ -5425,103 +5913,6 @@ operation the document authored. Those prohibitions are one boundary rather than a test fixture's list of components: adding a component cannot make validation effectful. -## Construct inventory - -Status is measured against main. - -| Construct | Does | Status | -| --- | --- | --- | -| `xmd syntax` | describes every structural construct and every selected component the production `run` profile would let a document write in the contextual working directory, as deterministic Markdown or as version-2 JSON, from one construction. `xmd syntax Elicit` names one component instead and renders its symbol metadata followed by the long-form documentation the owning package ships, through the same selection, index and renderer `` uses. Inspection only: it registers the run profile's declarations in a bounded scope and reads the filesystem for which files exist and, for a selected Markdown component, that file's frontmatter. It runs no body, imports no repository TypeScript module, installs no provider, mints no capability and writes no journal. An include it cannot enumerate — a selection-relevant symbolic link to a directory beneath it included — fails the whole request rather than printing a healthy subset. The Markdown renderer is core's rather than this command's, because canonical `` prints the same symbols for a running document and the two must be the same bytes for the same site | built on the #632 stack, with the shared Markdown renderer added on this stack | -| `` | outputs the components and control-flow constructs a document may write at the site it is written at, as the Markdown `xmd syntax` prints — one construction and one renderer, so an operator and an agent are never told different things about one profile. Self-closing only, with one optional closed `names` prop, and a text component: the bare form lists the symbols available here, `` renders those components' metadata and the long-form documentation their owning package ships, and the ordinary `as` captures the same text and emits nothing — while a paired spelling, an unknown prop, an empty list, a duplicate or a non-string member refuses before anything is authenticated or read. It is the one member of the canonical protected tier, resolved after structural syntax and ahead of every host or author tier: a repository `Syntax.md`, a bundled `Syntax`, an ordinary or reserved registration, a host's declared Markdown and an implementation from a second loaded copy can none of them answer for it, and `Component.importComponent` middleware may observe, delegate or refuse the import without being able to answer one. What the symbols say is the execution's own — built from the selection inputs it captured before any installation, middleware or document code ran, or from the one set of symbols a trusted host stated for its profile — and they are carried lexically on canonical core's execution environment rather than through any context. The documentation the named form reads is collected the same way: each package's bootstrap contributes its own through the additive `Documentation` Api, canonical core is the terminal, and the execution collects once after the trusted host's bootstrap and snapshots by value before the root import, so middleware a running document installs composes into a chain nothing reads and two contributions that disagree about one owning package and component name refuse at collection whichever order they were bootstrapped in, while a repetition of the same four values — owner, asset, exact text and component set — adds nothing and succeeds, because one package is deliberately bootstrapped at more than one layer. Each occurrence authenticates against the identity the execution minted, performs one `syntax_symbols` read, and retains a closed value carrying exactly one member — `{ symbols: string }` for a successful rendering, or `{ refused: non-empty string }` for a named selection canonical core refused, interpreted only after publication succeeds so it means the same thing live and on replay; a continuation hostile-parses that record and answers from it without rediscovering a moved environment, while existing `{ symbols }` histories stay readable and a missing, additional, mistyped, empty or simultaneous member is stale input that refuses before output or binding. A cancelled read completes its teardown and commits nothing. It reports itself under its own origin kind, `protected`, never as a reserved registration, and selection records `{ kind: "protected" }` alone — replay asks this execution for the implementation it built rather than resolving the name again. It decides nothing at all: a component the symbols name is neither registered, resolved nor authorized by being named | built on this stack; the narrower reference a trusted evaluation boundary installs for its subtree is the seam #713 fills | -| document validation | validates one supplied root projection and the recursive Markdown source closure normal component selection discovers, returning deterministic version-1 document diagnostics and `valid`, `invalid` or `not-statically-checkable` invocation outcomes without evaluating document code or installing operational host behavior | built on the #654 stack | -| `xmd plan` | turns one Prompt into approved Plan source and delivers it, by executing exactly one root document and starting no program. That root is the packaged plan command document, under the internal `` identity — an adapter that projects the request into `` and returns what comes back — and inside that, the packaged `` Component under the Plan writer profile its own `` installs: one enclosing Session, a host ceiling of one host-owned directory dedicated to that logical session — under `~/.xmd/plan/sessions` by default, keyed by the digest of the name, never the name, created empty and required to be empty before the provider exists or a session is materialized, refused rather than cleaned when it is not, durable when the caller named the session and handed back non-recursively after teardown when it did not — with no additional directories, no MCP servers, no native tools and a private strict denial no permission flag widens, no write, delete, directory-ensure, temporary-directory, command, service or network capability for that document — reading is not refused at all, because the packaged workflow is trusted source and takes the ordinary contextual route under the scope's effective `Permissions`; the #762 information-request boundary selects the shared ordinary profile's Syntax, File and Glob reads, no repository component search, and the Component's own private ``, whose closed assessment reports the structural defects the draft authored. The invocation settles one structural check — `validateDocumentStructure` under the ordinary run-profile registry, the `` identity, the caller's includes and the run profile's declarations — and ``, `` and the command's own final gate all ask that one, so what they can disagree about is when it was asked rather than what was asked. There is no caller-source defect to tell them apart from: the grammar is fixed and complete before a draft exists, because no generated document adds an option to a command whose result *is* the document. Its instructions require every Plan to begin with one descriptive level-one title and to keep the Prompt's outcomes as readable steps with each component beside the step it performs, through repairs and revisions alike; that is an authorship and human-review requirement, and `` never enforces it. A tenth draft that still has problems may be stopped or explained: the explanation is one more ordinary turn in the same Session carrying only the final diagnostics, is inert text, reopens no draft limit, and ends the command. The host's instruction layer states only that an answer belongs to the message that asked for it, so which shape a turn wants stays in the document. Authorship reports itself as it happens: the command root's rendered transcript is progress on stderr — one Markdown phase announced before each piece of work, its attempt and repair ordinals derived from the two bounds `Plan.md` binds once, with `--verbose` adding every cleared draft, every failed check's structured findings, and the committed information requests and results described in the #762 amendment — written through a private paired `` that renders its content, sends it through the current document-output operation and returns nothing, so a phase can never enter ``'s capture or the declaration's exact-source disposition. Which surface is asking and whether `--verbose` was written are sealed host facts, so the ordinary `` surface announces nothing and expands no progress body. The host owns the stream and the terminal alike: whitespace normalization for every invocation, terminal formatting only when the entrypoint states its own stderr is one, the transcript drained inside the scope that owns the execution, and a destination that stops accepting bytes cancelling the producer and waiting for every owned teardown before it reports — with no stdout fallback. Authorship's own durable stream is the host's choice and is written rather than read: a fresh invocation-owned in-memory one, or the file `--journal` exclusively created, holding the ordinary `serializeDurableEvent()` JSONL in commit order under the same serialized pre-append secret gate, so a rejected event reaches neither the file nor the committed sequence and the prefix before it stays readable. Nothing opens either as input, replays it, or resumes from it. Then, only after every provider, Prompt task and Elicitation resource inside the Plan writer frame has torn down, the Component's own `` structurally admits the exact approved bytes, and after that execution ends the host asks the same check once more — about a tree the whole teardown has had time to move, so a component the approved Plan names and something removed after that admission is refused here and nowhere else, and a Plan declaring properties a later run will supply is admitted rather than refused — and delivers it to exactly one sink: stdout byte for byte by default, or an exclusively created `--output` path. It starts no later root and retains no later execution, so the caller's own composition decides whether the program runs: `xmd plan … | xmd run -`, or an `--output` artifact a later `xmd run` names. Its grammar is what that leaves: one request, `--include`, `--agent-provider`, `--default-agent`, `--session`, `--timeout`, `--output`, `--verbose`, `--journal`, and ordinary help and version. The last two are spelled in full and observe this authorship alone — `-V` and `-j` are `xmd run`'s aliases for options about a program's run, and each is refused by naming the long spelling. Every option that configured the former execution — `--run`, the aggregate and generated root properties, `--raw`, the exec and fetch deadlines, the three permission flags and both secret-detection spellings — is refused by name in fixed preflight, before the general parser can drop or coerce a token and before `--help` can short-circuit the dispatch, in either order; `--run` reports the migration that names both compositions, and every other one reports that `xmd run` is where a program is configured. The same options are unchanged under `xmd run`. No permission mode is settled at all. The retired `prompt` spelling is not a command and is not absorbed by the default `run` grammar, which would read it as a document reference and execute a file of that name: an invocation whose exact first token is `prompt` is refused before any scan, selection or path lookup, establishing nothing, while `xmd run ./prompt` still executes a document legitimately called that | built on the #660 stack | -| `` | writes and reviews one Plan, from a Prompt an ordinary document wrote. `…` expands its paired body once with the capabilities the calling document already has — the complete untrimmed rendering is the Prompt, and it is never emitted separately — and produces the exact approved Plan source. It is a paired **exact text** component: the bare form emits that source into the calling document's own rendering, and the `as` form captures the same bytes instead. Neither form evaluates what it produced, and neither announces a phase: the progress `xmd plan` writes is a private side effect of the command surface, so an ordinary `` expands no progress body at all. It is the public name of the packaged `` Component: exact first-party Markdown declared to every ordinary run, so a repository `Plan.md`, a workflow bundle, a registration, `Component.importComponent` middleware and another loaded copy can none of them answer for it. Paired only, with one optional non-empty `session` prop and an optional `as`; a body that renders to nothing fails before any inspection, directory, Session, turn, review or check exists. The Agent writes under exactly the `xmd plan` fixed policy however broad the calling document's permissions are, and a host that supplies no Agent context refuses before placement — including the `` child an `xmd test` document launches, which is the run profile and therefore resolves the same protected bytes rather than reporting a missing component, and which supplies one when it declares a canonical ``. An authored `session` keeps the existing durable named-directory lifetime, so the same name at the same site reaches the same conversation next time, while an omitted one is site- and iteration-unique, replay-stable and handed back after teardown; sibling sites stay distinct even when they write one name, and the name never becomes a path. Every turn, answer, check, approval and admission belongs to the enclosing document's journal, so a continuation restores completed authorship instead of repeating it; there is no second journal. Complete authorship teardown precedes structural admission, which precedes the emission or the binding — and the admission is structure alone, so a Plan declaring properties a later run will supply is produced rather than refused. It creates no file and executes nothing it produced | built on the #660 stack; no delivery, custom root, policy prop or replacement selector exists (#536 owns constrained caller-authored policy) | -| `` / `` | chooses one branch by comparing a value with `===`. `……` decides its whole case structure from source before evaluating anything, then evaluates the selector once and each non-default matcher at most once in source order, expands the first `===` match — or the final default, or nothing — inline and transparently, and appends no journal event | built on the #692 stack | -| `xmd upgrade` | replaces the standalone binary that ran it with a published release, by executing one root document: the packaged upgrade command document, under the internal `` identity, with an empty component search path and no Files, Process, Service, command, Fetch, Agent, Elicitation, workflow or repository capability. That document is an **ordinary streaming text root** — it declares no `returns` and uses neither `` nor `` — so its rendered body is the command's output: each root segment reaches the reader as it completes, and a branch the command did not take contributes no prose, no phase call and no result. Its durable events go to one invocation-local in-memory stream, or to the file `--journal` named and the CLI exclusively created; neither is ever read back, and neither grants any resume or retry permission. Markdown owns the whole of the policy — the exact-tag grammar, which release is selected, semantic-version comparison through the npm `semver` package, which consent an install needs, the status, already-current and installation branches, and the wording of every refusal and every report. A compiled macOS or Linux binary whose platform the release publishes for is the only host that declares the four phases that policy may reach, ``, ``, `` and ``, and it declares them to canonical execution rather than through any contextual Api, middleware, repository lookup, ordinary `xmd run` profile or public syntax symbols; every other entrypoint states its provenance and no components at all, so an npm, Bun, Deno-source or compiled Windows invocation has no phase to reach and stops at its own refusal before release lookup or any filesystem change. That host alone owns the private half: the exact `process.execPath` spelling it will replace and never a link it resolved, one non-blocking exclusive advisory lock on a stable sidecar beside that file, the bounded anonymous GitHub reads under a scope-bound abort signal, the downloaded bytes, the digest, the staged candidate it runs for its version, and one same-directory rename. Opaque identity is the boundary between the two halves — a release identity per admitted release, then one candidate advancing `downloaded → verified → committed` exactly once, with one installation attempt per invocation — so the document chooses among the releases it was shown and can name no other release, target, asset or destination, skip verification or replay a phase. Before the rename every failure and cancellation leaves the installed file byte-identical; after it the candidate is authoritative and no cleanup restores the old bytes | built on the #659 stack | -| `` / `printErrors(fn)` | prints failures | built on main | -| `` | stops authored work with the sentence its author wrote, raised where it is written. An ordinary overridable core default — never structural, never reserved, so a repository `Fail.md` is chosen ahead of it — with a closed schema of one required non-empty `message` and **self-closing only**: a paired spelling never enters its body, and `as` is refused by the body itself because there is nothing to bind. Every refusal reports the invocation and happens before the authored message, so a document that never reached its decision is never reported as having made one. A valid invocation is the ordinary failure of a function component: an `Error` carrying the exact authored message, positioned at the opening tag, rendering nothing and binding nothing. It carries no `printErrors()` declaration, which is what leaves recovery to an authored `` region under ordinary text-root modes; a value body's `throw` is not replaced there, so the authored failure settles the body ahead of missing-`` settlement. No permission, context, provider, resource, module state or durable operation of its own: replay of a completed root restores the recorded outcome without re-expanding the body | built on the #659 stack | -| `` | binds one name in the current environment from exactly one source: the content it renders, or the exact value `value` names, bound by reference and never through the JSON boundary component props cross — the scanner resolves no JSON for that one prop, and expansion projects none. Which source it has is read from what the author wrote, before either one runs, so a construct naming both expands no child and evaluates no expression. It opens no scope, owns no resource, adds no middleware boundary and writes no journal record — replay reconstructs both sources through ordinary expansion | built on the #527 stack | -| `` | shows any supplied string inside one fenced Markdown code block, choosing a fence one backtick longer than the value's longest backtick run and never shorter than three, so the value cannot close it. An ordinary overridable text default: `value` and the optional single-token `language` are ordinary props on the closed schema, so a repository `CodeBlock.md` receives them the way it receives any other prop. Self-closing only, declared to canonical dispatch rather than decided in the body, so a paired spelling is refused before the body runs and its content never expands; prop validation refuses a missing or non-string `value`, a refused `language` and any unknown prop ahead of both. The value is returned unchanged — nothing trimmed, normalized, escaped or removed — and the framing line feeds belong to the envelope, with no line feed after the closing fence. Exactness is a promise at the function-component return and the `as` capture boundary; the `DocumentOutput` middleware contract governs emitted output after it. It owns no scope, resource, permission or durable effect of its own, so partial and completed replay are the ordinary ones | built on the #658 stack | -| `…` | renders its content only when the verbosity of the scope it expands in is true, and otherwise renders the empty string without expanding the content. It reads `Config.verbose` at the invocation, every time: the CLI seeds that value for the run from the `--verbose` it already resolved, and a component may install a nearer value that decides its own content and no ancestor's and no sibling's. It is a run-profile registered default, not core syntax and not reserved, so a repository `Verbose.md` is chosen ahead of it. What the CLI echoes to stderr is not this value and stays process presentation. It owns no resource, permission or durable effect of its own; replay follows ordinary expansion under the value that applies to that execution | built on this stack | -| `` | renders one supplied value as JSON text where the element was written, from one native two-space `JSON.stringify` call. An ordinary overridable core default whose operand is a capture: the exact evaluation result arrives by reference and is never mutated, cloned, replaced or frozen. Optional `as` is the engine's ordinary text capture rather than anything of this component's: it binds that exact string and the invocation emits nothing, which is what the single-child `` wrapper did. An expression-valued `as`, a literal `as` that names no binding, content, a missing `value` and an unknown prop are all refused before the operand evaluates. A value with no JSON text and a serialization that threw are distinct failures, each positioned at the invocation, emitting no partial output, binding nothing, and preserving the original error as its cause. No scope, resource, permission or JSON-specific durable effect: replay reaches it through ordinary expansion, and a surrounding `` or `` keeps its own record of the text it consumed | built on the #452 stack, with ordinary text capture enabled by #666 | -| root failure settlement | a text root installs the fail-capable `output` mode for its whole body, so an uncaught, undecided failure is the document execution's own outcome, whether or not the root declares `` | built on the #453 stack | -| `` region `output` mode | an undecided error fails the document execution | built on main | -| `` rendering selection | chooses which regions of a body render, and buffers a root that declares one; it decides nothing about failure | built on main | -| `Expansion` / `getExpansion()` | describes the current logical element expansion | built on main | -| `Component.expand` | surrounds one element's complete expansion — before resolution, through the accepted body and its owned cleanup, to the acceptance of what it produced. Core issues a branded request the public chain composes around; a handler delegates it once, may refuse the work by throwing or by not delegating, and decides nothing about the outcome. Phases carry `Result` and a detached, frozen report, so no canonical identity, binding or resource crosses. Completion follows the whole dispatch, middleware cleanup included. Each subscription is registered with the latest phase and then told each change in order, ending after one terminal observation; a slow, absent or cancelled reader delays and cancels nothing | built on this stack | -| `inspectSource()` | answers where a text's executable elements are written and what they are called, without resolving, compiling, evaluating or running any of it. The scanner's own reading, so fences, inline code, quoted delimiters and tag-like expressions are decided once; a document's body boundary is read lexically, without the frontmatter value parser, and definition parsing asserts the two agree | built on this stack | -| document targets | catalogs a root document's addressable static headings, resolves one selector to one exact target, and projects the document to it before expansion | built on the #412 stack | -| document-aware `xmd run … --help` | describes what one document declares and every target it addresses, each as a full document reference with the description its section states, by inspection alone | built on the #463 stack | -| standard-input root documents | `xmd run -` and `xmd run -- -` read the whole root document from standard input, once, to end of file, and run it through the ordinary run profile. Fixed grammar selects it — the explicit `run` command form plus a document argument that is exactly `-`, read from the parser's own unconsumed remainder so a `-` another option took as its value is not one — and every other spelling keeps the meaning it had: the shorthand `xmd -` executes the file named `-`, `xmd run -#Section` executes that file's `Section`, another command's `-` is that command's, and `--eval -` keeps its refusal. `-` is the one filename the option grammar leaves unwritable, so the reference grammar reaches it and nothing else beginning with `-` is read as a document. The parsed path and every recovered reference stay separate facts until the grammar is settled, so a command line naming two roots refuses in either order, before the read and before either candidate is inspected. The reader is a value each runtime-named entrypoint supplies and the shared CLI never reaches a stdin global; what comes back is `retainedSource("", source)`, adding no root-source variant, constructor, digest member or public API. The complete input is acquired before inspection, provider setup, the secret-detection announcement, journal creation, root admission and execution, inside the run's existing deadline; a failed read is one fixed sentence carrying no host error, input or path, and cancellation tears the reader down without becoming one | built on this stack | -| targeted `xmd run` | reads a file argument as a document reference and executes the one exact target its selector resolved to, replacing the selector before execution rereads the file | built on the #412 stack | -| targeted workflow definition | a workflow definition of either version optionally carries the exact canonical document target, which takes part in definition identity and in compatible reuse but not in the source-bundle hash | built on the #412 stack; `xmd workflow start` takes a document reference and resolves any selector through core against the bytes it is about to retain | -| source-bundle workflow definition | version 2 of the workflow definition is the exact bytes themselves: an entrypoint, a canonically ordered manifest of logical paths with each source's domain-separated SHA-256 and byte length, a bundle hash over that manifest and mapping, and the optional exact target and component mapping. The bytes are retained with the run in live schema version 2, so starting, resuming, replaying, forking and exporting need no repository and survive the original being edited, moved or deleted | built on the #443 stack; version 1 is unchanged, nothing migrates between them, and the artifact-backed fork remains unbuilt | -| installed structural syntax | a trusted host declares a structural construct and the regions written directly inside it, as the second arm of the same `declarations` list: name, origin, accepted forms, props schema, syntax examples, description, what the content means, and `parent` — `null` for the construct, the construct's name for one of its regions. The accepted regions are derived from the regions that named the construct rather than restated by it. The installation that declares them supplies the one `expand` handler, read once and bound at capture, and held on the admitted catalog entry rather than in any registry, context or second dispatch protocol. Admission refuses an invalid name, an empty origin, absent or non-canonical forms, an uncompilable schema, missing syntax examples or description, a context that is neither prose nor `null`, a name the engine's structural table or the canonical protected tier owns, a name a reserved registration reserves, a duplicate across either arm or across installations, a collision with a private closure name, an orphan region, a region of a region, a pair split across two installations, a construct with no region, declarations with no handler, and a handler with no declarations — all of it before the root document is read. Resolution places a declared construct in the host tier beside declared Markdown; `inspectComponent()`, `inspectSyntax()` and document validation describe and check it from the same catalog, with the structural entries sorted by code point beside the engine's own and the symbols unchanged at version 2. Expansion asks the catalog after every engine branch and before component import, settles placement, forms, `as`/`slot` and every construct and region prop, and then calls the captured handler once inside its own scope with a frozen request. Each region is an operation whose subscription owns one demand-driven producer: a chunk is delivered only to a waiting read, authored work waits for the read that permits it, output never reaches `DocumentOutput` or the journal, and leaving the handler's scope halts and joins every producer it entered | built on the #806 stack | -| declared Markdown component | a trusted host declares exact first-party Markdown to one execution as immutable data on an `ExecutionInstallation`, built with `Markdown({…})`: the host states the name, origin, source, its SHA-256, the accepted forms, an optional statement of the props and return that must agree with the parsed source, and an optional private component closure, and the constructor returns a fresh declaration carrying the required `kind: "component"` written after that description. It validates, hashes, copies and freezes nothing. Admission reads the kind before any other member and refuses a missing, unknown or superseded one — including the `"markdown"` earlier versions stated — rather than reading the value as Markdown, then parses the bytes and refuses a mismatched digest or schema, a non-canonical form, a name that is not a component name or is structural, a duplicate, a reserved-registration collision and a private name a registration also reserves. Resolution places it in the protected tier with reserved registrations, above the workflow component bundle, repository files and every registered default. Live import and retained history are held to the declared origin, digest and bytes, private names resolve only while canonical core expands the declaring bytes' own body — by the authored occurrence rather than by the name, so an answer kept from a legitimate private import authorizes no later site, no alias, no copy of the definition, no invocation that is over and no later execution — including one that declares no Markdown at all — while a private name written anywhere else resolves to nothing before the bundle, the repository or a registration can answer for it — and `xmd syntax` and document validation describe the declared contract from the same declaration without describing the closure. Closure is per name: only the declared component and its private closure become canonical imports, and every other name in the execution stays the ordinary open import middleware may still answer | built on the #660 stack; no public component uses it yet (#660 PR 2) | -| workflow component bundle | a workflow root declares a closed set of authored Markdown components; the workflow definition optionally carries them as one canonically ordered array, and an absent member identifies a run closed over no components — so a definition closed over none identifies exactly that. An entry maps the name onto a logical path the same source bundle retains. `start` reads every component from beside the root and retains it, and `resume` executes the closure the lifecycle authenticated; the array takes part in definition identity and is compared as part of the same descriptor in compatible reuse; and canonical core resolves those names and holds both live import and retained history to that exact bundle | built on the #301 stack; the full adversarial implementation loop and its scheduling remain unbuilt (#300), and generated XMD admits no bundled Markdown component (#369) | -| `getWorkflowRun()` | reads the run the current execution is associated with | built on the #366 stack | -| `retainedWorkflowInstallation()` | associates one document execution with a run storage already created, requiring exact journal agreement. It resolves no revision and names no Git feature, and a run retains the bytes it executes, so nothing afterwards reaches a repository for its source | built on the #366 stack | -| `GitQuery.resolve()` | verifies and resolves one Git revision expression contextually | built on main | -| workflow run storage | creates or compatibly finds one run by public run ID, retains its identity, state, document executions and filtered journal, and validates immutable Workspace roots through one provider-owned connection entry | built on the #365 stack; the CLI lifecycle reaches it on the #366 stack | -| caller-owned storage transaction | publishes several changes, including journal events, in one transaction nothing else enlists in | built on main | -| live durable-operation coordinator | explicitly coordinates structured live execution with existing Yield publication while leaving replay and callback effects unchanged | built on the #365 stack | -| Workspace coordination API | fails closed by default; replaceable context routes only a one-use provider selection, while the selected provider directly invokes an execution-owned credentialed capability for execution, publication and failure activation | built on the #365 stack; the Deno provider installs an adapter-private atomic handler | -| shared Git-host reconciliation | reconciles Git push and pull-request upsert through one provider-neutral state machine journaled as `git_host_effect` — Prompt is not a consumer and keeps its own Agent-provider contract: run and expansion identity derived by the engine, observation before mutation, adoption of a proven compatible completion, one performance of a proven absence, refusal of conflict and permanent ambiguity, temporary unavailability as its own ordinary failure, and a provider free to support only some kinds; routing is one request-only contextual operation that settles no completion, and only an answer the invocation's own terminal accepted can author a journaled outcome | built on the #297 stack; `Git.Push` is its first consumer, built on the #370 stack, and `` its second — a create-or-update upsert — built on the #295 stack. `` reuses these mechanics inside a boundary of its own rather than joining this one (#296): no shared state machine, no routing surface, no phases and no terminal, and its providers are ordinary `IssueApi` middleware | -| explicit WorkflowRun journal route | binds one already-filtered publication to one exact active transaction and otherwise uses ordinary serialized journal storage | built on the #365 stack | -| `API.Service` / `startService()` | creates an authenticated, supervised loopback service attachment through a provider-neutral operation | built on main | -| foreground command routing and retention | forwards a child's channels live, captures stdout for a `` region, and retains output only when the host asked for a record | built on the #441 stack | -| `exec as="name"` | binds one command's settled outcome — exit status, stdout and stderr — as an ordinary mutable binding; the block displays neither channel, renders nothing, and a nonzero status raises nothing. Only the built-in exec terminal and the built-in `timeout` may compose one, authorized by factory identity rather than by registered name, and asked for through a capability-backed request public middleware composes around but cannot issue, consume twice or answer | built on the #447 stack | -| testing harness (``) | runs another document as a real root under a production host profile, authorized by canonical `` alone: declarations installed before the root import, child output displayed progressively and collected only when asked, journal retention selected independently of observation, and the outcome published by the invocation's own terminal through a request public middleware composes around but cannot answer | built on the #454 stack for `host="run"`; the workflow profile and `` are unbuilt, and a host that offers no workflow profile refuses them | -| nested run-profile Agent and elicitation declarations | lets one `` declare one child-scoped `` scenario set and one non-delegating `` matcher set; only frozen test data crosses the harness request, the trusted host constructs both providers inside the isolated child, siblings share no session or provider state, ordinary component shadowing remains in force, and the child journal retains only the selected Prompt and Elicit components' ordinary results. A controlled `` may author an exact scenario label that this host alone maps to Plan's derived conversation identity; declaration selection uses the label while runtime state stays keyed by the opaque identity and child, with no matcher or fallback added to ordinary TestAgent sessions | built on the #641 stack; controlled Plan routing added on the #728 stack | -| `Config` run deadline / exec default / Fetch default / verbosity | three independently owned contextual timeouts, absent unless configured, each read by exactly one consumer, and contextual verbosity — a boolean that is false unless configured, seeded by the command line and overridable for a lexical subtree, bounding nothing and deciding no permission | built on this stack | -| Agent session configuration (`` / `Agent.options()`) | a `` says which model and effort level its conversation runs under, in exact provider ids, and no other element does. `Agent.session()` continues to route only a name or opaque placement; configuration is not a middleware argument. Each provider installation owns one private registration closure for its exact lifetime. An opaque placement carries an unreadable reference to its operation-local issuance, including optional frozen configuration and durable component identity; `with({ name })` preserves it, another placement substitutes its own, and a raw string or directly returned raw Session selects no configuration. The directly delivered provider coordinator accepts the final live placement once and completes it with the provider's exact Session plus either fresh state or one stable established-session configuration operation. Fresh completion performs no configuration work; established completion applies and verifies eagerly, and the returned canonical value is sealed into one opaque frozen session use selected lexically for nested prompts and launches. The owner refuses a different operation for an already registered exact Session, registration or lookup after teardown, and copied or foreign placements and uses; teardown drops its registrations after provider finalizers. No Context, module registry, public Agent operation, middleware-readable member, return value or Session property exposes the owner or operation, and the provider retains no desired-settings association. A string passed to `useConfiguredSession()` creates one operation-local placement with no durable component identity. Middleware may observe, refuse or reroute whole Sessions. The final routed value decides the operation: an authentic configured use applies and verifies its own settings; another authentic use applies its settings; a raw Session, name or absence is unconfigured; and a copied or foreign configured session use refuses before provider work. The journal records only the verified configuration the final route actually started under, never an earlier lexical request. Settings are applied under the Session's existing serialization and ownership — read, apply model, refresh for that model's effort choices, apply effort, verify exact equality — before a turn or native UI. Omission performs no configuration read or write. An unavailable option, unsupported capability, rejected write, mismatching readback or unverifiable restoration fails before the turn, restoring model first and reporting the original rejection; a restoration nobody can verify makes that exact Session unusable for the rest of the run. A prompt that started and a successful launch preparation retain one canonical `configuration`; a configuration refusal and an unconfigured operation retain none. Completed replay contacts no provider, and an incomplete configured launch reapplies that canonical value to the exact retained Session or fails. Released launch `requestedModel` and observational `model` strings are compatibility-reader inputs only: valid values are discarded, malformed values refuse, and neither can configure replay. A final configured provider-returned launch verifies before materialization, detach and native UI; a final configured client-allocated launch refuses before route, identity, private file or process, while middleware that routes an unconfigured Session receives the unchanged unconfigured contract. `Agent.options()` reports provider-ordered, grouped choices through one scoped unretained inspection session that sends no prompt and creates no XMD execution, journal or durable session, and `xmd agent options` renders them as text or version-1 JSON | built on the #828 stack; ACP translation lives entirely in `@executablemd/acp`, and no ACP or ACPX type crosses into core | -| native session launch (`` / `launchAgentSession()`) | prepares one durable coding-agent session from the rendered body of `` and hands the provider's native UI the terminal for that exact session, then continues the document after it exits. The body renders completely first and only what it rendered crosses as the instruction layer; the launch performs no model turn except an adapter's declared materialization turn, which runs at most once, only for a conversation the launch just created, and only with that adapter's fixed prompt; it takes the run's foreground-terminal lease before an agent is resolved. A host with no applicable terminal refuses without probing for an installed CLI. A session is constructed once, by one of two mechanisms, and its create-once construction route says which. Where the provider returns the identity, the ACPX provider creates the session, installs the layer at creation, releases ACP ownership before the spawn, and marks its handle stale so a later `` reattaches. Where the adapter names its own sessions, it allocates the identity inside ownership before any process exists, the native process creates the session under that name from a private mode-0600 instruction file, and ACP creates nothing — the instruction text reaches neither argv nor environment, and the file is removed on success, failure and cancellation alike while ownership is still held. Neither route converts into the other, and which one governs is chosen by the first operation that consumes the placement rather than by the `` that made it: a fresh `` publishes no route and establishes nothing, so a `` nested inside one constructs the session it placed, while a first subscribed `` publishes ACP-first before it ensures and keeps that account even if the turn that follows is never accepted. An established route is validated eagerly by a later ``, and a launch meeting a published ACP-first route refuses before an identity exists. A `` or `` meeting a bound client-allocated route attaches under the route's exact identity; a legacy unbound route or an unavailable attachment capability refuses before a turn and creates no substitute conversation. Phases are retained as `agent_session_launch` records under one expansion identity — `prepared` before ownership is released, then `materialized` where an adapter required a materialization turn, then `detached`, then `exited` — so a completed replay launches nothing, a replay holding only `prepared` proves the handoff never began and may still create under the retained identity, and one holding `detached` resumes and never falls back. The public route carries an opaque one-use launch request and answers nothing; the right to run and retain a phase is delivered to the installed provider directly, so neither a returned completion nor a rebuilt request authors a launch. Every operation that can act on an advertised session takes exclusive ownership under one natural key first, through a coordinator the host built and passed in; contention refuses instead of queueing, and an owner that never proved it stopped leaves a recovery tombstone. A host that cannot say who owns a session refuses every advertised operation, and one that cannot say how a session was constructed additionally refuses an agent that names its own — before any provider effect. Every private setup or child-creation failure is normalized to `process-creation-failed` with fixed provider-owned text, carrying no path, argv, environment or host message. No launch path discards persistent provider state. A session whose adapter pins a build is bound to that one build, whichever side named it: the build is observed inside ownership before an identity is allocated or a provider session is created, the binding is published with the bound route — V2 for client-native, V3 for ACP-first — and retained beside the prepared record, the native child runs the exact observed path in place of the launcher name, and every later create, resume, attachment and incomplete replay reobserves and compares before a process, an ensure or a turn. A `` or `` meeting a bound client-native route attaches to it: it reobserves the build, requires any retained provider arrangement to assert that same conversation, calls ensure with the route identity as `resumeSessionId`, and requires the provider to report that identity before a turn — refusing on missing capability, build drift, missing history or a differing assertion without creating a substitute conversation. ACP runtimes are partitioned by resolved agent command and binding, each handle is closed by the partition that created it, and a bound partition is torn down when its last handle closes. A legacy V1 client-native route keeps exactly the released native-only behavior and never attaches, and a legacy V1 ACP-first route refuses an agent whose adapter binds a build rather than gaining the current executable | built on the #517 stack, extended by the #519 and #561 stacks; Deno and the compiled binary assemble the host — coordinator, route store and executable observer — and Node and Bun keep the same advertised names while assembling none of it, so every advertised operation refuses before provider work; `claude` is advertised for native launch after passing the client-allocated gate at Claude Code 2.1.241 on macOS arm64 (#520) and separately for client-native attachment after passing the native-to-ACP marker gate (#561); `codex` is advertised for native launch after passing the provider-returned gate at `codex-cli 0.153.2` on macOS arm64 with the vendored `@agentclientprotocol/codex-acp` 1.6.2 snapshot that asserts the App Server thread on the response `_meta` (#755), which costs one fixed `codex-materialization.v1` turn because Codex writes the rollout `codex resume ` reads at a thread's first turn. That tuple is the compatibility point the advertisement stands on rather than an allowlist: admission accepts one canonical `codex-cli x.y.z` line, so another canonical version is admitted while bare, malformed, ambiguous or multi-line output refuses; exactness belongs to the session, whose accepted string becomes its retained executable binding and is reobserved and compared before every later create, resume, attachment and incomplete replay, so continuing a session across releases is not offered. Reading the ACP session id as the native identity stays forbidden; `Agent.AddDir` is unbuilt | -| `` | performs one XMD-mediated HTTP read through contextual `API.Fetch`, admitting the whole request before transport, and retains the normalized request and the detached response as one `fetch` durable observation; capture decides whether a status is data or a failure, and the trusted host's destination ceiling sits below the component | built on the #456 stack; a generated fragment may name the pinned identity only for a request the trusted host stated exactly, on the #369 stack | -| `API.Files` | routes every document filesystem operation to the installed provider, with no host default and structural failure data. Its mandatory semantic operations include `ensureDirectory`, which recursively creates or adopts one directory and returns Unit; separately loaded copies compose through the stable Api name | built on the #227 stack; directory ensure added by #643 | -| `` | removes one file the document names, inside the contextual working directory. An ordinary overridable core default with a closed schema of one required non-empty `path`, **self-closing only** — a paired spelling never enters its body, because the component declares its one form and canonical invocation-form dispatch enters that body only for the form the scan recorded, before `Env.cwd` is read and before the provider is reached. Neither the composable `Component.hasContent()` chain nor a method on whatever object a caller handed over takes part. It renders the empty string, declares no `returns` and hands back no receipt, so an ordinary `as` captures that empty string; absence is the same success, so deleting a path twice succeeds twice. One regular file or one final symbolic link goes — the link rather than its target, inside or outside — and every directory is refused, an empty one included. Empty, absolute, lexically escaping and parent-link-escaping paths are refused before any removal, and a printed error names only the path the document wrote. One semantic `API.Files.deleteFile` call and no filesystem access of its own; under a workflow run it is one `workspace_file` effect retaining `{ kind: "deleted" }`. The standard Deno workflow profile admits it to generated XMD as the exact self-closing identity `@executablemd/core#File.Delete`, third in the write table, where it performs that same ordinary effect and contributes no evaluator result | built on the #567 stack | -| host Files provider / `useHostFiles()` | resolves document paths in the caller's filesystem, containing them while the host namespace is stable; its directory ensure accepts Dir's existing absolute target or resolves a relative one against the enclosing cwd, recursively creates or adopts it directly, preserves existing contents, refuses a non-directory before content and never rolls back or deletes created directories; installed by all four CLI entrypoints, so Dir remains operational under Node and Bun | built on the #227 stack; directory ensure added by #643 | -| transaction-bound Files provider | resolves document paths in the run-owned logical Workspace inside the caller-owned transaction; directory ensure is one durable `workspace_file` mutation whose recursive creation, resulting root and recorded success commit together, whose documented refusal rolls its savepoint back, and whose replay restores the record without creating again | built on the #366 stack; directory ensure added by #643 | -| `service=` | publishes the attachment's endpoint into the live binding overlay for its invocation | built on main | -| `ephemeral eval` | reconstructs live middleware and bindings without a journal entry | built on main | -| `useWorkflowServiceDenial()` | provides a non-delegating workflow service denial provider, installed inside every start and resume execution scope | built on the #366 stack | -| `xmd workflow start` / `xmd workflow resume` | starts or resumes a workflow run from the CLI, under the Deno entrypoints only | built on the #366 stack; both acquire #367's executor lock before any lifecycle transition | -| implicit workflow Workspace | retains provider-neutral filesystem, repository and attachment state by run ID | document filesystem built on the #366 stack and Repository/Worktree composition on the #293 stack; process capabilities unbuilt (#218) | -| `` / `` composition under a workflow run | names a Git repository and its linked checkouts inside the run-owned Workspace, installs each as contextual working directory, and retains creation identity beside the retained Git bytes | built on the #293 stack, Deno provider only | -| `` | asks the mandatory provider-neutral `API.Files.ensureDirectory` operation to recursively create or adopt its target before content begins, then expands content with that target as cwd. Relative paths resolve from the enclosing cwd and absolute paths keep their existing meaning; an existing directory and its contents are preserved, a file or special target refuses before content, and the enclosing cwd is restored on success, failure and cancellation without deleting what was created. Dir changes cwd only and leaves the Repository selection untouched | directory-creation contract added by #643; host and workflow Files providers | -| transactional Git effects (`Git.Switch` / `Git.Add` / `Git.Commit`) under a workflow run | publish local Git mutations with their journal result; the enclosing Repository and the contextual working directory select which retained checkout one runs in, and neither observation admits anything — the observed record is compared with the retained row and the directory with the checkouts that row holds, so a failure of admission, of retained state or of an unrecognized native condition fails the run instead of publishing a result | built on the #294 stack, Deno provider only | -| `Git.Push` under a workflow run | publishes the selected checkout's exact current named branch and commit to the same branch on the retained Repository's canonical `origin`, reconciled through the shared Git-host state machine rather than through a Workspace transaction: no props and no component result, no force, no upstream mutation and no implicit staging or committing; the durable request and record carry the Repository's filtered identity without its checkout path, and the transport runs in a provider-owned isolated control repository reading the checkout's objects through an object-source attachment whose alternates chain and object tree are proven contained before the first remote observation, aimed at the exact private retained locator. A destination proven absent is published to once and one already naming this exact commit is adopted; one naming a distinct commit that same authenticated source proves is in this commit's ancestry is a performable pre-state, published over by the same exact non-force refspec and retained as the predecessor with the attested relation, while a divergent commit and one the source cannot read are both conflicts and nothing is fetched to decide either; a completed Push is reconstructed from the Workspace root its own journal event was appended against, read without publishing it or moving the run's frontier, so a branch published more than once resumes | built on the #370 stack, Deno provider only | -| `` under a workflow run | upserts one pull request of the selected checkout's current named branch, reconciled through the shared Git-host state machine: a required `title`, an optional positive-integer `number`, an optional `base` defaulting to the Repository's retained initial branch, an optional `draft`, and the rendered content as the body; it renders nothing and returns stable evidence through `as` — the filtered Repository identity, the provider's own stable pull-request identity, number, URL, open state, and the head and base SHAs of the snapshot it finished at. Without a number it creates one pull request for the head/base pair or adopts the compatible one an interrupted attempt left; with a number it brings that exact pull request's title, body, draft state and base to what the request says, records a no-op when they already match, and refuses a number belonging to another repository, opened from another head, or no longer open. It never pushes, never rewrites a head, and never reopens, merges or comments. The run must already hold its own successful `Git.Push` result for that exact Repository identity, head branch, destination ref and commit — proven by a scan of the whole successful history that requires each relevant record's natural key, inputs and result to describe one publication; a branch is published repeatedly, so the whole history is read in order and the run's last publication of that branch decides — an earlier one behind it is history rather than disagreement, while a last one naming another commit is the branch having moved on; that is conflicting, no relevant record at all is missing, and a relevant record that cannot be read whole is unreadable, each failing locally before the Git host is observed; the first adapter works over `github.com` on REST plus the two GraphQL draft transitions, selected from the private retained locator, credentialed from `GH_TOKEN`, then `GITHUB_TOKEN`, then the machine's own `gh` login, issuing each required mutation at most once per attempt and deciding the outcome by one observation, with the locator, endpoint, credential and payload confined to the per-invocation provider closure | built on the #295 stack, Deno provider only | -| repository composition vocabulary | one array of thirteen ordinary, shadowable registrations — `Repository`, `Worktree`, `Dir`, the four `Git.*` operations, `PullRequest` and its three evidence reads, `IssueTracker` and `Issue` — declared by the bundled Git Plugin and consumed wherever its profile is assembled: an ordinary document execution, `xmd syntax`, `xmd plan`'s validation and generation, and a workflow action that executes a document, so one vocabulary is described and resolved everywhere. A Workspace attachment installs this run's providers and durable state and declares none of it. Registering it installs no provider, discovers no repository, acquires no lock, spawns no Git and reads no credential; what a name does is the installed provider's. A repository-local Markdown or TypeScript component of the same name is chosen ahead of any of them | built on the #643 stack | -| `` / `` composition under an ordinary run | selects a managed checkout under `~/.xmd/repositories`, addressed by a digest of its whole identity and described by a closed version 1 sidecar written by exclusive temporary sibling plus atomic rename only after the checkout is complete and verified. The slot is entered under an exclusive non-blocking advisory lock held for the whole document execution, so a second process is refused rather than made to wait and a self-closing Worktree captured with `as` stays protected while a later sibling `` and an interactive Session use it. Reuse compares creation identity alone — the immutable request, the recorded creation facts, the canonical checkout and common directory, the object format, the admitted `origin` and the creation commit still being present — and never HEAD, the current branch, the index or the working tree, which are the mutable work the checkout exists to preserve; a conflict refuses and leaves every byte where it was, and nothing resets, switches, cleans, fetches, moves, replaces, repairs or deletes. A metadata-free slot is adopted only after the stricter pre-exposure state is proved — exact owner and locator, the branch and base this request resolves to, the creation commit still being HEAD, the object format, linked-worktree registration where applicable, and nothing in the slot but the checkout — and refuses otherwise. Written outside a lexical ``, a Worktree belongs to the ambient Repository; outside a Git checkout it refuses locally and names how to run inside one | built on the #643 stack, Deno and compiled only | -| local Git operations (`Git.Switch` / `Git.Add` / `Git.Commit`) under an ordinary run | perform the same authored transitions the workflow performers perform — named branches only, explicit Add pathspecs, index-only Commit, no implicit stage or push, and the same fixed provider Git configuration that disables hooks, signing, file-system monitors and repository-supplied helper programs — directly against the authenticated selected checkout. A commit records the invoking user's own effective Git identity, captured from the trusted host by the first `` that needs it and read back off the written object — a run that commits nothing asks the host nothing about who it is; a host that can name no identity refuses `` and names the commands that fix it rather than writing the workflow identity into somebody's repository, and every other component stays usable. They enlist in no transaction, roll back nothing and replay nothing, and a failure promises neither. Which checkout one runs in is decided by the Repository selection in scope and the contextual working directory, resolved through the provider's own invocation-owned checkout registry rather than through anything the selection says about itself | built on the #643 stack, Deno and compiled only | -| `Git.Push` under an ordinary run | keeps the same observe/adopt/fast-forward/refuse rules and the same isolated transport aimed at the checkout's admitted `origin`: a destination proven absent is published once, one already naming this exact commit is adopted, one holding a proven ancestor is published over by the same exact non-force refspec, and a divergent or unreadable one is a conflict, with an unreachable host never read as absence. It reconciles no Git-host effect and retains nothing. After a verified performed or adopted publication it stores one private evidence entry — the authenticated Repository identity, canonical checkout root, origin, named branch, destination ref and exact commit — in the provider instance's own closure. A checkout with no admitted `origin` refuses before a credential, a session or a transport exists | built on the #643 stack, Deno and compiled only | -| `` and its evidence reads under an ordinary run | share the URL matching, host ceiling, response normalization and low-level GitHub reconciliation, and differ in lifecycle and admission. A read is performed afresh every execution and retained nowhere. An upsert authenticates the Repository selection and the contextual checkout, reads the current named branch and commit, and requires the exact matching entry this provider instance already holds — a Push for another checkout, Repository, origin, destination, branch or commit is irrelevant, a later Push of the same destination supersedes the earlier entry, and missing or conflicting evidence is a local refusal before a credential is opened. Nothing crosses executions: a new run and a new `--journal` run each start with a new invocation identity and empty evidence, and copying a Context value, a component result or a previous trace file grants nothing. Within one invocation the attempt happens at most once; across a process interruption there is no exactly-once guarantee | built on the #643 stack, Deno and compiled only | -| `` under an ordinary run | reaches the same configured transport under the same host ceiling, with no durable envelope: identity is this execution's own opaque invocation identity together with the engine's expansion identity, so an upsert presents an idempotency key a provider can carry and a second run is a new request rather than a resumption. Absent or out-of-ceiling configuration installs no matching provider and sends no credential and no request | built on the #643 stack, Deno and compiled only | -| ordinary repository provider assembly | the Deno source entrypoint and the compiled binary install the live provider for `xmd run`, parameterized by the same credential-helper assembly the workflow host uses. It reads no configuration: `XMD_WORKFLOW_GITHUB_ISSUES` and `XMD_WORKFLOW_GITHUB_PULL_REQUESTS` belong to the GitHub adapter inside `@executablemd/git`, which reads and validates one when an invoked GitHub-backed operation needs it, so a run that opens no pull request and files no issue reads neither. Installing the provider acquires nothing at all — no managed root, no lease, no Git session, no identity, no ambient discovery; each of those is acquired by the first operation that needs it and kept for the rest of the execution. A nested `` child receives a fresh instance — its own invocation identity, leases and Push evidence — so nothing it publishes authorizes its parent or a sibling. The outer `xmd test` command installs none — it is not a run profile, and a nested `` child assembles one for itself rather than inheriting what its parent declined. Node and Bun register the vocabulary and install no operational provider, so every repository operation reports an absent provider before a lock, a credential, a subprocess or a request exists | built on the #643 stack | -| `` under a workflow run | asks one of two questions, decided by its own shape, through a boundary of its own rather than the Git host's. Self-closing with `url` reads that issue and binds `{ url, title, description, tags, assignee }`; paired with `title` upserts and binds exactly `{ url }`, its rendered content being the description. There is no `description` prop. Props are exactly `url`, `title`, optional `tags`, optional `assignee` and — on a read only — optional `provider`; no repository/token/label/milestone/project/comment/close or approval prop. Both forms render nothing. The form is decided before the tracker is read, before any provider is asked and before an `issue_effect` record exists, and that is where a mixed `url`+`title`, a read carrying content or `tags`/`assignee`, an upsert with no content, an upsert naming a `provider`, and an element that is neither are all refused. A read needs no tracker — its URL is the identity; an upsert requires the nearest lexical `` and takes its discriminator only from there. The tracker carries a credential-free `url` and an optional `provider`; the URL is canonicalized — a credential, a query and a fragment are refused rather than stripped — and a nested tracker replaces the whole value for its descendants, never merging members, with the enclosing one restored on leaving. It is composition data, not permission: the provider holds an adapter-private ceiling beside its credentials, admitted before it connects, so a target outside it sends nothing. One stable contextual operation, `executablemd.git.issue`, with `read(url, options)` and `upsert(issue, options)`; a provider is ordinary middleware around it, matching its own URLs without a discriminator and only its own name with one, independently per member, with no host-side resolution. Once middleware matches it owns the answer — it never delegates afterwards, and nothing catches its refusal to try somebody else — and a request everyone delegated reaches `NoIssueProvider` unchanged. `issue_effect` records an operation discriminator with the normalized request and result; both forms replay without reaching `IssueApi` and therefore without network access; only an upsert derives an idempotency key, from the operation, the canonical target and the run's own effect identity. Retention excludes credentials, endpoints, payloads, provider identities, origin markers and host paths. Observing, adopting, creating once and recovering an interrupted creation are the provider's, because they are knowledge about what a service can prove; title is never identity, and tags are a code-point-sorted set. The bundled Git Plugin installs the GitHub adapter itself rather than a decision about it: installing reads no variable, obtains no credential and opens no socket, and configuration is read only when a matching invoked operation recognizes a GitHub target. Absence is therefore not an uninstalled provider but an adapter that delegates, and the request reaches the boundary's own `NoIssueProvider` refusal unchanged — fail-closed either way, and without making startup depend on what a run turns out to do | built on the #296 stack; GitHub middleware, Deno host | -| workflow lifecycle inspection and control | reads status/list/history without advancing a run, recovering a private copy when a crashed source needs rollback; enforces the executor lock, refuses live cancellation, cancels non-live runs under that lock and deletes retained state | direct read-only inspection and control built on the #367 stack; coordinated recovered inspection built on the #513 stack, Deno provider only | -| XMD artifact export, inspection and fork source | seals one run's committed retained state, Workspace roots and workflow definition source closure into one immutable `.xmd` evidence file; opens that file read-only for status/history and admits continuation only by creating a new history fork whose lineage names the artifact identity. The physical container stays at schema version 1 while the semantic format follows the run: format 1 carries a Git closure, format 2 carries a source bundle as one entry/content pair per logical path under its own manifest version and `xmd-artifact\0v2\0` identity domain, and the reader selects the closed inventory and verifier from the header rather than reading either as the other | specified by `specs/xmd-artifact-spec.md`; the sealed container, its total read-only verifier, both semantic formats, `xmd workflow export` and artifact `status`/`history` are built, Deno provider only — the artifact-source fork remains unbuilt. Inspection is two sibling lifecycle operations, `inspectArtifact()` and `historyArtifact()`, taking a path rather than a run id: a run id names live lifecycle ownership and a path names immutable evidence, so neither is a mode of the other. They reach no run store, lock, Workspace, definition reader or external provider, and the artifact path never enters the structural answer | -| Agent session portability evidence in an XMD artifact | classifies every logical Agent session that contributed a retained Prompt as portable — with ordered provider checkpoint tokens and an opaque Agent session bundle — or as explicitly unavailable, as two content kinds inside the existing version-1 manifest and identity | specified by `specs/xmd-artifact-spec.md` §2.5; the closed union, both content kinds and the complete post-identity profile verifier are built on the #621 stack, Deno provider only. Provider bundle capture, Agent-aware export, intrinsic Agent-aware inspection and artifact-backed fork are unbuilt | -| historical authored source | retains an authored durable operation's normalized `SourcePosition` beside its identity, and history parses it or refuses the entry | built on the #367 stack | -| history fork | creates a new run from one compatible checkpoint and retained Workspace root, under a new immutable definition and normalized props | built on the #368 stack, Deno provider only | -| workflow Agent session | a workflow document's `` runs under a profile the host attaches only for a live or partial run: an empty host-owned working directory instead of any Workspace, checkout or caller path, no MCP servers, an empty requested native tool set, and `deny-all` with a permission path that denies every native request and fails the turn that asked without reaching the public permission chain. Within a run a session is identified by the Agent/Session expansion identity the engine derived — the authored name is descriptive, so two sibling `` elements are two sessions — routed inside a placement bound to its element and good for one use, so a kept placement cannot be substituted for the next. The conversation is retained as a row in the run's own database with the provider, resolved agent command and policy fingerprint beside it as compatibility attributes. The order is placement, the backend's acceptance of the session's first turn, the provider's canonical tagged assertion, then the mapping commit — and only then is anything that turn produced exposed. A placement is inert: it creates no provider session and writes no row. Occupancy of a provider key is not an assertion, and a record held for a first turn nobody accepted asserts nothing at all; the pre-commit window reconciles only from exactly one canonical assertion, and a missing, conflicting, replaced or ambiguous assertion is one explicit refusal that starts no replacement. Deleting a run removes the row with the run and the provider-session directory beside it, and reports the categories. The profile selects ACP-only capability explicitly — no native-launch advertisement and no client-native attachment advertisement — rather than inheriting the provider package's ordinary-run sets by omission, and it supplies no machine session coordinator, construction-route store or executable observer: a workflow session belongs to a run, and the machine-wide account describes a different thing entirely | built on the #302 stack, with the explicit ACP-only selection from #561; the portable proof that an adapter honours an empty tool set is tracked by #496 and does not widen the ceiling | -| generated-XMD admission | admits one Agent-generated fragment through the trusted-host seam: host policy is a `read` table and a `write` table of exact pinned identities, each carrying the authored forms it is admitted for, and an authored `allow` selects a canonical non-empty subset of the closed classes — omitted means `read`. The complete source is preflighted inside one `generated_xmd` durable effect before its first generated effect; only the pinned identity the selected classes hold for that name **and** that form executes; and the admitted source, class selection, selected root, every selected entry with its forms, the identity and form of each element named, and the normalized request policy are retained in that effect's own result — so a continuation restores the decision without reading the current candidate and expands only the retained source. The roots are an as-of-admission retained basis checked by membership — the run's own later root publications and an advanced retained current root pass, while a lost admission root or lost selected root refuses — and every non-root term is checked exactly, refusing a run whose classes, identities, forms or requests have moved. The admission and every nested generated effect are offered inline by the owning expansion in authored order, so a partial continuation restores each completed one without another live execution. Each admitted effect is retained by its own ordinary record, and every component keeps its ordinary binding and output behavior | built on the #369 stack, continuation basis amended by #589; core owns the mechanics and the workflow policy wrapper is internal | -| `` | canonical core's own protected component, written where program text the document did not author should run. Core reserves the name ahead of every host and author tier, so no registration, repository file, bundle member, declared Markdown component, import handler answer or second loaded copy replaces it; a handler may observe or refuse the import, and only canonical execution answers one. Protection settles which implementation runs and grants nothing: a host supplies the *ceiling* as one `ExecutionInstallation.evaluation` profile, captured by value before any installation runs, and an execution accepts one and refuses two. Its schema is closed on `text` and an optional `allow` array selecting a non-empty duplicate-free subset of the closed effect classes `read` and `write` — omitted means `read`. The two input forms are disjoint: `text` states the program, paired content renders it, and an element stating both is refused rather than resolved by precedence. A paired producer renders under the narrowed syntax reference through an execution-owned one-shot projection that bypasses the public `content()`/`tryContent()` chain, and keeps its own operational permission while doing so. Evaluate renders the fragment's ordinary output and invents no observation or result envelope. Fragment-local `as` bindings suppress their component's output normally, and language constructs plus pure components such as `` remain available regardless of effect selection; the fragment explicitly renders any bound values it wants to expose. Every ceiling comes from values the host captured at installation — the run's retained roots and its authoritative current root read from the run's own storage per invocation, as-of-admission provenance a continuation holds by membership so the run's own later publications and an advanced retained current root invalidate nothing, core's self-closing `` read — joined in the ordinary run profile by core's self-closing `` and canonical protected `` — the write table of core's paired ``, core's `` under the identity the host that assembles the profile states, since this package states no directory entry of its own — and core's self-closing ``, and `` only when the captured request ceiling is non-empty — and no prop, binding, context or middleware return value supplies or widens one. An entry states what is behind a name in one of exactly two ways, and neither carries a function. A *capability* names the component canonical core supplies for it: capture answers with core's own ordinary definition — the same ``, ``, ``, ``, `` or `` an author writes — held to the execution's lifetime, with the component's own form dispatcher travelling beside it so canonical selection enters the body the scan chose. The operation it performs is therefore the ordinary one, through contextual `API.Files` and `API.Fetch`, under the provider the host installed and the effective `Permissions` of the consuming scope; the host hands the profile no operation of its own. A capability states what it binds wherever the ordinary component does because it *is* the ordinary component, so an admitted `` is the value component the ordinary one is — sharing its props, return contract, source rules and sanitized failure sentence, refusing a missing `as` and an unusable pattern before the search, and resolving its paths against the contextual working directory. One name runs one component, so a name admitted for capabilities owned by two different components refuses at capture. An admitted `` is the one entry with a ceiling: the element is classified against the entry's exact admitted requests, performs the admitted one rather than its props, and that ceiling is installed as an ordinary `Permissions` restriction for the invocation, so a contextual transformation that moves the request off it is refused by the terminal rather than sent. A *component answer* names an implementation the ordinary import chain resolves, and states only the identity a provider must have registered for it; canonical execution resolves that name once — before the root import and before any document code, through the complete ordinary `Component.importComponent` chain and a private terminal that writes no record — and asks the identity owner about the exact final answer in one call. That call answers with the registered identity *and* core's own copy of what was registered, taken when the identity was registered: the check and the thing kept are one result, so nothing downstream reads the chain's object a second time and an answer whose members read differently on each read cannot pass a check with one reading and be sealed with another. The identity is then compared whole, and the copy — not the answer — is sealed behind the same revocation. One name states one identity, however many forms and tables hold it: two entries under one name are the two spellings of one component and share one lookup and one sealed implementation, while a second identity for that name, or a name held as both a capability and a component answer, refuses at capture rather than letting assembly order decide. Provider installation and the right to answer one occurrence are separate: installation receives a registrar that installs import middleware, and every invocation of that middleware receives a fresh request fixed to the exact name, position, provider installation, origin and resolution-window object it was asked under. Only that request may register an identity, and it states the answer plus key and revision without restating the name. Canonical execution closes the request synchronously when its handler returns, fails or is cancelled; an outer request remains live while it delegates and may register a replacement after the inner handler returns. Enclosing resolution still closes in its own `finally` on success, fallback, failure or cancellation. A registration therefore requires the execution, request and exact current window to remain active, the request's fixed name to match that window, and that provider installation not to have stated a different answer in the window. Identification takes the expected window explicitly and accepts only a registration recorded for that exact object and name, so a stale request cannot answer a later same-name resolution or retag itself while another name is live. The installation is reusable: one provider may answer several admitted names and repeated resolutions through distinct requests. All of this state is held in ordinary private closures — no Context, shared symbol, public brand or module-global registry — and an unidentified middleware replacement remains a valid ordinary import answer outside fragment evaluation. A capability entry also states the exact version-1 identity strings it succeeds, which is the only thing a released untagged record reconciles against; a component answer states none. A capability-only profile performs no such lookup at all. Resolution happens at capture and never again: a fragment runs the sealed snapshot, a continuation resolves once more in its own capture and reconciles before any effect, and a provider still answering when a fragment resolves its admitted name is answering a generated import, which only canonical execution answers. A trusted layering control may enter one package bootstrap through an inherited layer and an execution-local one — documentation, registrations and the provider together — and the repeat is additive: the outer entry observes and delegates rather than registering a second identity for an implementation that already states what it is, and a non-identical documentation overlap on one owner and component still refuses at the child collection boundary, before the root import and therefore before the provider is asked at all. `allow` selects among those tables and adds nothing to them; approval, when a workflow needs one, is authored control flow before the element. Its durable operation is named through that invocation authenticator, on the exact invocation the engine handed it and in that invocation's own frame — not from a context a document could rebind, a contextual Api answer, a definition, or a registry answer. Generated effects use occurrence-owned identities in the existing ordinary durable sequence: their records persist and replay normally, with no staging or rollback. The generated projection and its structured teardown complete before Evaluate returns and later parent work begins. It is deliberately not wrapped in `printErrors`, so a refused fragment stops the authored loop rather than becoming text the next turn could read as a read that happened | built on the #302 stack, extended by #369 | -| generated mutation proposals | lets an Agent propose constrained executable changes that a separate admission then performs against the run's own Workspace | built on the #369 and #567 stacks, with directory creation added by #643: the standard Deno profile's write table is core's paired `File:write`, the paired `@executablemd/workflow/composition/dir-v2#Dir` and core's self-closing `File.Delete`, in that retained order and followed by any host extension. `allow={["write"]}` intentionally authorizes Dir's persistent recursive directory creation; its versioned identity makes every continuation retained under the former non-mutating Dir identity refuse before generated execution. Admitted mutations run as the ordinary components they are through the run's effect transactions, and the evaluator adds no mutation API or receipt. Approval is authored control flow before the write-enabled element. Local Git, Git-host, issue, process, execution, credential and external-write effects are outside the class | -| Deno-local DOFS provider | owns one authoritative SQLite/DOFS connection per run path, captures arbitrary canonical retained roots, privately restores them, and atomically coordinates one Workspace mutation with its filtered Yield | built on the #365 stack; public document filesystem effects and the CLI lifecycle route to it on the #366 stack | -| scoped Worker Shell | executes `just-bash` through the Workspace adapter inside a Deno Worker | containment and effect-transaction POCs complete (#351, #357); production integration unbuilt | -| `` | retry a region until it completes | defined, unbuilt | -| `suspendFor()` | publishes one filtered suspension request, settles `suspended`, releases the executor lock, and re-enters the same wait without duplicating it | built on the #367 stack | -| `xmd workflow answer ` | retains one schema-validated value for one retained wait, taking no executor lock and changing no run state | built by #300 | -| `suspension_answer` durable effect | ends a wait from retained delivery state, publishing the answer and consuming that state in one transaction | built by #300 | -| `` · `` · `` | read the reviews, comments and checks a Git host already holds for one numbered pull request, completely or not at all | built by #576 | -| workflow scheduling (watchers, unattended iteration, remote host selection) | — | #300 | -| `` | binds `{ok: true, value}` or `{ok: false, error}`; a failure becomes a bound value, not a raise | defined, unbuilt | -| error middleware (JS api) | retry · suspend · decline | defined, unbuilt | - -`` composes with validation by nesting: a `` failing inside -fails the attempt — "retry until it parses" is two existing ideas. - ## The private REPL composition kernel `packages/cli/src/repl/` is a private CLI feature, not a package boundary and @@ -5539,6 +5930,163 @@ projection detaches every value it retains and freezes it, so nothing below holds a `DurableEvent` and nothing above can change one. Route resolution is pure and answers with the exact objects that model holds. +### Sequential entries over one physical journal + +One execution is one append-only stream, and entries are ranges of it. A segment +begins at the root `import_component` admission and ends at that root's `Close`; +only the final one may be unfinished. Each live execution is given a private +view over exactly its own segment, so a repeated root name and a reused +coroutine id cannot correlate across entries — the view reads only its range, +delegates every append to the physical stream, updates what it believes only +after that append is acknowledged, and refuses to write at all once its segment +is closed or is no longer the final one. No physical offset reaches Core; the +command's own observer holds the acknowledged prefix and computes the resulting +marker. This partitioning is private to the CLI: `DurableStream` is unchanged +and there is no physical-offset API. + +The whole prefix is partitioned and validated before any entry is projected, and +a malformed boundary refuses the model whole rather than yielding a partial +catalog — work before the first admission, a nested or repeated admission inside +an unfinished segment, a close with no open segment, work between a terminal +close and the next admission, a second unfinished segment, or a segment the +one-entry reader cannot read. + +Entry keys are `entry-1`, `entry-2`, and so on, from immutable admission order; +they are not Journal markers. The first entry keeps every marker spelling it +ever had, so every location written before this existed still resolves, and +later entries namespace their segment-local markers with their key. Projection +converts between the two at that private boundary, and Core sees only the +segment-local history it already understands. + +Values cross between entries through one trusted-host input: `executeInstalled()` +takes the root environment's initial bare bindings beside the source, exported +from `@executablemd/core/host` and from nowhere else — not a Context, component, +middleware answer, document prop or durable record, and not on ordinary +`execute()`. Core parses, name-validates and recursively copies the whole record +before any installation or document code runs, and the detached values are +ordinarily mutable inside the execution that receives them. The REPL derives +that record only from values durably retained before the new entry's admission, +so replay derives the identical record from the identical bytes. + +The session owns at most one entry task. Starting another needs two separate +facts — the preceding entry has a retained terminal close, *and* its task has +completely torn down and joined — because observing a terminal outcome is not +the same as the work being over. There is no queue: a submission that arrives +before both hold is refused, having started nothing, and teardown stops a +pending one from starting rather than appending on its behalf. Settled segments +are projected and never replayed; only an unfinished final segment is given back +to Core to continue. + +A location is resolved against the retained history before a session opens. +Opening one starts or resumes the execution, and replay past the retained prefix +appends — so a route naming a scope or drawer the file never held is answered +from the records already on disk, where being wrong costs nothing. Resolution +takes one fact the model cannot hold: whether this process is asking a question. +A waiting question is the one thing a Journal never records, so neither +`settled` nor the recorded elicitations distinguish a history that is about to +ask from one that never will, and a live drawer resolved from a history alone +would replay a whole execution to mount nothing. It defaults to *not* asking, so +a caller that cannot say refuses. The overlay's +output is announced on its own stream, because it is the one thing a frame shows +that leaves no record for anything else to observe. + +`specs/repl-spec.md` describes the product this assembles. The runtime-named +`{deno,node,bun,compiled}-repl.ts` modules state their own platform's data +directory, identifier and filesystem operations; nothing under `repl/` names a +runtime. + +### Navigation is the whole file; content is one prefix + +Every pane reads the selected prefix. The History band and its drawer are the +explicit exception, and the only one: they read a thin summary of the *whole* +validated file — a marker and a kind per retained position, plus the head — +because the positions after the selected one are where a reader can go next. +No label, source, name, value, outcome detail or observation is in it, so +nothing a later position holds reaches a reading of an earlier one. + +Both halves come from one acknowledged event array per wake: the full file for +the summary, the requested marker for everything else, through the projector's +existing whole-file validation. The full model is discarded from child +reachability rather than kept, so nothing downstream can walk to a later entry +through it, and navigation is never assembled by appending to a selected +model. A later malformed segment refuses the whole reading even for an earlier +marker — the alternative is stale navigation standing beside a prefix that +still looks plausible. + +The session hands both halves over together, as one reading, and the root +carries that one value through resolution, paint and resize rather than asking +again for either half. It has to: the root reads the terminal's size between +resolving a frame and composing it, that read suspends, and a record can be +retained while it does. Content taken before it and navigation taken after it +would describe two different files on one screen. A resize recomposes the +reading that was resolved at a new size, never at a new snapshot. + +When that reading cannot be built at all — a record this process wrote and can +no longer read — the root draws the refused view, the same screen a cold open +of the same file shows. That is a different outcome from a route that does not +resolve at its position: there, a reading still stands and the reason is said +beside it. A refusal has nothing to stand beside, so nothing is left on the +screen pretending to be current. + +The head is derived on read from retained facts and from work this session +owns, never from a body's return, a recorded root Close, a component badge or +the selected content. It changes while the file stands still, so it publishes +through the wake the session and the controller already send; History adds no +stream, timer or clock. The inspected action row keeps its historical +restrictions: navigation is the exception for the band, not for capabilities. + +### What one REPL command settles before it draws + +The command line is read, the Agent configuration is settled, and then one +immutable profile is assembled in the command's own scope: the selected Plugins, +the Agent identity components, the packaged `` declaration and the ordinary +evaluation ceiling, with the settled permission mode beside them. The program +takes that one profile and nothing else, so what an entry may resolve, what +ceiling a generated fragment runs under and how a permission request is answered +are facts about the command a person invoked rather than about the entry they +typed. The profile carries data and installations, never authority: no stack, +provider, Plan writer, live request or scope reaches the application model, the +route or the Journal through it. + +The `` the REPL declares is the packaged Component `xmd plan` runs, and the +ceiling around a returned program is the shared ordinary profile — whose write +table holds paired canonical `` beside `` and ``, so a +generated program may ask before it writes. That entry is a pinned capability +rather than a name the fragment resolves: preflight selects it where an +`` occurrence appears under a `write` selection, and the body it selects +is core's own ``, closed over before any document code runs. No component +name is resolved for it, so a same-name repository, registered, declared, +middleware or separately loaded definition inherits no grant and never runs, and +an execution that never writes the element resolves nothing. Only the interaction +is contextual: the pinned body asks through the Elicitation Api lexically in +scope, which is what lets the REPL drawer answer a generated question and an +`` region answer it without anybody being asked. + +Permission composes outermost-first: Core's own audit observer, then this +session's authoritative policy, then whatever the document and provider +installed, then the base denial. One private ledger per inherited Prompt scope +holds what was granted, and a retained audit is read rather than answered. The +live overlay, the application and the terminal have one owner each — the session, +the root transition boundary and the screen — and every ending cancels and joins +that owner rather than appending on its way out. + +Ownership in the model is read from the Journal and from nothing else. An +import's retained selection says where its scope's source came from — a path for +a component read from somewhere, the retained origin for one the host declared — +and that origin is the path the effects inside it are recorded against. A +generated fragment has no file, so the work inside it names the admission it was +decided under, and the scope that admission created is what owns it: the +identity chooses the candidate, and journal order and coroutine ancestry only +say whether that candidate could be its owner — an admission that happened afterwards, +or on work the effect is not part of, owns nothing however recently it ran. Two +sequential fragments are therefore sibling scopes, work in a fragment's spawned +descendants belongs to that fragment, and concurrent siblings are independent of +which finished first. A record naming a source this prefix never admitted, one it +admitted twice, or two sources at once is refused rather than attached to a +guessed owner. + +### Keyed composition and mounted ownership + Composition is keyed. A parent describes its complete child set; the reconciler judges all of it before mutating any of it, adds and updates before removing, then sets canonical order, and acknowledges the offer only after that commit. @@ -5736,10 +6284,10 @@ hands out a view into its own memory and an `info` that the next render invalidates. An older frame therefore keeps answering exactly what it answered. Recovery changes nothing above it. -Placement at narrow shows the one surface the route selected. The other surface -stays mounted and stays off the frame, which is what makes it unreachable rather -than merely hidden: it is in no cell, in no frame map, and no pointer resolves to -it. The inspection reading is different, and is not described there at all: a +Placement at narrow describes and mounts only the surface the route selected. +The unselected surface has no node, cell, frame-map entry, focus stop or pointer +target; both surface controls remain available, and application and journal +state are retained. The inspection reading is different, and is not described there at all: a narrow frame has no inspection region to put a binding row or a recorded answer in, and a row described into a region that does not exist is mounted, focusable and placed nowhere — a Tab stop drawing no cell and a pointer target behind @@ -5790,194 +6338,6 @@ finished, refused, failed, was cancelled or reached end of input, stops the reader, removes every listener, restores the modes and writes the final reset exactly once. -### The whole of it, and where the pieces are - -```text -DurableEvents -> frozen ReplModel -> resolved immutable view - -> keyed Freedom tree -> semantic layout -> tty frame - -> typed action back to the root -``` - -| module | what it owns | -| --- | --- | -| `model.ts` | projecting one validated Journal prefix into a frozen model | -| `route.ts` | the location grammar, and resolving one against a model | -| `journal.ts` | the retained NDJSON stream and the repository over a root | -| `session.ts` | admitting entries in turn, reopening one history, and the live overlay | -| `entries.ts` | partitioning one physical journal into entry segments, and the private stream view over one of them | -| `expansion.ts` | pausing and continuing expansion over the public seams | -| `elicitation.ts` | the one question shape this REPL presents | -| `description.ts` | opaque immutable descriptions and the closed action boundary | -| `reconcile.ts` | reconciling descriptions into one mounted Freedom tree | -| `handoff.ts` | the acknowledged commit boundary | -| `navigation.ts` | every retained position and the head, as the one reading taken at the whole file | -| `history-rail.ts` | the five-row History band, placed from engine measurements | -| `source-reading.ts` | one entry as output and then source, with what each element is doing | -| `presentation-text.ts` · `presentation-style.ts` | what the characters of a reading are, and the colours it is drawn in | -| `fitting.ts` | engine-measured widths, and the rows a reading is cut into | -| `layout.ts` | deterministic placement at four sizes | -| `renderer.ts` | drawing the mounted tree, and the frame map | -| `frame.ts` | the one acknowledged frame stream | -| `input.ts` | normalizing bytes into the closed event union | -| `terminal.ts` · `terminal-host.ts` · `screen.ts` | the terminal Api, its portable half, and the one owner of its modes | -| `components/` · `application.ts` | the screens, and the one state transition boundary | -| `storage.ts` · `program.ts` | where histories live, and the command as one scope | - -### Navigation is the whole file; content is one prefix - -Every pane reads the selected prefix. The History band and its drawer are the -explicit exception, and the only one: they read a thin summary of the *whole* -validated file — a marker and a kind per retained position, plus the head — -because the positions after the selected one are where a reader can go next. -No label, source, name, value, outcome detail or observation is in it, so -nothing a later position holds reaches a reading of an earlier one. - -Both halves come from one acknowledged event array per wake: the full file for -the summary, the requested marker for everything else, through the projector's -existing whole-file validation. The full model is discarded from child -reachability rather than kept, so nothing downstream can walk to a later entry -through it, and navigation is never assembled by appending to a selected -model. A later malformed segment refuses the whole reading even for an earlier -marker — the alternative is stale navigation standing beside a prefix that -still looks plausible. - -The session hands both halves over together, as one reading, and the root -carries that one value through resolution, paint and resize rather than asking -again for either half. It has to: the root reads the terminal's size between -resolving a frame and composing it, that read suspends, and a record can be -retained while it does. Content taken before it and navigation taken after it -would describe two different files on one screen. A resize recomposes the -reading that was resolved at a new size, never at a new snapshot. - -When that reading cannot be built at all — a record this process wrote and can -no longer read — the root draws the refused view, the same screen a cold open -of the same file shows. That is a different outcome from a route that does not -resolve at its position: there, a reading still stands and the reason is said -beside it. A refusal has nothing to stand beside, so nothing is left on the -screen pretending to be current. - -The head is derived on read from retained facts and from work this session -owns, never from a body's return, a recorded root Close, a component badge or -the selected content. It changes while the file stands still, so it publishes -through the wake the session and the controller already send; History adds no -stream, timer or clock. The inspected action row keeps its historical -restrictions: navigation is the exception for the band, not for capabilities. - -### Sequential entries over one physical journal - -One execution is one append-only stream, and entries are ranges of it. A segment -begins at the root `import_component` admission and ends at that root's `Close`; -only the final one may be unfinished. Each live execution is given a private -view over exactly its own segment, so a repeated root name and a reused -coroutine id cannot correlate across entries — the view reads only its range, -delegates every append to the physical stream, updates what it believes only -after that append is acknowledged, and refuses to write at all once its segment -is closed or is no longer the final one. No physical offset reaches Core; the -command's own observer holds the acknowledged prefix and computes the resulting -marker. This partitioning is private to the CLI: `DurableStream` is unchanged -and there is no physical-offset API. - -The whole prefix is partitioned and validated before any entry is projected, and -a malformed boundary refuses the model whole rather than yielding a partial -catalog — work before the first admission, a nested or repeated admission inside -an unfinished segment, a close with no open segment, work between a terminal -close and the next admission, a second unfinished segment, or a segment the -one-entry reader cannot read. - -Entry keys are `entry-1`, `entry-2`, and so on, from immutable admission order; -they are not Journal markers. The first entry keeps every marker spelling it -ever had, so every location written before this existed still resolves, and -later entries namespace their segment-local markers with their key. Projection -converts between the two at that private boundary, and Core sees only the -segment-local history it already understands. - -Values cross between entries through one trusted-host input: `executeInstalled()` -takes the root environment's initial bare bindings beside the source, exported -from `@executablemd/core/host` and from nowhere else — not a Context, component, -middleware answer, document prop or durable record, and not on ordinary -`execute()`. Core parses, name-validates and recursively copies the whole record -before any installation or document code runs, and the detached values are -ordinarily mutable inside the execution that receives them. The REPL derives -that record only from values durably retained before the new entry's admission, -so replay derives the identical record from the identical bytes. - -The session owns at most one entry task. Starting another needs two separate -facts — the preceding entry has a retained terminal close, *and* its task has -completely torn down and joined — because observing a terminal outcome is not -the same as the work being over. There is no queue: a submission that arrives -before both hold is refused, having started nothing, and teardown stops a -pending one from starting rather than appending on its behalf. Settled segments -are projected and never replayed; only an unfinished final segment is given back -to Core to continue. - -A location is resolved against the retained history before a session opens. -Opening one starts or resumes the execution, and replay past the retained prefix -appends — so a route naming a scope or drawer the file never held is answered -from the records already on disk, where being wrong costs nothing. Resolution -takes one fact the model cannot hold: whether this process is asking a question. -A waiting question is the one thing a Journal never records, so neither -`settled` nor the recorded elicitations distinguish a history that is about to -ask from one that never will, and a live drawer resolved from a history alone -would replay a whole execution to mount nothing. It defaults to *not* asking, so -a caller that cannot say refuses. The overlay's -output is announced on its own stream, because it is the one thing a frame shows -that leaves no record for anything else to observe. - -`specs/repl-spec.md` describes the product this assembles. The runtime-named -`{deno,node,bun,compiled}-repl.ts` modules state their own platform's data -directory, identifier and filesystem operations; nothing under `repl/` names a -runtime. - -### What one REPL command settles before it draws - -The command line is read, the Agent configuration is settled, and then one -immutable profile is assembled in the command's own scope: the selected Plugins, -the Agent identity components, the packaged `` declaration and the ordinary -evaluation ceiling, with the settled permission mode beside them. The program -takes that one profile and nothing else, so what an entry may resolve, what -ceiling a generated fragment runs under and how a permission request is answered -are facts about the command a person invoked rather than about the entry they -typed. The profile carries data and installations, never authority: no stack, -provider, Plan writer, live request or scope reaches the application model, the -route or the Journal through it. - -The `` the REPL declares is the packaged Component `xmd plan` runs, and the -ceiling around a returned program is the shared ordinary profile — whose write -table holds paired canonical `` beside `` and ``, so a -generated program may ask before it writes. That entry is a pinned capability -rather than a name the fragment resolves: preflight selects it where an -`` occurrence appears under a `write` selection, and the body it selects -is core's own ``, closed over before any document code runs. No component -name is resolved for it, so a same-name repository, registered, declared, -middleware or separately loaded definition inherits no grant and never runs, and -an execution that never writes the element resolves nothing. Only the interaction -is contextual: the pinned body asks through the Elicitation Api lexically in -scope, which is what lets the REPL drawer answer a generated question and an -`` region answer it without anybody being asked. - -Permission composes outermost-first: Core's own audit observer, then this -session's authoritative policy, then whatever the document and provider -installed, then the base denial. One private ledger per inherited Prompt scope -holds what was granted, and a retained audit is read rather than answered. The -live overlay, the application and the terminal have one owner each — the session, -the root transition boundary and the screen — and every ending cancels and joins -that owner rather than appending on its way out. - -Ownership in the model is read from the Journal and from nothing else. An -import's retained selection says where its scope's source came from — a path for -a component read from somewhere, the retained origin for one the host declared — -and that origin is the path the effects inside it are recorded against. A -generated fragment has no file, so the work inside it names the admission it was -decided under, and the scope that admission created is what owns it: the -identity chooses the candidate, and journal order and coroutine ancestry only -say whether that candidate could be its owner — an admission that happened afterwards, -or on work the effect is not part of, owns nothing however recently it ran. Two -sequential fragments are therefore sibling scopes, work in a fragment's spawned -descendants belongs to that fragment, and concurrent siblings are independent of -which finished first. A record naming a source this prefix never admitted, one it -admitted twice, or two sources at once is refused rather than attached to a -guessed owner. - ### What the screen teaches, and what leaving it costs Presentation guidance, visual emphasis and the contextual status a person reads @@ -6139,6 +6499,277 @@ This command installs no foreground launcher, no browser elicitation and no readline permission handling, reads no runtime global, and adds no durable record family of its own: what it retains is the ordinary events any other run writes. +### The whole of it, and where the pieces are + +```text +DurableEvents -> frozen ReplModel -> resolved immutable view + -> keyed Freedom tree -> semantic layout -> tty frame + -> typed action back to the root +``` + +| module | what it owns | +| --- | --- | +| `model.ts` | projecting one validated Journal prefix into a frozen model | +| `route.ts` | the location grammar, and resolving one against a model | +| `journal.ts` | the retained NDJSON stream and the repository over a root | +| `session.ts` | admitting entries in turn, reopening one history, and the live overlay | +| `entries.ts` | partitioning one physical journal into entry segments, and the private stream view over one of them | +| `expansion.ts` | pausing and continuing expansion over the public seams | +| `elicitation.ts` | the one question shape this REPL presents | +| `description.ts` | opaque immutable descriptions and the closed action boundary | +| `reconcile.ts` | reconciling descriptions into one mounted Freedom tree | +| `handoff.ts` | the acknowledged commit boundary | +| `navigation.ts` | every retained position and the head, as the one reading taken at the whole file | +| `history-rail.ts` | the five-row History band, placed from engine measurements | +| `source-reading.ts` | one entry as output and then source, with what each element is doing | +| `presentation-text.ts` · `presentation-style.ts` | what the characters of a reading are, and the colours it is drawn in | +| `fitting.ts` | engine-measured widths, and the rows a reading is cut into | +| `layout.ts` | deterministic placement at four sizes | +| `renderer.ts` | drawing the mounted tree, and the frame map | +| `frame.ts` | the one acknowledged frame stream | +| `input.ts` | normalizing bytes into the closed event union | +| `terminal.ts` · `terminal-host.ts` · `screen.ts` | the terminal Api, its portable half, and the one owner of its modes | +| `components/` · `application.ts` | the screens, and the one state transition boundary | +| `storage.ts` · `program.ts` | where histories live, and the command as one scope | + +## Terminology + +Terms are locked here. Two rules: + +1. Only use concepts and words described here or already defined in the spec + and code. +2. If a concept or term is missing, ask for permission before using it; using + it requires adding it here first. + +Existing documents and code get aligned to this section retroactively. + +"Authority" is not one of these terms. Each boundary it once covered is named +for what it actually does: canonical resolution, admission, configured +permission, ownership, coordination, publication, execution, or definitive +settlement. Two serialized spellings keep the older word because their bytes are +a compatibility contract, not a description: the durable `LaunchFailureClass` +value `"directory-authority"`, which a retained launch record is parsed against +by exact match, and the Files invariant category `"authority"`, which crosses +loaded copies and is matched the same way. Renaming either would break replay +and categorization rather than clarify them, so both stay exactly as written. + +| Term | Meaning | +| --- | --- | +| behavioral predictability | a plan as a program: ~80% deterministic, ~20% probabilistic [Reference](#three-axes). | +| replay determinism | the journal doesn't lose the execution chain: replaying arrives at the same state, where execution can continue [Reference](#three-axes). | +| replay | using the durable journal to restore the recorded portion of a document execution without repeating durable effects; after the recorded entries are consumed, execution may continue live [Reference](#replay-and-effect-transactions). | +| divergence | reserved for replay: a replay that departs from the recorded journal [Reference](#replay-and-effect-transactions). | +| REPL history | the immutable ordered execution records the REPL reads from the durable journal and projects into `ReplModel`; the History surface presents this read model and is not itself a journal [Reference](#the-private-repl-composition-kernel). | +| middleware | applied by the lexical structure, used by runtime execution [Reference](#two-layers). | +| workflow run | a workflow being carried out with its progress and outcome recorded durably; document executions perform its work, while ongoing effects remain scoped to the document execution in which they run [Reference](#workflow-runs). | +| document execution | one evaluation of a root document initiated through `execute()`, producing one output stream and one completion result while reading and appending a durable journal; its ongoing effects belong to the Effection scope in which the evaluation runs [Reference](#workflow-runs). | +| workflow definition | one closed retained source-bundle descriptor: `hashAlgorithm`, `bundleHash`, `entrypoint`, `sources`, optional `targetPath` and `components`. It carries no version, kind, Git base or pinned commit; exact target and component mapping participate in identity. [Source and identity](#identity-is-separate-from-retrieval). | +| source bundle | The exact retained entrypoint, source manifest and optional component mapping from which a workflow executes. [Owning rule](#identity-is-separate-from-retrieval). | +| logical path | a portable identity inside a source bundle, never a host filesystem path: NFC Unicode scalar values, `/`-separated, without leading or trailing separator, NUL, C0 control, DEL, backslash or `#`, and without an empty, `.` or `..` segment, compared case-sensitively by its UTF-8 bytes. The containing directory, the absolute path and the invocation working directory are retrieval facts and never identity [Reference](#identity-is-separate-from-retrieval). | +| workflow component bundle | the closed set of authored Markdown components a workflow root declares. A bundle maps each name onto a logical path the same source bundle retains. It is both definition identity and canonical resolution: the names it declares resolve to its exact retained sources and to nothing else [Reference](#the-workflow-lifecycle). | +| declared Markdown component | Exact first-party Markdown a trusted host declares to one execution. [Owning rule](#the-declared-markdown-boundary). | +| private component closure | A declaration-owned set of implementations available only at eligible lexical occurrences of its own body. [Owning rule](#the-declared-markdown-boundary). | +| retrieval metadata | replaceable, credential-free information about where a workflow definition can be fetched from now; it takes no part in run identity and is reauthorized by the host before use [Reference](#identity-is-separate-from-retrieval). | +| stop reason | why a workflow run or a document execution stopped: a categorical host code, or a reference to an already-filtered journal event [Reference](#the-workflow-lifecycle). | +| run ID | an opaque stable public identifier generated by the host or selected by an authorized caller; it associates the run's durable records and effects, remains unchanged for the life of the run, and has no semantics beyond equality and lifecycle addressing [Reference](#workflow-runs). | +| Repository base | the optional Git revision from which one named Repository initializes its primary checkout [Reference](#ordinary-repository-ownership). | +| Repository selection | Provider-minted composition data naming a repository and checkout, never permission. [Owning rule](#ordinary-repository-ownership). | +| ambient Repository | The lazily discovered repository enclosing the invocation working directory [Full contract](#ordinary-repository-ownership). | +| managed checkout | A persistent authenticated checkout with execution-owned locking [Full contract](#ordinary-repository-ownership). | +| ordinary Git identity | The live invoking-user identity captured only when a commit needs it [Full contract](#ordinary-repository-ownership). | +| ordinary invocation identity | An opaque provider-owned identity unique to one ordinary execution [Full contract](#ordinary-repository-ownership). | +| pinned commit | a Repository's creation commit, resolved once from its optional base and stable while its descendants run. A workflow source definition has no base or pinned commit. [Reference](#ordinary-repository-ownership). | +| document target | an addressable static heading in a root document's own Markdown flow, named by the canonical path of heading labels that reaches it; selecting one executes the preamble, each ancestor's own content, and that heading's complete subtree [Reference](#document-targets). | +| Prompt | a person's original request, in ordinary natural language. `xmd plan` takes exactly one [Reference](#plan-information-requests). | +| Plan | An inert executable Markdown program produced from a Prompt, with a descriptive title and readable steps. [Owning rule](#plan-information-requests). | +| plan command document | The packaged value-root adapter through which the Plan command obtains approved source. [Owning rule](#plan-information-requests). | +| packaged `` Component | The one first-party Markdown program that owns Plan drafting, repair, review and source emission. [Packaged component contract](specs/plan-command-spec.md#the-packaged-plan-component). | +| Plan writer profile | The fixed host assembly in which the packaged Plan authors source. [Profile and resource lifetimes](specs/plan-command-spec.md#the-plan-writer-profile). | +| upgrade command document | The packaged streaming text root that owns release selection, consent, refusals and reports. [Owning rule](#construct-inventory). | +| upgrade assembly | what one runtime-named entrypoint states about the `xmd` that is running: its provenance, reported version, invoked executable path, platform, architecture, release target when the release publishes one, and — for an eligible compiled macOS or Linux host alone — the factory for the four phases an installation needs. It describes how this `xmd` is running, never how its files arrived [Reference](#construct-inventory). | +| release identity | the invocation-local opaque identifier `` mints for each release it admits. Holding one is what authorizes downloading that release, and nothing outside that one invocation's private admission map can read, extend or forge it. A download mints an upgrade candidate identity in the same way, and that candidate advances `downloaded → verified → committed` exactly once [Reference](#construct-inventory). | +| expansion | one logical evaluation of an authored executable element within a document execution [Reference](#expansion-identity). | +| expansion ID | a deterministic identifier for one logical expansion; restoring or retrying that expansion preserves the ID, while a distinct evaluation requested by the document receives another [Reference](#expansion-identity). | +| Git capability | the contextual interface through which `@executablemd/git` queries the Git repository associated with the current working directory [Reference](#workflow-runs). | +| Git host | an external service that owns remote Git repositories and associated collaboration objects such as branches and pull requests. GitHub is one Git-host adapter. A Git host is distinct from the local Git capability and the trusted workflow host, and from an Issue provider [Reference](#replay-and-effect-transactions). | +| Issue provider | an external service that owns a collection of issues. GitHub and Atlassian Cloud are Issue-provider adapters. It is a separate boundary from a Git host because an Issue provider need not own a Git repository, and one that does not cannot truthfully persist as a Git-host effect [Reference](#issue-effects). | +| Issue tracker | what a lexical `` names: a credential-free URL for the container new issues are created in, together with an optional explicit provider discriminator. An upsert requires one and takes its discriminator only from it; a read needs none, because the URL it was given is the identity. It is replaceable composition data; it requests a destination and grants nothing [Reference](#issue-effects). | +| canonical target URL | the normalized form of an Issue tracker's URL. One destination has one spelling, which is what durable identity, the natural key and every provider comparison use [Reference](#issue-effects). | +| provider discriminator | the stable lower-case name of an Issue provider. Stated on a tracker, it names the only provider allowed to handle the request; omitted, each installed provider matches the URLs it recognizes [Reference](#issue-effects). | +| printed error | an error printed into the document (`ErrorSegment`); data, not control flow [Reference](#error-propagation). | +| failure | an error that propagates (`ComponentFailure`, `DocumentationError`) [Reference](#error-propagation). | +| error mode | what an undecided error in a region becomes: `print` (printed into the document), `output` (fails the document execution; `` can print instead), `throw` (fails the document execution) [Reference](#error-propagation). | +| attempt | one execution of a retried operation or region [Reference](#attempts). | +| raise | where an error no middleware converted is decided: printed or failed, per the error mode; observed exactly once, where raised [Reference](#error-propagation). | +| blocker | execution needs input (auth, a human answer); not a failure [Reference](#error-propagation). | +| suspension | a durable wait: a crash restarts into the same wait [Reference](#durable-suspension). | +| suspension ID | an opaque stable identifier derived by the trusted workflow execution for one logical suspension request; replay preserves it and only equality and input correlation are public [Reference](#durable-suspension). | +| delivery | Retained input for one exact durable suspension, separate from resuming or publishing its answer. [Owning rule](#typed-answer-delivery). | +| answer publication | appending the durable answer event that records what ended one durable wait; performed by the resuming document execution, never by a delivery [Reference](#typed-answer-delivery). | +| answer consumption | marking a retained delivery spent; it commits in the same transaction as the answer publication it belongs to, so neither can be observed without the other [Reference](#typed-answer-delivery). | +| workflow executor | the live host invocation that advances or settles one workflow run [Reference](#workflow-lifecycle-ownership). | +| executor lock | the scope-owned, non-blocking exclusive host lock that permits at most one workflow executor for a run [Reference](#workflow-lifecycle-ownership). | +| stale execution | an unfinished retained document execution found after acquiring the executor lock proves its previous workflow executor is gone [Reference](#workflow-lifecycle-ownership). | +| inspection recovery coordination | provider-private cross-process exclusion between copying a crashed run's database-and-journal pair for inspection and opening the retained database write-capably for SQLite recovery; it grants no executor or lifecycle ownership [Reference](#read-only-lifecycle-inspection). | +| recovered inspection snapshot | an immutable lifecycle snapshot read from a private scratch copy after SQLite rolls that copy's hot journal back to the retained store's last committed state; the retained database and journal are not changed [Reference](#read-only-lifecycle-inspection). | +| XMD artifact | one immutable portable evidence file with the `.xmd` extension, containing one workflow run's committed retained state at one artifact frontier together with its workflow definition source closure; it is neither a live run store nor permission to continue the source run [Reference](#portable-xmd-artifacts). | +| artifact frontier | the exact committed lifecycle snapshot, journal boundary and current Workspace root an XMD artifact records [Reference](#portable-xmd-artifacts). | +| workflow definition source closure | the exact root Markdown source and every source in its declared workflow component bundle, the retained bytes re-derived from the run's own store. Retrieval metadata, provenance and a repository checkout are not part of either [Reference](#portable-xmd-artifacts). | +| artifact manifest | the canonical versioned inventory of every semantic record and retained byte in one XMD artifact, excluding its own derived identity and physical container layout [Reference](#portable-xmd-artifacts). | +| XMD artifact identity | the lowercase SHA-256 of an XMD artifact's artifact manifest; it identifies the evidence independently of its host path and physical encoding and is retained as the source identity of a history fork made from that artifact [Reference](#portable-xmd-artifacts). | +| Agent session portability evidence | Verified portable session checkpoint tokens and bundle bytes, or an explicit unavailability classification. [Owning rule](#portable-xmd-artifacts). | +| provider checkpoint token | one opaque value a provider issued for one completed Agent Prompt, retained in Agent session portability evidence beside the event it belongs to. It says where a conversation reached; it is not a credential, is never derived from event position, transcript text, another token or a provider head, and no ordinary presentation renders it [Reference](#portable-xmd-artifacts). | +| Agent session bundle | the opaque bytes a provider captured one Agent session as, retained under that session's key in an XMD artifact. Nothing scans or scrubs them: a bundle may hold transcript text, tool state, secrets a conversation repeated and the exporting host's own paths, and it carries the confidentiality of the whole retained Workspace [Reference](#portable-xmd-artifacts). | +| Workspace | the provider-neutral, run-owned environment that supplies retained filesystem, repository, process and working-directory capabilities to a workflow [Reference](#workflow-workspace). | +| document filesystem | the files a document names in its own text, reached only through `API.Files`; distinct from the host paths the engine's own control plane reads [Reference](#the-document-filesystem-boundary). | +| Files provider | the installed implementation of `API.Files` for a document execution: the host provider under `xmd run`, a transaction-bound provider under a workflow run. There is no default, and no provider falls back to another [Reference](#the-document-filesystem-boundary). | +| stable host namespace | the condition the host Files provider's containment is stated under: no other process replaces a directory, symlink, junction or reparse point between the moment a path is observed and the moment it is used [Reference](#the-document-filesystem-boundary). | +| Files infrastructure failure | a Files provider that is absent, that refuses an operation, or that broke its own contract; fatal like a durability failure, and never a printed error [Reference](#the-document-filesystem-boundary). | +| ephemeral | a replay classification for an operation, context or attachment that runs again to reconstruct live execution; its result is not substituted from the journal and it owns no durable workflow state [DEC-034](packages/durable-streams/specs/DECISIONS.md#dec-034-ephemeral--explicit-escape-hatch-for-non-durable-operations-in-workflows). | +| live binding | an execution-owned value reconstructed ephemerally for the current document execution; it is visible only to constructs that explicitly consume the live binding overlay and never enters interpolation or the journal [Reference](#replay-safe-live-services). | +| attached service | a scoped host process that publishes its authenticated loopback endpoint through the XMD service handshake protocol and remains supervised for the lifetime of its service attachment [Reference](#replay-safe-live-services). | +| effect transaction | the single atomic SQLite transaction that publishes one Workspace-local mutation together with that effect's journal result [Reference](#replay-and-effect-transactions). | +| external effect | an effect whose provider-owned outcome cannot participate in the Workspace SQLite transaction and therefore requires a stable identity and provider reconciliation [Reference](#replay-and-effect-transactions). | +| XMD-mediated HTTP read | one bounded, non-mutating HTTP request an authored document performs through the contextual Fetch capability, whose detached response is retained as an ordinary durable observation. It is distinct from an Agent's own network permission, which it does not grant, and from a reconciled mutating external effect: it performs no reconciliation, so an interruption before its record commits may repeat the read [Reference](#replay-and-effect-transactions). | +| Git-host evidence read | A complete bounded observation of one canonical pull-request URL, retained as an ordinary durable effect. [Owning rule](#git-host-evidence-reads). | +| object-source attachment | Invocation-private authenticated local Git objects and destination supplied to one reconciliation. [Owning rule](#replay-and-effect-transactions). | +| checkpoint | a completed journal boundary associated with the logical Workspace root visible after that effect [Reference](#replay-and-effect-transactions). | +| history fork | a new workflow run that replays a compatible journal prefix and continues from its checkpoint and Workspace root under a new immutable document definition [Reference](#replay-and-effect-transactions). | +| loaded copy | one independently evaluated instance of a package, such as the copy bundled into the binary or a separately installed dependency [Reference](#state-across-loaded-copies). | +| authoritative behavior | behavior that decides what an execution or effect *is* — whether it happens, what it may replay from, and what it settles to. Non-authoritative behavior may inspect, narrow, refuse or add a failure, but cannot bring an execution into being, substitute one, or rescue one [Reference](#authoritative-behavior). | +| testing harness | the testing-only construct through which one canonical `` invocation runs another document as a real document execution under a selected host profile; it is not component composition and is unavailable to any element core did not recognize as its own `` [Reference](#capability-backed-nested-execution). | +| host profile | a named production assembly a trusted host offers a testing harness — `run` today — reused after argument parsing rather than restated; a host that cannot offer one refuses rather than approximating it [Reference](#capability-backed-nested-execution). | +| execution outcome | what one nested document execution ended as, as the harness publishes it: a settlement carrying the exact completion `Result`, or, for a workflow attempt, a durable suspension carrying the run and suspension identities. A suspension is neither success nor failure [Reference](#capability-backed-nested-execution). | +| trusted host | the code that decides what an execution is for — a CLI entrypoint or a workflow runner — as distinct from the document, the components it expands, and the middleware packages composed around it [Reference](#trusted-host-orchestration). | +| native session launch | Construction and ownership handoff of one durable session to its provider-native interactive UI. [Owning rule](#native-agent-sessions). | +| provider-native session identity | The provider conversation identity, distinct from XMD and ACP identifiers [Full contract](#native-agent-sessions). | +| pending session placement | One operation-local placement before its first consumer constructs a session [Full contract](#native-agent-sessions). | +| session materialization | The backend acceptance that makes a new conversation available to native resume [Full contract](#native-agent-sessions). | +| established session | An immutable route and asserted conversation identity [Full contract](#native-agent-sessions). | +| instruction layer | Provider-native instructions installed before user content [Full contract](#native-agent-sessions). | +| foreground-terminal lease | The exclusive execution-owned right to inherit the foreground terminal [Full contract](#native-agent-sessions). | +| native launcher | The host adapter that launches a provider UI and reports only its status [Full contract](#native-agent-sessions). | +| launch request | One immutable routing request that cannot author a launch completion [Full contract](#native-agent-sessions). | +| agent launch coordinator | The host-delivered capability that validates and retains launch phases [Full contract](#native-agent-sessions). | +| session coordinator | The host-delivered cross-process owner of one logical conversation [Full contract](#native-agent-sessions). | +| construction route | Create-once durable provenance of how a logical session was constructed, separate from its live owner. [Owning rule](#construction-routes). | +| executable build binding | The exact canonical provider version and executable digest accepted for a session [Full contract](#native-agent-sessions). | +| executable observer | The host-delivered observation of the executable that will actually run [Full contract](#native-agent-sessions). | +| client-native attachment | ACP attachment to the exact conversation constructed by a native process [Full contract](#native-agent-sessions). | +| identity provenance | Who chose the provider-native identity [Full contract](#native-agent-sessions). | +| quiescence | Acknowledgement that nothing the former owner started can still act [Full contract](#native-agent-sessions). | +| recovery tombstone | Retained active ownership that a crash alone cannot clear [Full contract](#native-agent-sessions). | +| provider partition | One independently owned provider runtime, store, session set and teardown [Full contract](#native-agent-sessions). | +| `JournalProvenance` | 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 is meaningful only because the provider retains the witness it established and later requires exact equality [Reference](#the-weak-journal-provenance-association). | +| result object | the value a component binds instead of failing: `{ok: true, value}` or `{ok: false, …}`, whose failure members the component declares [Reference](#construct-inventory). | +| syntax symbols | A versioned description of the structural and selected component vocabulary for one directory and host profile. [Owning rule](#the-syntax-symbols-boundary). | +| execution environment | One execution-private bundle passed by value through canonical expansion. [Owning rule](#capability-backed-invocation-identity). | +| syntax reference | The execution-owned lexical description of what may be written at one site. [Owning rule](#the-syntax-symbols-boundary). | +| run profile declarations | the component registrations a first-party package makes, held as plain values apart from the middleware, providers, activation and launchers its installer also arranges. The installer registers exactly those values and inspection reads exactly those values, so what a run installs and what the symbols report cannot drift [Reference](#command-declaration-ownership). | +| Plugin | Trusted explicitly selected code installed before a root document is imported. [Owning rule](#the-plugin-boundary). | +| document envelope | Plugin-composed Markdown into which canonical core splices the parsed root at `` placeholders. [Owning rule](#the-three-contextual-apis-a-plugin-composes-through). | +| active Plugins | the frozen ordered list of Plugin values installed for one command — the bundled Git Plugin first where the command's profile carries it, then the operator's own in the order they wrote them — published before the first `install()` runs so that every Plugin and every later consumer read the same list. It is a description of what is installed and grants nothing [Reference](#the-plugin-boundary). | +| command declarations | One ordered value describing the exact Markdown and structural vocabulary a command offers. [Owning rule](#command-declaration-ownership). | +| origin-only | the inspectability of a component whose contract could only be learned by loading it: a repository TypeScript module, whose schemas live on its exports and whose top level would run. Such an entry carries name, category, origin and source kind, and no contract field at all — an absent contract is stated, never rendered as an empty one [Reference](#the-document-validation-boundary). | +| definition-owned return state | Ephemeral value-selection state owned by one execution of one value body. [Owning rule](#definition-owned-return-state). | + +## Construct inventory + +Status is measured against main. + +| Construct | Does | Status | +| --- | --- | --- | +| `xmd syntax` | Describes the production run-profile vocabulary without execution, as deterministic Markdown or version-2 JSON. [Construction and observation](#the-syntax-symbols-boundary); [named documentation](#the-canonical-protected-tier). | Built. | +| `` | Renders the lexical vocabulary or requested component documentation under canonical protection. [Forms, capture, documentation and retained reads](#the-canonical-protected-tier). | Built; evaluation narrows the lexical execution reference. | +| document validation | validates one supplied root projection and the recursive Markdown source closure normal component selection discovers, returning deterministic version-1 document diagnostics and `valid`, `invalid` or `not-statically-checkable` invocation outcomes without evaluating document code or installing operational host behavior [Invariant](#the-document-validation-boundary). | built on the #654 stack | +| `xmd plan` | Produces exact approved Plan source through the packaged adapter and shared Plan component, then delivers it to one exclusive sink without running it. [Command grammar](specs/plan-command-spec.md#command-grammar), [profile](specs/plan-command-spec.md#the-plan-writer-profile), [progress](specs/plan-command-spec.md#watching-a-plan-being-written), [journal](specs/plan-command-spec.md#the---journal-file) and [final admission](specs/plan-command-spec.md#structural-host-validation-and-the-artifact). | Built. | +| `` | Authors one exact-source Plan inside the enclosing execution, using the same fixed writer profile as the command. [Packaged component](specs/plan-command-spec.md#the-packaged-plan-component) and [language contract](specs/executable-mdx-spec.md#53-import-durableimportcomponent). | Built; no delivery, custom root, policy prop or replacement selector (#536). | +| `` / `` | chooses one branch by comparing a value with `===`. `……` decides its whole case structure from source before evaluating anything, then evaluates the selector once and each non-default matcher at most once in source order, expands the first `===` match — or the final default, or nothing — inline and transparently, and appends no journal event [Invariant](#structural-branch-selection). | built on the #692 stack | +| `xmd upgrade` | Runs one packaged streaming policy document that may replace an eligible standalone binary after verification. [Policy](specs/upgrade-command-spec.md#the-packaged-document-owns-the-policy), [host admission](specs/upgrade-command-spec.md#the-runtime-neutral-host) and [atomic replacement](specs/upgrade-command-spec.md#download-verification-and-replacement). | built on the #659 stack | +| `` / `printErrors(fn)` | prints failures [Invariant](#4-printerrors-prints-a-failure). | built on main | +| `` | Raises the authored failure at its invocation, with recovery owned by the enclosing structure. [Authored failures](specs/executable-mdx-spec.md#682-stopping-deliberately-fail). | built on the #659 stack | +| `` | Binds rendered content or an exact by-reference value in the existing environment; source choice precedes execution. [Binding](specs/executable-mdx-spec.md#65-prop-validation). | built on the #527 stack | +| `` | Renders a supplied string inside a safe fence, preserving its exact value through return and capture. [Forms and validation](specs/executable-mdx-spec.md#619-showing-text-as-a-code-block-codeblock). | built on the #658 stack | +| `…` | Renders content only under a true lexical verbosity value; CLI diagnostics remain host presentation. [Verbosity](specs/executable-mdx-spec.md#620-verbose-only-content-verbose). | built on this stack | +| `` | Renders a captured value through native two-space JSON serialization without changing its operand. [Validation, failures and exact capture](specs/executable-mdx-spec.md#612-json-in-both-directions-json-parse-and-safeparse). | built on the #452 stack, with ordinary text capture enabled by #666 | +| root failure settlement | a text root installs the fail-capable `output` mode for its whole body, so an uncaught, undecided failure is the document execution's own outcome, whether or not the root declares `` [Invariant](#3-error-mode-is-lexical). | built on the #453 stack | +| `` region `output` mode | an undecided error fails the document execution [Invariant](#3-error-mode-is-lexical). | built on main | +| `` rendering selection | chooses which regions of a body render, and buffers a root that declares one; it decides nothing about failure [Interface and evidence](specs/executable-mdx-spec.md#69-component-declared-output-output). | built on main | +| `Expansion` / `getExpansion()` | describes the current logical element expansion [Invariant](#expansion-identity). | built on main | +| `Component.expand` | Observes one complete element expansion without deciding its outcome or delaying execution. [Expansion observation](#1a-one-elements-expansion-can-be-surrounded). | built on this stack | +| `inspectSource()` | Locates executable delimiters using the engine scanner without interpreting source. [Source inspection](#1b-where-a-texts-elements-are-without-running-it). | built on this stack | +| document targets | catalogs a root document's addressable static headings, resolves one selector to one exact target, and projects the document to it before expansion [Invariant](#document-targets). | built on the #412 stack | +| document-aware `xmd run … --help` | describes what one document declares and every target it addresses, each as a full document reference with the description its section states, by inspection alone [Interface and evidence](specs/executable-mdx-spec.md#document-targets). | built on the #463 stack | +| standard-input root documents | Acquires one whole source document from standard input only for explicit `xmd run` with the exact `-` document argument. [Root grammar, cancellation and retained origin](#the-root-document-a-run-is-given). | Built. | +| targeted `xmd run` | reads a file argument as a document reference and executes the one exact target its selector resolved to, replacing the selector before execution rereads the file [Invariant](#document-targets). | built on the #412 stack | +| targeted workflow definition | a workflow definition optionally carries the exact canonical document target, which takes part in definition identity and in compatible reuse but not in the source-bundle hash [Invariant](#document-targets); [Interface and evidence](specs/workflow-spec.md#the-document-target-a-run-is-a-run-of). | built on the #412 stack; `xmd workflow start` takes a document reference and resolves any selector through core against the bytes it is about to retain | +| source-bundle workflow definition | retains exact source bytes under logical paths, with domain-separated source hashes, lengths, bundle hash and optional exact target/component mapping. [Definition identity](#identity-is-separate-from-retrieval) and [source retention](#what-a-run-retains). | Built; one descriptor and live schema version 1, with no migration. Artifact-backed fork remains unbuilt. | +| installed structural syntax | Declares a construct and its regions together with their expansion handler. [Installation, demand and lifetime](#the-installed-structural-syntax-boundary). | built on the #806 stack | +| declared Markdown component | Reserves exact first-party Markdown and its lexical private closure for one execution. [Capture, import and replay](#the-declared-markdown-boundary). | Built; packaged Plan and the review graph use the declaration boundary. | +| workflow component bundle | Retains a closed mapping of component names to the definition source bundle. [Workflow lifecycle](#the-workflow-lifecycle). | built on the #301 stack; the full adversarial implementation loop and its scheduling remain unbuilt (#300), and generated XMD admits no bundled Markdown component (#369) | +| `getWorkflowRun()` | reads the run the current execution is associated with [Invariant](#workflow-runs). | built on the #366 stack | +| `retainedWorkflowInstallation()` | associates one document execution with a run storage already created, requiring exact journal agreement. It resolves no revision and names no Git feature, and a run retains the bytes it executes, so nothing afterwards reaches a repository for its source [Invariant](#workflow-runs); [Interface and evidence](specs/workflow-spec.md#3-where-the-run-is-prepared). | built on the #366 stack | +| `GitQuery.resolve()` | verifies and resolves one Git revision expression contextually [Interface and evidence](specs/workflow-spec.md#7-the-git-capability--moved). | built on main | +| workflow run storage | creates or compatibly finds one run by public run ID, retains its identity, state, document executions and filtered journal, and validates immutable Workspace roots through one provider-owned connection entry [Invariant](#workflow-run-storage); [Interface and evidence](specs/workflow-spec.md#9-retained-run-storage). | built on the #365 stack; the CLI lifecycle reaches it on the #366 stack | +| caller-owned storage transaction | publishes several changes, including journal events, in one transaction nothing else enlists in [Invariant](#one-database-at-a-time-and-a-transaction-a-caller-can-hold). | built on main | +| live durable-operation coordinator | explicitly coordinates structured live execution with existing Yield publication while leaving replay and callback effects unchanged [Invariant](#live-effect-coordination). | built on the #365 stack | +| Workspace coordination API | fails closed by default; replaceable context routes only a one-use provider selection, while the selected provider directly invokes an execution-owned credentialed capability for execution, publication and failure activation [Invariant](#live-effect-coordination). | built on the #365 stack; the Deno provider installs an adapter-private atomic handler | +| shared Git-host reconciliation | Reconciles Git push and pull-request upsert through one request-only boundary whose private terminal alone settles the retained outcome. [Invariant](#external-effects-and-git-host-mutations); [Invariant](#issue-effects). | Built on #297; Push (#370) and PullRequest (#295) consume it. Issue (#296) has its own middleware boundary. | +| explicit WorkflowRun journal route | binds one already-filtered publication to one exact active transaction and otherwise uses ordinary serialized journal storage [Invariant](#local-workspace-topology). | built on the #365 stack | +| `API.Service` / `startService()` | creates an authenticated, supervised loopback service attachment through a provider-neutral operation [Invariant](#replay-safe-live-services); [Interface and evidence](specs/executable-mdx-spec.md#attached-service-api). | built on main | +| foreground command routing and retention | forwards a child's channels live, captures stdout for a `` region, and retains output only when the host asked for a record [Invariant](#foreground-commands). | built on the #441 stack | +| `exec as="name"` | binds one command's settled outcome — exit status, stdout and stderr — as an ordinary mutable binding; the block displays neither channel, renders nothing, and a nonzero status raises nothing. Only the built-in exec terminal and the built-in `timeout` may compose one, authorized by factory identity rather than by registered name, and asked for through a capability-backed request public middleware composes around but cannot issue, consume twice or answer [Invariant](#capability-backed-bound-commands); [Interface and evidence](specs/executable-mdx-spec.md#36-foreground-execution-capture-and-retention). | built on the #447 stack | +| testing harness (``) | runs another document as a real root under a production host profile, authorized by canonical `` alone: declarations installed before the root import, child output displayed progressively and collected only when asked, journal retention selected independently of observation, and the outcome published by the invocation's own terminal through a request public middleware composes around but cannot answer [Invariant](#capability-backed-nested-execution); [Interface and evidence](specs/testing-spec.md#nested-executions). | built on the #454 stack for `host="run"`; the workflow profile and `` are unbuilt, and a host that offers no workflow profile refuses them | +| nested run-profile Agent and elicitation declarations | Configures one isolated run-profile child with frozen TestAgent scenarios and non-delegating Answers, preserving ordinary component shadowing. [Invariant](#capability-backed-nested-execution); [Interface and evidence](specs/testing-spec.md#nested-executions). | Built on #641; controlled Plan label routing added by #728. | +| `Config` run deadline / exec default / Fetch default / verbosity | three independently owned contextual timeouts, absent unless configured, each read by exactly one consumer, and contextual verbosity — a boolean that is false unless configured, seeded by the command line and overridable for a lexical subtree, bounding nothing and deciding no permission [Invariant](#contextual-run-configuration). | built on this stack | +| Agent session configuration (`` / `Agent.options()`) | Applies and verifies exact provider model and effort settings for the final routed session; inspection starts no turn. [Full contract](#native-agent-sessions). | Built; [provider availability and compatibility](#native-agent-sessions). | +| native session launch (`` / `launchAgentSession()`) | Hands one durable session to its provider native UI, retaining construction and build compatibility through interruption. [Full contract](#native-agent-sessions). | Built; [provider availability and compatibility](#native-agent-sessions). | +| `` | performs one XMD-mediated HTTP read through contextual `API.Fetch`, admitting the whole request before transport, and retains the normalized request and the detached response as one `fetch` durable observation; capture decides whether a status is data or a failure, and the trusted host's destination ceiling sits below the component [Interface and evidence](specs/executable-mdx-spec.md#618-reading-over-http-fetch). | built on the #456 stack; a generated fragment may name the pinned identity only for a request the trusted host stated exactly, on the #369 stack | +| `API.Files` | routes every document filesystem operation to the installed provider, with no host default and structural failure data. Its mandatory semantic operations include `ensureDirectory`, which recursively creates or adopts one directory and returns Unit; separately loaded copies compose through the stable Api name [Invariant](#the-document-filesystem-boundary). | built on the #227 stack; directory ensure added by #643 | +| `` | Removes one file or final symlink through Files; absence succeeds and directories refuse. [Filesystem semantics](#the-document-filesystem-boundary), [canonical form dispatch](#capability-backed-invocation-identity) and [generated write admission](#agent-permissions-and-generated-xmd). | built on the #567 stack | +| host Files provider / `useHostFiles()` | resolves document paths in the caller's filesystem, containing them while the host namespace is stable; its directory ensure accepts Dir's existing absolute target or resolves a relative one against the enclosing cwd, recursively creates or adopts it directly, preserves existing contents, refuses a non-directory before content and never rolls back or deletes created directories; installed by all four CLI entrypoints, so Dir remains operational under Node and Bun [Invariant](#the-document-filesystem-boundary). | built on the #227 stack; directory ensure added by #643 | +| transaction-bound Files provider | resolves document paths in the run-owned logical Workspace inside the caller-owned transaction; directory ensure is one durable `workspace_file` mutation whose recursive creation, resulting root and recorded success commit together, whose documented refusal rolls its savepoint back, and whose replay restores the record without creating again [Invariant](#the-document-filesystem-boundary); [Interface and evidence](specs/workflow-spec.md#10-the-document-filesystem-of-a-run). | built on the #366 stack; directory ensure added by #643 | +| `service=` | publishes the attachment's endpoint into the live binding overlay for its invocation [Invariant](#replay-safe-live-services); [Interface and evidence](specs/executable-mdx-spec.md#attached-service-api). | built on main | +| `ephemeral eval` | reconstructs live middleware and bindings without a journal entry [Invariant](#replay-safe-live-services); [Interface and evidence](specs/executable-mdx-spec.md#43-durable-and-live-binding-environments). | built on main | +| `useWorkflowServiceDenial()` | provides a non-delegating workflow service denial provider, installed inside every start and resume execution scope [Invariant](#replay-safe-live-services). | built on the #366 stack | +| `xmd workflow start` / `xmd workflow resume` | starts or resumes a workflow run from the CLI, under the Deno entrypoints only [Invariant](#the-workflow-lifecycle); [Interface and evidence](specs/workflow-workspace-spec.md#3-workflow-lifecycle). | built on the #366 stack; both acquire #367's executor lock before any lifecycle transition | +| implicit workflow Workspace | retains provider-neutral filesystem, repository and attachment state by run ID [Invariant](#workflow-workspace); [Interface and evidence](specs/workflow-workspace-spec.md#5-workspace-structure-and-identity). | document filesystem built on the #366 stack and Repository/Worktree composition on the #293 stack; process capabilities unbuilt (#218) | +| `` / `` composition under a workflow run | names a Git repository and its linked checkouts inside the run-owned Workspace, installs each as contextual working directory, and retains creation identity beside the retained Git bytes [Interface and evidence](specs/workflow-workspace-spec.md#6-repository-and-worktree). | built on the #293 stack, Deno provider only | +| `` | Creates or adopts its target before content, scopes cwd without changing Repository selection, and restores cwd without deleting created directories. [Invariant](#the-document-filesystem-boundary); [Interface and evidence](specs/executable-mdx-spec.md#6132-entering-a-directory-dir). | Directory-creation contract added by #643; host and workflow Files providers. | +| transactional Git effects (`Git.Switch` / `Git.Add` / `Git.Commit`) under a workflow run | publish local Git mutations with their journal result; the enclosing Repository and the contextual working directory select which retained checkout one runs in, and neither observation admits anything — the observed record is compared with the retained row and the directory with the checkouts that row holds, so a failure of admission, of retained state or of an unrecognized native condition fails the run instead of publishing a result [Invariant](#workspace-local-transactions); [Interface and evidence](specs/workflow-workspace-spec.md#7-git-operations). | built on the #294 stack, Deno provider only | +| `Git.Push` under a workflow run | Publishes or adopts the selected named branch through authenticated non-force reconciliation. [Authored contract](specs/workflow-workspace-spec.md#74-push) and [effect mechanics](#replay-and-effect-transactions). | built on the #370 stack, Deno provider only | +| `` under a workflow run | Upserts the exact published branch and returns stable evidence only after this run proves matching Push history. [Props, results and refusals](specs/workflow-workspace-spec.md#75-pull-request); [positive-integer number and reconciliation](#issue-effects). | built on the #295 stack, Deno provider only | +| repository composition vocabulary | Declares twelve ordinary shadowable defaults without installing an operational provider. [Names and profile ownership](#ordinary-repository-vocabulary). | built on the #643 stack | +| `` / `` composition under an ordinary run | Selects an authenticated persistent checkout under an execution-owned exclusive lock [Full contract](#ordinary-repository-ownership). | built on the #643 stack, Deno and compiled only | +| local Git operations (`Git.Switch` / `Git.Add` / `Git.Commit`) under an ordinary run | Performs explicit local transitions against the authenticated checkout without rollback or replay [Full contract](#ordinary-repository-ownership). | built on the #643 stack, Deno and compiled only | +| `Git.Push` under an ordinary run | Publishes or adopts a non-force branch result and keeps invocation-private evidence [Full contract](#ordinary-repository-ownership). | built on the #643 stack, Deno and compiled only | +| `` and its evidence reads under an ordinary run | Reads current evidence or upserts only after this invocation proved the exact matching Push [Full contract](#ordinary-repository-ownership). | built on the #643 stack, Deno and compiled only | +| `` under an ordinary run | Calls the configured Issue provider using this execution and expansion identity [Full contract](#ordinary-repository-ownership). | built on the #643 stack, Deno and compiled only | +| ordinary repository provider assembly | Assembles a fresh provider per ordinary execution, acquiring each resource only when needed [Full contract](#ordinary-repository-ownership). | built on the #643 stack | +| `` under a workflow run | Reads or upserts an Issue through its independent middleware boundary. [Forms and retained results](specs/workflow-workspace-spec.md#103-issue-effects); [tracker normalization, routing and provider responsibilities](#issue-effects). | built on the #296 stack; GitHub middleware, Deno host | +| workflow lifecycle inspection and control | reads status/list/history without advancing a run, recovering a private copy when a crashed source needs rollback; enforces the executor lock, refuses live cancellation, cancels non-live runs under that lock and deletes retained state [Invariant](#read-only-lifecycle-inspection); [Invariant](#cancellation-control). | direct read-only inspection and control built on the #367 stack; coordinated recovered inspection built on the #513 stack, Deno provider only | +| XMD artifact export, inspection and fork source | Seals one committed frontier into immutable version-1 evidence for read-only inspection and future history-fork input. [Artifact identity and compatibility](#portable-xmd-artifacts) and [inspection APIs](#workflow-lifecycle-ownership). | Export, total verification, status and history built for Deno; artifact-source fork remains unbuilt. | +| Agent session portability evidence in an XMD artifact | classifies every logical Agent session that contributed a retained Prompt as portable — with ordered provider checkpoint tokens and an opaque Agent session bundle — or as explicitly unavailable, as two content kinds inside the existing version-1 manifest and identity [Invariant](#portable-xmd-artifacts); [Interface and evidence](specs/xmd-artifact-spec.md#25-agent-session-portability-evidence). | specified by `specs/xmd-artifact-spec.md` §2.5; the closed union, both content kinds and the complete post-identity profile verifier are built on the #621 stack, Deno provider only. Provider bundle capture, Agent-aware export, intrinsic Agent-aware inspection and artifact-backed fork are unbuilt | +| historical authored source | retains an authored durable operation's normalized `SourcePosition` beside its identity, and history parses it or refuses the entry [Invariant](#read-only-lifecycle-inspection); [Interface and evidence](specs/workflow-workspace-spec.md#42-history). | built on the #367 stack | +| history fork | creates a new run from one compatible checkpoint and retained Workspace root, under a new immutable definition and normalized props [Invariant](#retained-effects-and-history-forks); [Interface and evidence](specs/workflow-workspace-spec.md#11-history-forks). | built on the #368 stack, Deno provider only | +| workflow Agent session | Uses inert placement and authenticated first-turn mapping under the run-owned no-native-tool profile. [Session mapping](#agent-permissions-and-generated-xmd). | built on the #302 stack, with the explicit ACP-only selection from #561; the portable proof that an adapter honours an empty tool set is tracked by #496 and does not widen the ceiling | +| generated-XMD admission | Preflights and retains one generated fragment under exact selected identities/forms and request ceilings. [Admission and continuation](#agent-permissions-and-generated-xmd). | built on the #369 stack, continuation basis amended by #589; core owns the mechanics and the workflow policy wrapper is internal | +| `` | Runs untrusted fragment text or paired-produced text under one captured host ceiling; whole-fragment admission, local data expressions and ordinary rendered output remain distinct from provider permission. [Admission and sealed entries](#agent-permissions-and-generated-xmd). | built on the #302 stack, extended by #369 | +| generated mutation proposals | Admits filesystem change proposals through the selected generated write table and ordinary component transactions. [Invariant](#agent-permissions-and-generated-xmd); [Interface and evidence](specs/workflow-workspace-spec.md#mutation-proposal-admission). | Built on #369/#567, with persistent directory creation added by #643; legacy Dir grants refuse, and Git/process/credential/external-write classes remain excluded. | +| Deno-local DOFS provider | owns one authoritative SQLite/DOFS connection per run path, captures arbitrary canonical retained roots, privately restores them, and atomically coordinates one Workspace mutation with its filtered Yield [Invariant](#local-workspace-topology). | built on the #365 stack; public document filesystem effects and the CLI lifecycle route to it on the #366 stack | +| scoped Worker Shell | executes `just-bash` through the Workspace adapter inside a Deno Worker [Interface and evidence](specs/workflow-workspace-spec.md#104-worker-shell). | containment and effect-transaction POCs complete (#351, #357); production integration unbuilt | +| `` | retry a region until it completes [Invariant](#1-middleware-wraps-the-work). | defined, unbuilt | +| `suspendFor()` | publishes one filtered suspension request, settles `suspended`, releases the executor lock, and re-enters the same wait without duplicating it [Invariant](#durable-suspension). | built on the #367 stack | +| `xmd workflow answer ` | retains one schema-validated value for one retained wait, taking no executor lock and changing no run state [Invariant](#typed-answer-delivery); [Interface and evidence](specs/workflow-workspace-spec.md#typed-answer-delivery). | built by #300 | +| `suspension_answer` durable effect | ends a wait from retained delivery state, publishing the answer and consuming that state in one transaction [Invariant](#typed-answer-delivery). | built by #300 | +| `` · `` · `` | read the reviews, comments and checks a Git host already holds for one numbered pull request, completely or not at all [Invariant](#git-host-evidence-reads). | built by #576 | +| workflow scheduling (watchers, unattended iteration, remote host selection) | — [Invariant](#typed-answer-delivery). | #300 | +| `` | binds `{ok: true, value}` or `{ok: false, error}`; a failure becomes a bound value, not a raise [Invariant](#6-an-error-is-never-a-value). | defined, unbuilt | +| error middleware (JS api) | retry · suspend · decline [Invariant](#1-middleware-wraps-the-work). | defined, unbuilt | + +`` composes with validation by nesting: a `` failing inside +fails the attempt — "retry until it parses" is two existing ideas. + ## Changing these rules Spec, tests, and mechanics move together, in the same PR. If a workaround diff --git a/specs/acp-client-spec.md b/specs/acp-client-spec.md index 84275cf1f..f9359c78f 100644 --- a/specs/acp-client-spec.md +++ b/specs/acp-client-spec.md @@ -3,7 +3,14 @@ The `Agent` Api, the `` / `` / `` components, prompt journaling and replay, the provider-factory seam, and the `Config` timeouts are implemented in `@executablemd/core`. A provider is supplied through -the `rootProvider` factory seam described below. +the `rootProvider` factory seam described below. This specification owns the +public operations, configuration and retained record shapes. +[Agent permissions and generated work](../architecture.md#agent-permissions-and-generated-xmd) +own workflow isolation and mapping publication; +[native Agent sessions](../architecture.md#native-agent-sessions) own the shared +session lifetime and enforcement rules. Exact native handoff interfaces and +compatibility exceptions remain in the +[native launch specification](./native-agent-session-launch-spec.md). ## The Agent Api @@ -110,9 +117,11 @@ routes an opaque request and cannot manufacture that result. contextual Api for the same reason the coordinator is not: executable validation decides which retained history may be accepted. - While ownership is held and before any provider construction effect, the - provider reconciles the route. A first `session()` or subscribed `prompt()` - publishes or adopts `acp-first` before runtime creation, `ensureSession()` or - a turn, and an ensure that fails afterwards leaves that route standing. A + provider reconciles the route. A fresh `session()` places the session and + publishes no route. The first subscribed consuming `prompt()` publishes or + adopts `acp-first` before runtime creation, `ensureSession()` or a turn; an + established `session()` reconciles its retained route eagerly. An ensure that + fails afterwards leaves that route standing. A launch by an adapter that names its own sessions publishes `client-native` under an identity that adapter allocated, unless existing durable ACPX state or an existing route says the session was constructed through ACP — in which @@ -536,28 +545,11 @@ omits the member entirely, which is also how every record written before this member existed reads. Parsing an older record is unchanged, and nothing rewrites one. -Observing a request changes no decision, and what observes one is deliberately -somewhere else from what answers it. A policy decides every request inside its -scope and never defers outward, so an observer installed inside a policy — or -inside the turn, below a policy a host installed around the execution — sees -nothing of the decision it exists to record. Auditing is therefore installed with the -Agent itself, outside every policy installed after it, and it owns both sides: -each `Agent.prompt()` call is given its own private ledger, and the cold stream -that call returns is wrapped so every event is read with that ledger in place. A -request raised while a turn's stream is being read inherits that turn's ledger, -so two turns asking at the same time keep their audits apart without anything -correlating by recency, prompt text, agent, conversation or the order decisions -settled in. Nothing a turn does installs its own audit, and a turn reads what it -was allowed to do from the exact stream it consumed. - -The ledger is a destination and grants no authority: holding it says which turn a -decision belongs to, never what the decision is. The observer reserves the -request's place on the way in, delegates exactly once to whatever answers it, and -copies the returned outcome into that same place — which is why two overlapping -requests from one turn keep the order they arrived in however their answers -interleave. A place nobody completed is published as nothing: a policy that -raised, and one cancelled with its turn during teardown, both leave no member -rather than a fabricated outcome. +The audit observes decisions without granting permission. Each turn has its own +ledger, and overlapping turns cannot take one another's entries. A request that +raises or is cancelled before a decision returns contributes no member. The +[Agent permission-audit boundary](../architecture.md#agent-permissions-and-generated-xmd) +owns observer placement, per-stream ownership and arrival-order publication. On a **full replay** (the journal already holds the root `Close`), completed records are restored from the journal without contacting any provider — @@ -699,40 +691,19 @@ omitted MCP and session options, and its public permission routing. A provider session outlives one execution, so a continued run reattaches the conversation it was having rather than replaying its transcript into a new one. -**Identity is the engine's.** Within one workflow run a session is identified by -the Agent/Session expansion identity the engine derived, and by nothing else. The -authored `` is descriptive: two sibling `` -elements are two sessions, and a document that reuses a name cannot make them -one. The name travels the public `Agent.session()` chain, where a handler may -observe or change it; the identity travels inside an opaque placement the -`` element routes and is readable only through the coordinator delivered -to the installed provider. Middleware holding the placement reads the name and -reaches no further. - -Where the element gets that identity is the other half. `` does not -read it from anywhere: the execution mints it for that invocation and answers -for it only through the claimant it delivered to this `` factory. So an -implementation kept from one execution's `` names nothing at another's, -one kept from a different component names nothing here, and an issuance -belonging to another element — finished, still projecting, or live in a frame of -its own — names nothing either. Two attachments running side by side hold two -claimants, and neither answers for the other. - -The provider and the resolved agent command are stored beside that identity as -compatibility attributes. Changing either refuses reattachment rather than -addressing a second mapping. The key ACPX places a session under is a different -thing and namespaces both, because ACPX's store is shared; that key is -arrangement, not this run's session identity. - -**A canonical assertion comes before the mapping.** The order is: placement, -then the backend's acceptance of the session's first turn, then the provider's -canonical, tagged assertion of a durable identity, then the mapping commit — and -only then whatever that first turn produced. Placement is inert: it creates no -provider session and commits no row. Holding a key in ACPX's store is not an -assertion — it says something is there, not what conversation it is — and a -record held for a first turn nobody accepted asserts nothing at all, so a mapping -is committed only from one canonical assertion, and an interruption before that -commit is reconciled only the same way. +**Each Session site owns one conversation.** The engine-derived Agent/Session +expansion identity names it within the run. Authored names remain descriptive: +two sibling `` elements stay distinct even if middleware +changes their names. Provider and resolved agent command are compatibility +attributes; changing either refuses reattachment. ACPX's arrangement key does +not replace this logical identity. + +Placement is inert. The backend must accept the first turn and provide exactly +one canonical tagged assertion before the mapping commits; the first turn's +output is disclosed only after that commit. A stored ACPX key or an unaccepted +first turn proves no identity. The +[workflow Agent ownership contract](../architecture.md#agent-permissions-and-generated-xmd) +owns the opaque invocation claim, mapping transaction and interruption ordering. Continuation is decided before a turn starts: @@ -931,10 +902,12 @@ ceiling that has nothing in it; they configure the approved document later. A provider that cannot establish this ceiling refuses before session materialization or a turn. -The command document itself is given no Files, command, service or XMD-mediated -network capability, and the host decides for that whole execution that a failing -`` ends it — so a turn that streamed text and then failed presents -nothing. +The [Plan writer profile](./plan-command-spec.md#agent-permissions-under-the-plan-writer-profile) +refuses document mutations, commands, services and XMD-mediated network access. +Reads retain the ordinary host provider, middleware and effective `Permissions`; +the frame grants no read permission of its own. A failing `` ends the +whole authorship execution, so a turn that streamed text and then failed +presents nothing. Those refusals are installed on a scope holding the document execution and nothing else, so what the ceiling covers is what it says it covers. @@ -949,26 +922,12 @@ installs the ceiling and states those two acts as its own; they run there, and everything else stays refused. An ended command takes an unfinished install with it, and a failed open stays a warning beside the printed URL. -The profile's working directory is derived from the logical session name rather -than shared or freshly made: `~/.xmd/plan/sessions/`, with the -digest in the path and never the name. A session's key includes the directory it -lives in, so a shared location would put two conversations in one ambient -directory while a fresh one would leave `--session` unable to name a conversation -that already exists. It is created empty and required to be empty before the -provider is constructed or a session is materialized; a non-empty one is a -terminal refusal rather than something to clean. Nothing this profile grants can -write there. - -An explicitly named session keeps its directory afterwards, because that is what -the next invocation derives the same session identity from, and no cleanup -applies to it. An invocation-unique default keeps nothing: its release is -registered before the directory is created, and once the profile has torn down, -on every ending, exactly one cleanup is attempted — the leaf is removed -non-recursively if it is still the empty directory that was handed over, and if -it has gained content or disappeared it is left as found and the command fails -terminally. Which applies is a trusted host value — -whether `--session` was written — and where the directories live is a host -dependency no caller or document can select. +The [Plan session-directory contract](./plan-command-spec.md#agent-permissions-under-the-plan-writer-profile) +owns establishment, named versus invocation-unique lifetime and cleanup. The +provider gets the same digest-derived directory on named continuation. A +non-empty directory refuses before provider construction; an invocation-unique +leaf that gains content or disappears at cleanup is preserved as found and +fails authorship terminally. No caller or document selects its host dependency. Only the profile's provider receives `newSessionOptions.systemPrompt`. It carries one fixed statement — that an answer belongs to the message that asked for it — diff --git a/specs/decisions.md b/specs/decisions.md index 4f1403690..2dbfab89c 100644 --- a/specs/decisions.md +++ b/specs/decisions.md @@ -2,6 +2,20 @@ Decisions made during implementation, including learnings discovered along the way. +## Finding the current rule + +This log records decisions in their historical context. For the current +implementation contract, start at [the architecture reading map](../architecture.md#reading-this-architecture). +The [vision](markdown-agents-vision.md) is non-normative; language interfaces and +conformance belong to [the language specification](executable-mdx-spec.md). + +| Topic | Decision and current owner | +| --- | --- | +| Mixed durable expansion and intentionally non-durable Operations | [DEC-015](#dec-015-generated-continuation--root-basis-membership-and-an-operation-evaluator) partially supersedes [DEC-001](#dec-001-workflowt-vs-operationt--the-type-boundary) and [DEC-002](#dec-002-expansion-generators-are-workflows); [DEC-034](../packages/durable-streams/specs/DECISIONS.md#dec-034-ephemeral--explicit-escape-hatch-for-non-durable-operations-in-workflows) still governs genuinely non-durable work. Current [generated admission](../architecture.md#agent-permissions-and-generated-xmd) preserves both rules. | +| Plan information-request delivery | [DEC-017](#dec-017-plan-information-requests--delivery-history) records sequencing; [Plan architecture](../architecture.md#plan-information-requests) and [the Plan interface](plan-command-spec.md) own current behavior. | +| Main health and verification ownership | [DEC-013](#dec-013-a-red-main-is-reported-as-an-issue-and-gates-pull-requests-no-merge-queue), [DEC-014](#dec-014-composability-proves-ownership-not-correctness-a-second-time), and [contribution verification](../AGENTS.md#verification). | +| Withdrawn terminal-grid surface | [DEC-016](#dec-016-withdraw-the-unimplemented-terminal-grid-surface); [installed structural syntax](../architecture.md#the-installed-structural-syntax-boundary) remains the extension boundary. | + --- ## DEC-001: `Workflow` vs `Operation` — the type boundary @@ -9,6 +23,12 @@ Decisions made during implementation, including learnings discovered along the w **Status:** Decided **Date:** 2026-03-08 +**Current scope:** [DEC-015](#dec-015-generated-continuation--root-basis-membership-and-an-operation-evaluator) +partially supersedes this decision for generated mixed expansion that yields +durable effects. The historical examples below remain as recorded; `ephemeral()` +is still the explicit adapter for genuinely non-durable Operations under +[DEC-034](../packages/durable-streams/specs/DECISIONS.md#dec-034-ephemeral--explicit-escape-hatch-for-non-durable-operations-in-workflows). + ### Context `@effectionx/durable-streams` defines: @@ -81,6 +101,12 @@ accidentally mixing durable and non-durable yields. **Status:** Decided **Date:** 2026-03-08 +**Current scope:** [DEC-015](#dec-015-generated-continuation--root-basis-membership-and-an-operation-evaluator) +partially supersedes this decision for generated mixed expansion that yields +durable effects. The historical examples below remain as recorded; `ephemeral()` +is still the explicit adapter for genuinely non-durable Operations under +[DEC-034](../packages/durable-streams/specs/DECISIONS.md#dec-034-ephemeral--explicit-escape-hatch-for-non-durable-operations-in-workflows). + ### Context `expandSegments` and `expandComponent` need to yield durable effects @@ -701,7 +727,7 @@ whose state has legitimately advanced. Separately, the evaluator crossed from the durable `Workflow` typing to Operations through `ephemeral()` bridges, `ephemeral(expand(...))` among them — -an adapter DEC-034 reserves for intentionally non-durable Operations, wrapped +an adapter [DEC-034](../packages/durable-streams/specs/DECISIONS.md#dec-034-ephemeral--explicit-escape-hatch-for-non-durable-operations-in-workflows) reserves for intentionally non-durable Operations, wrapped here around an expansion that yields ordinary durable effects. ### Decision @@ -714,13 +740,15 @@ the current selected root must be retained, while additional roots and an advanced current root pass. Loss of any admission root refuses before generated work, with the unchanged fixed diagnostic. Every non-root term — class selection, selected pinned identities and forms, request ceilings — still -compares whole, exactly, and in canonical order. This supersedes DEC-001 and -DEC-002 only where a generated mixed expansion can yield durable effects: the +compares whole, exactly, and in canonical order. This supersedes [DEC-001](#dec-001-workflowt-vs-operationt--the-type-boundary) and +[DEC-002](#dec-002-expansion-generators-are-workflows) only where a generated mixed expansion can yield durable effects: the evaluator chain (`persistAdmission`, `evaluateGeneratedXmd`, `evaluateGeneratedFragment`, the host's `admit()`) is typed `Operation`, and no `ephemeral()` bridge encloses it, so the admission and every nested generated effect belong to the owning expansion's own durable sequence. `ephemeral()` -remains the valid, explicit adapter for truly non-durable Operations. +remains the valid, explicit adapter for truly non-durable Operations. The +[current generated-admission contract](../architecture.md#agent-permissions-and-generated-xmd) +preserves that distinction. ### Consequences @@ -790,3 +818,69 @@ was never released. Removing the surface is surgical rather than a commit revert. The unrelated catalog corrections that landed with #729, the generic structural installation work from #806, and every root native-launch change remain. + +## DEC-017: Plan information requests — delivery history + +**Status:** Historical delivery record; current rules remain in the +[architecture](../architecture.md#plan-information-requests) and +[Plan specification](plan-command-spec.md). + +**Date:** 2026-09-07 (the accepted Evaluate forms and ordinary-output changes, +`d56d5ba016150b7750384ba4a2ba81f56a5b1ab9` and +`426df00656a9a38087e2c961454a9b8015161f4a`). + +The following amendment and sequence were recorded during delivery. References +to this stack describe that historical sequence, rather than prerequisites for +current use. The later 2026-10-14 correction +`cdcfe328f1c8ec0cd7a62329a99d17b7edd60d1e` preserves ordinary host-governed reads +throughout the trusted Plan frame while refusing mutations. + +### Delivery status and standing of this amendment + +This section records the Product Owner's #762 amendment against +`1a7b96ab6c75f29fcf82513017f6daabab4d0d82`. PR #776 delivers protected Syntax +routing and PR #786 the ordinary generated output and `as` bindings the requests +compose with. The contracts below replace the earlier Planner artifacts' +Syntax-only child execution, prohibition on file reads, output-only context, and +profile-owned evaluation limits. + +Everything below is delivered on this stack. The shared ordinary read profile — +`xmd run` and every `host="run"` child stating one profile whose read table is +canonical self-closing ``, canonical self-closing `` and exact +canonical protected self-closing ``, with the write table unchanged. +Every built-in structural construct inside generated evaluation, with ordinary +semantics and whole-fragment preflight across every alternative and nested body. +The lexical response classifier, the eight-request loop the packaged `` +owns, and the narrow typed recovery around the public throwing ``. + +### Delivery order and deferred access policy + +After #776 and #786, delivery is three independently reviewed PRs, all of them +on this stack: + +1. **Shared read profile.** Canonical self-closing Glob and canonical protected + Syntax join self-closing File in the ordinary `read` table, through their + captured capabilities and the existing component-answer route, preserving + ordinary value, binding, persistence and replay behavior. The shared write + table is unchanged. +2. **Generated structural constructs.** Every built-in structural construct + becomes available inside generated evaluation with its ordinary semantics, + declarative expressions and fragment-local bindings. Preflight validates + every branch and nested body before any effect, and every effectful component + reached through a construct stays limited by the selected permission. +3. **The Plan loop.** Both command hosts install the shared profile, and Plan + gains the classifier, prompts, narrow typed recovery, counters, records, + progress, disclosure, journal behavior and the source/npm/compiled journeys. + +No temporary Plan-private evaluator bridges them. + +[#787](https://github.com/taras/executable.md/issues/787) owns question-based +selection and request-aware rewriting of relevant Syntax documentation; explicit +`names` lookup here does not wait for it. +[#788](https://github.com/taras/executable.md/issues/788) owns the general +`` component, which is why this design adds no Plan-local deadline. +[#777](https://github.com/taras/executable.md/issues/777) owns the later workspace +selection and user-approval escalation policy. Existing host containment remains +in force with its stable-namespace limitation; #227 owns concurrent +path-replacement hardening. Deferring those decisions does not defer File or +Glob reads. #679 remains separate evaluation work after product delivery. diff --git a/specs/executable-mdx-spec.md b/specs/executable-mdx-spec.md index ec23c5450..5af51ffcf 100644 --- a/specs/executable-mdx-spec.md +++ b/specs/executable-mdx-spec.md @@ -8,6 +8,11 @@ ## 1. Overview +Use this specification for authored syntax, interfaces and conformance. Start +with [a document running](../architecture.md#a-document-running) for ordinary +execution, retained workflows and replay; the [architecture reading map](../architecture.md#reading-this-architecture) +routes implementation questions to their owning rules. + An executable MDX document is a markdown file containing embedded JSX component invocations and annotated code blocks. The system treats each document as an executable workflow: text is emitted immediately, component @@ -2701,10 +2706,12 @@ A component name is resolved in tiers, and the first tier that answers wins: They are reserved: a registration cannot claim one, and a repository file named after one never stands in for it. A structural name written where its construct gives it no meaning is a printed error, not a missing component. -2. **a component canonical core protects** — the engine's own claim rather than - a host's, so the name means the same thing in every execution. `` - (§5.3.1) is the one member. The table is the resolver's own and is consulted - unconditionally, so no option a caller passes puts anything in front of it. +2. **a component canonical core protects** — `Syntax`, `Evaluate`, `File`, + `File.Delete` and `Dir`. The table is the resolver's own and is consulted + unconditionally before every host or author tier. Protection fixes the + implementation and grants no permission; the filesystem components still use + contextual `API.Files` and effective `Permissions`. See + [the canonical protected tier](../architecture.md#the-canonical-protected-tier). 3. **a host claiming the name** — a reserved registration protecting a language or security invariant, or a *declared Markdown component*: exact first-party Markdown a trusted host handed this execution. Both claim the name rather @@ -2902,9 +2909,9 @@ decision, and what may run is still the execution's. the symbol origin kind `protected`, carrying the canonical core origin — never as a reserved registration, which is a *host's* claim under a name and can be absent, replaced or refused where this cannot. A workflow-bundle member is -reported under the kind `workflow`, carrying its canonical repository-relative -path **and** the blob's own object id, so it stays distinguishable from a -repository candidate, which is whatever that path holds now. Both kinds are +reported under the kind `workflow`, carrying its canonical logical path **and** +the retained source's own hash, so it stays distinguishable from a repository +candidate, which is whatever that path holds now. Both kinds are additions, so the symbols are **version 2**: a version-1 reader was promised a closed set of origins, and neither emitting an unknown kind nor reusing a neighbouring one would keep that promise. Nothing else about the shape changed. @@ -2940,12 +2947,13 @@ completes its teardown and commits no record. #### The run profile's repository declarations -Thirteen names — `Repository`, `Worktree`, `Dir`, `Git.Switch`, `Git.Add`, -`Git.Commit`, `Git.Push`, `PullRequest`, `PullRequest.Reviews`, -`PullRequest.Comments`, `PullRequest.Checks`, `IssueTracker` and `Issue` — are -ordinary registered defaults in tier 5. A repository-local Markdown or -TypeScript component of any of those names is chosen ahead of them, exactly as -it is ahead of any other package's default, and for its own scope alone. +Twelve names — `Repository`, `Worktree`, `Git.Switch`, `Git.Add`, `Git.Commit`, +`Git.Push`, `PullRequest`, `PullRequest.Reviews`, `PullRequest.Comments`, +`PullRequest.Checks`, `IssueTracker` and `Issue` — are ordinary +[registered defaults](#registration-and-resolution-order). A repository-local +Markdown or TypeScript component of any of those names is chosen ahead of them, +for its own scope alone. `Dir` is a separate protected core component and is +available without the Git Plugin. They are one array declared by the bundled Git Plugin and read wherever its profile is assembled: an ordinary document execution, `xmd syntax`, `xmd plan`'s @@ -2959,8 +2967,8 @@ command installed (§8.1), and a host that installed none still resolves every one of them. The bundle tier exists only inside a workflow run, and such a run searches no -repository directories at all — so a declared name resolves to the exact source -its pinned commit holds and to nothing beside it in a mutable checkout. Core's +repository directories at all — so a declared name resolves to the exact +retained source and to nothing beside it in a mutable checkout. Core's own components stay available underneath, and a bundle may claim neither structural syntax, a name core supplies, nor a name the host reserved: those collisions are refused before the root document is imported. Ordinary @@ -3358,7 +3366,8 @@ observation and delegation are unchanged. **Which imports are closed is a property of the name, not of the execution.** A component bundle closes every import a workflow run makes, including one that -resolved to a core default underneath it: a run is a run *of* that pinned tree, +resolved to a core default underneath it: a run is a run *of* those retained +sources, and a name resolving outside it is what the bundle exists to prevent. A declared Markdown component closes only the names it declares — the component itself and its private closure. Every other name in that execution resolves and composes @@ -3912,6 +3921,13 @@ filesystem operation the document authored. That is one boundary rather than a list of components: adding a component cannot make validation effectful. +#### Durable component import + +Validation reports structure without executing a document. Import below is the +execution boundary: it records the selected implementation and source for replay. +The [canonical selection rules](../architecture.md#the-canonical-protected-tier) +keep protected names fixed across both phases. + Which implementation a name resolves to is an observation of the environment — which files exist, and what is registered — so it is made **inside** the durable operation, with the read it leads to. What the journal holds is serializable: a @@ -4053,12 +4069,11 @@ One journal entry per component, whatever it resolved to. A repository entry captures both *which file was found* (path) and *what was in it* (content); a registration entry captures the origin that named it, because a function cannot be serialized and the implementation is looked up again on replay. A workflow -entry captures the canonical repository-relative path of the blob inside the -pinned commit — never the `./Name.md` a root document wrote — the blob's own -object ID, and the exact pinned source, so a replay reconstructs the component -from its own record without resolving a name or reading a file. Nothing else is -added: no checkout path, no locator, no duplicate of the commit, no timestamp, -no provider or generated identity. +entry captures the canonical logical path — never the `./Name.md` a root +document wrote — the source's own hash and the exact retained source. Replay +reconstructs the component from its own record without resolving a name or +reading a file. Nothing else is added: no checkout path, locator, timestamp, +provider or generated identity. The trusted host that installed the bundle contributes a journal admission that holds every retained component import to it before public document policy or the @@ -4526,25 +4541,16 @@ ordinary trusted-document `new Function()` path. An unbound identifier fails as an ordinary binding error before the consuming component runs. The rule belongs to generated data composition, not to Json. -**Canonical execution owns projected generated work.** Each Evaluate occurrence -retains its admission and generated effects under identities belonging to that -occurrence. No caller-controlled context, stream, replay cursor, coroutine -identifier, durable owner, public projection or request field participates in -that ownership. Two Evaluate occurrences therefore cannot consume one another's -retained work. - -Each admission and generated effect is appended to the configured stream before -its operation resumes and is visible through the ordinary history contract -immediately. Evaluate neither stages nor rolls back those records and introduces -no second persistence or settlement protocol. A later candidate refusal, -ordinary failure, cancellation or interruption leaves completed effects as -ordinary retained history. A partial continuation restores those effects and -resumes at the first unrecorded effect. - -The generated projection remains structured work of its Evaluate invocation. -The projection and all of its teardown complete before Evaluate returns and -before later parent work begins. This ordering is observable without requiring -a distinct durable cursor or child `Close` record. +**Each Evaluate occurrence owns its retained work.** Its admission and effects +cannot be consumed by another occurrence or reassigned through caller-controlled +state. Completed effects are ordinary history as soon as they commit; a later +refusal, failure or cancellation does not roll them back. Partial continuation +restores them and resumes at the first unrecorded effect. + +Evaluate returns only after its generated projection and all teardown finish. +The [generated-work ownership and admission rules](../architecture.md#agent-permissions-and-generated-xmd) +and [typed recovery boundary](../architecture.md#typed-candidate-recovery-around-evaluate) +own the identity, publication and settlement mechanics. ### 5.4 The root document is a component @@ -5374,7 +5380,7 @@ Neither this operation nor the method on the invocation selects an effect. A component whose forms do different things declares them, and canonical invocation-form dispatch (§5.6) enters the one the scan recorded — `` (§6.13) is dual-form, `` (§6.13.1) is self-closing only, and the -bundled Git Plugin's `` is paired only. A component that reads this +core's protected `` is paired only. A component that reads this contextual operation has no authored-form guarantee at all: what it receives is whatever the chain answered for that call. @@ -9864,6 +9870,10 @@ workflow and returns a `DocumentExecution` handle. Options: #### The host's repository provider +[Ordinary repository ownership](../architecture.md#ordinary-repository-ownership) +owns the distinction between execution-owned resources and persistent checkout +bytes. The contracts below describe the two providers a document can reach. + A document execution reaches repository operations only through the provider the command installed inside the execution scope, before the root document is imported. There are two, and they differ in lifetime and permission rather than @@ -10324,86 +10334,33 @@ cause-free `DocumentProtocolError`. A chain that returns without ever reaching the terminal fails the same way. A refused delegation consumes neither the request nor the expansion, so a handler may recover and delegate a valid one. -A refusal stops what has not happened yet; it does not unwind what has. An -invalid delegation authorizes no root import, no expansion and no authored -effect — and the durable root still records the refusal as its own terminal, -because that is what the run turned out to be. A second delegation adds no -second expansion, import or authored effect, while the work the first valid -delegation already did stands. A preparation refusal prevents every later -preparation, all document policy, the root import and every authored effect, -while durable effects earlier preparations completed stay retained and are not -rolled back; the root records the refusal as its terminal. - -A failure raised before the root import is whatever a handler or a preparation -threw, which is to say: anything. Describing it is therefore total over unknown -and hostile values, and the boundary is a specific one: - -- **Nothing is coerced.** `String(value)` is a call into whatever the value - decides `Symbol.toPrimitive` means. -- **No member is read without tolerating a refusal**, and that includes - classification itself: `instanceof` consults a `Proxy`'s `getPrototypeOf` - trap, and prototype and own-property checks are calls into the value too. A - member that will not be read is simply absent. -- **A trap failure never replaces the failure being handled**, never escapes, - and never stops the bound terminal from being written. Text a trap planted - does not reach the result, the journal, the diagnostic, the stack or the - cause. -- **Diagnostics obtained safely from an ordinary `Error` — its name, its - message, a string cause — are recorded.** That is what makes a replayed - failure describe the one that happened, and what is recorded stays subject to - the journal's existing security boundary like any other recorded value. -- **A value that will not describe itself is recorded under a fixed core-owned - name and message.** - -What crosses the completion boundary as itself is decided by **provenance, not -inspection**. Inspecting an untrusted value cannot establish that publishing it -is safe: an accessor or a `Proxy` trap may answer harmlessly while core is -looking and throw when the caller reads the same member afterwards, so a value -that passed inspection is only a value that behaved once. Publishing it would -move the trap into whoever reads the result. - -So an ordinary value thrown by public `Execution.document` policy or by trusted -durable preparation is **always** converted into a core-owned error built from -the total description above — which keeps whatever the failure gave up safely, -and can be read as often as anyone likes. - -Only a durability or Files infrastructure failure crosses by exact identity, and -those are not converted at all. Everything else the completion publishes is a -value this run constructed. - -A canonical protocol refusal keeps its classification and its original -diagnostic, and it does so **without** publishing the object middleware saw. -Provenance says where an object came from; it does not say what the object still -holds. A handler can catch the very refusal the expansion raised, replace its -members with throwing accessors, and rethrow the same object — still core's -object, no longer core's diagnostic. So the expansion records the *reason* it -refused beside the identity it raised, and the completion publishes a fresh -`DocumentProtocolError` built from that reason. The returned object is never -read. The record is private to the expansion — no brand, stable name, shape, -ambient state or module-scoped registry carries it. - -Reuse is authorized by the **complete** form core writes here, not by the -binding alone. Core reaches this position only by failing, so a recorded success -carrying a copied binding is a record it could not have produced. Admission -parses the whole terminal — one recorded completion, on the root coroutine, with -no root import, an outer settlement core returned rather than threw, an inner -result whose status is `err`, the canonical failure shape, and exactly one -well-formed binding agreeing with the requested root. Every other form is the -fixed cause-free unreadable-root diagnostic, and nothing planted in journal data -escapes through it. - -A terminal core creates *before* the root import carries a binding to the exact -root source and target it was about. Core builds that binding from the root -source this execution was given and parses it on the way back in; nothing a -host, a document or a middleware package supplies can reach it. Root-history -admission validates it before any terminal is reused, so an identical execution -replays the recorded failure — running no preparation, no policy, no import -and no authored work, and appending nothing — while a different root source or -target is refused, and a missing, malformed, duplicated or conflicting binding -fails closed. A partial history is re-entered: a preparation that already -recorded a durable effect finds it retained and restores it rather than -performing it again, and a refusal that follows records a fresh terminal. A history with no recorded completion is unaffected: it continues -live, and preparation runs again through the ordinary durable protocol. +A refusal stops later work and is retained as the root's terminal outcome. +Already completed preparation effects remain history; a second delegation starts +no second expansion. The [document-expansion contract](../architecture.md#capability-backed-document-expansion) +defines refusal ordering and complete-terminal admission. + +A failure raised before the root import may be any value. Canonical core safely +normalizes it without coercing it or letting unreadable members escape into the +completion or journal. A value that cannot describe itself receives a fixed +core-owned name and message. + +Pre-import failures are published as core-owned, repeatedly readable errors. +Safe diagnostics remain subject to the journal's secret policy; hostile accessors +cannot publish their trap text. Only durability and Files infrastructure failures +cross by exact identity. The [provenance rule](../architecture.md#capability-backed-document-expansion) +defines this boundary. + +A canonical protocol refusal keeps its original reason even if middleware +mutates and rethrows its error object. Completion reports a fresh +`DocumentProtocolError`; [core's private refusal provenance](../architecture.md#capability-backed-document-expansion) +never relies on the object's current members. + +An identical root source and target may replay a retained pre-import failure +without preparation, policy, import, authored work or append. A changed root or +an unreadable binding refuses. Partial history restores completed preparation +effects and continues live. Reuse requires the +[complete canonical terminal](../architecture.md#capability-backed-document-expansion), +not a copied binding on an otherwise invalid result. Terminal acceptance stores a **detached** structural copy of the accepted props: @@ -12451,17 +12408,17 @@ Defined in [Workflow runs](./workflow-spec.md). | WR2 | One value | Every read in one execution answers with the same frozen object | | WR3 | Isolation | Concurrent executions each read their own run | | WR4 | Completed journal | The run is restored and Git is never consulted; the replayed output's middleware reads it | -| WR5 | A different base | Refused, naming both bases, before the recorded result reaches the caller | -| WR6 | A moving base | A branch that moved cannot change the recorded pinned commit | -| WR7 | Git fails | No run recorded, and the root document never expands | +| WR5 | Retired: Git base comparison | Historical Git-definition check removed by #880. Current source-bundle identity is specified in [Workflow runs §9.1](./workflow-spec.md#91-what-identifies-a-run) | +| WR6 | Retired: moving Git base | Historical pinned-commit check removed by #880; current workflow definitions retain source bytes rather than resolving branches | +| WR7 | Retired: Git preparation failure | The first-party Git preparation helper was removed by #880. Generic host-preparation failure and replay remain covered by WR18 and WR19 | | WR8 | A later failure | A document that fails after the run was recorded does not erase it | | WR9 | No workflow | An execution without a workflow installation invokes no process at all | | WR20 | One installation, two executions | A host that passes the same installation value to two concurrent executions gets two runs: each journal records its own, each document reads the run its own journal records, a sibling preparing or completing changes neither, repeated reads in one execution answer with the same frozen object, and a later ordinary execution sees no workflow run at all | | WR10 | No workflow, inside | `getWorkflowRun()` throws inside an execution that installed none | | WR11 | A malformed record | Refused, and the refusal never quotes what the journal held | -| WR12 | A slow base | Resolving one run's base does not stall a sibling execution | +| WR12 | Retired: slow Git base | Historical Git base-resolution concurrency check removed by #880 | | WR13/WR14 | Seeded journals | A completed and a truncated journal written by hand restore without any live run having happened | -| WR18 | Recorded base-resolution failure | A programmatic installation raises no evidence objection to the journal §6 describes: Git is not consulted, the root does not expand, and nothing is appended | +| WR18 | Recorded preparation failure | A programmatic installation raises no missing-evidence objection to its recorded preparation failure; the root does not expand and nothing is appended | | WR19 | Replaying a failed preparation | A preparation that failed before the root import left the bound pre-root terminal, and the identical execution reads that failure back: no Git, no preparation, no document policy, no root import, no expansion and no append | | WM1 | A non-delegating handler | A document handler that answers instead of delegating cannot fabricate a successful run: core refuses it, nothing expands, and the journal holds the canonical `workflow_run` record and one failed terminal rather than the handler's value | | WM2 | A substitute result | A result returned after delegating replaces neither the document nor the recorded run | @@ -12728,9 +12685,9 @@ Defined in [Workflow runs](./workflow-spec.md) §3.1. | RR1 | Exact record | The retained run is recorded verbatim; no identifier is allocated and Git is never consulted | | RR2/RR3 | Restoration | A truncated and a completed journal each restore the retained run without recording it again | | RR4 | A different run id | Refused as `StaleInputError` naming the field, never either value, and the root document does not expand | -| RR5 | A different base or pinned commit | Refused on the same terms, naming the fields that differ | +| RR5 | A different bundle or target | Refused on the same terms as RR4, naming `bundleHash` or `targetPath` rather than either value | | RR6 | A malformed record | Refused rather than coerced, without quoting what the journal held | -| RR7 | An unusable installation | A missing run id, base or pinned commit is refused before any document executes | +| RR7 | An unusable installation | A missing or empty required `runId` or `bundleHash` is refused before any document executes | | RR8 | A completed journal recording no run, or more than one | A terminal result whose journal holds no successful `workflow_run` record, holds one that failed, or holds two is refused before the recorded root result is handed back, and without appending anything | | RR9 | A completed journal recording another run | A completed journal whose record names a different run, or holds a value that does not read as a run at all, is refused on the same terms as a truncated one | | RR10 | Suppressed guard policy | A completed run-a journal resumed as run-b is refused beneath a `ReplayGuard` that answers check, admit or decide without delegating — nothing expands, nothing is emitted, nothing is appended | @@ -12739,7 +12696,7 @@ Defined in [Workflow runs](./workflow-spec.md) §3.1. | RR13 | Policy still composes | A public guard observes the admitted history, and one that rejects it still refuses | | RR14 | Non-canonical records | A same-typed Yield under another name, under a child coroutine, or holding a value with an extra member establishes nothing, and none of what it held is quoted back | | RR15 | Failed establishment in a truncated history | A recorded `workflow_run` failure fails closed rather than replaying its planted text for a run storage already describes | -| RR16 | What a refusal retains | Inspecting the whole error object — not only its message — finds no run id, base, pinned commit or planted description member | +| RR16 | What a refusal retains | Inspecting the whole error object — not only its message — finds no immutable run-identity values or planted description members; historical `base` and `pinnedCommit` input is not reintroduced as run identity | | RR17 | Suppressed or reordered `Execution` policy | A handler that hands the durable run a different stream — the package's own descriptor or one built elsewhere, registered before or after the workflow installation — cannot make another run's journal replay | | RR18 | One retained snapshot | A journal whose recorded value shifts between reads is settled once: identity admission, guard observation and replay are handed the same objects, and the second answer is never reached | | RR20 | A hostile recorded value | A value whose `ownKeys`, `getOwnPropertyDescriptor` or getter refuses, and one whose classification refuses, each become the same fixed refusal — nothing reused, executed, emitted or appended, and the whole error object carries none of the planted text | @@ -14007,4 +13964,4 @@ must preserve the trace for diagnosis or remove it before starting a new run. | 100 | `` → `` → `` is the explicit JSON direction | A document names a value, renders it as text, and validates text back into a value at three boundaries a reader can see. `{binding}` interpolation keeps its ordinary string coercion — there is no hidden JSON conversion — and `` takes no `indent`, `pretty`, replacer, sorting, canonicalization or newline option, so nothing about the format has to be agreed on per invocation. A file that must end in a newline authors that newline at the point it is written, rather than buying an option every other caller then has to reason about | | 101 | A capture is delivered from the authored expression | The scanner resolves a `{…}` prop whose text reads as JSON, but it runs before a name resolves and so cannot know the prop is a capture. Keeping the authored text beside the reading lets expansion decide with the selected definition in hand: a captured prop gets what its expression produced, every other prop gets the reading it always had, and an overriding repository file is an ordinary component either way. Without it `value={undefined}` reached a capturing component as `null`, which is the projection a capture exists to avoid | | 102 | A successful `undefined` omits an ordinary prop | Absence is the fact an author has to be able to state: an optional identity that a preceding result will supply is not there yet, and there is no value that means "not there" — `null` is a value, and a second element written for the other case duplicates the invocation. The omission is decided in the one resolver both component kinds pass through, before validation, so the schema answers it: optional stays unset, a `default` applies, a required prop fails as missing. It stops at the root and at that boundary — nested members keep native `JSON.stringify` normalization, failures stay failures, and captures and `` keep binding the exact value — and it puts `undefined` nowhere durable, because a prop that was never there is nothing to record | -| 103 | A declared `` and an authored loop, not hidden `` behavior | A workflow Agent's request/result exchange is written in the document: `` owns the bound, `` and `` own the branch, and `` is where a generated fragment is admitted and performed. `` stays exactly one durable turn, with no hidden retry, schema repair or observation loop inside it, and the Agent Api gains no second operation. A component rather than a new execution primitive, because the operation needs a name a trusted document can write and a schema a reader can check. The host does not register it: it declares it to the execution, which calls the host's factory with the claimant it minted and registers what comes back — so the registration is canonical execution's, and what it provides is availability, never permission. Every ceiling comes from values the host captured before any document existed. Exhaustion is therefore the document's own failure, stated where the bound is, with the turns that produced it retained | +| 103 | Protected `` and an authored loop, not hidden `` behavior | A workflow Agent's request/result exchange is written in the document: `` owns the bound, `` and `` own the branch, and `` is where a generated fragment is admitted and performed. `` stays exactly one durable turn, with no hidden retry, schema repair or observation loop inside it, and the Agent Api gains no second operation. A component rather than a new execution primitive, because the operation needs a name a trusted document can write and a schema a reader can check. Canonical core supplies [protected Evaluate](#532-evaluate-the-protected-evaluation-component); the host states its evaluation profile, captured before any document existed. Protection fixes the implementation and grants no permission; every ceiling comes from the captured profile. Exhaustion is therefore the document's own failure, stated where the bound is, with the turns that produced it retained | diff --git a/specs/native-agent-session-launch-spec.md b/specs/native-agent-session-launch-spec.md index fca0958ad..ac8e2b668 100644 --- a/specs/native-agent-session-launch-spec.md +++ b/specs/native-agent-session-launch-spec.md @@ -88,7 +88,10 @@ selects Claude Code, Codex, or another compatible agent without changing what ## Existing semantics -The feature reuses these contracts unchanged: +[Native Agent sessions](../architecture.md#native-agent-sessions) own the shared +lifetime and enforcement rules. This specification owns the launch interfaces, +phase records, compatibility exceptions and provider proof requirements. The +feature reuses these contracts unchanged: - a static heading target is the role entry point, and sibling targets do not execute; @@ -111,7 +114,7 @@ can launch a prepared native session. ## The launch operation -The ACP Client specification owns the complete five-operation `AgentApi`, the +The ACP Client specification owns the complete six-operation `AgentApi`, the caller-facing `launchAgentSession(instructions, options)` operation, and the shared `LaunchOptions` and `SessionLaunchResult` types. Native launch adds no second Agent or Session hierarchy. Its launch-specific routed request is: @@ -785,10 +788,11 @@ advisory lock, and V1 emulates none: a pid, heartbeat, or stale-file timeout calls a paused process dead and admits two owners. So every advertised session, prompt, launch and incomplete replay refuses there with `unsupported-capability`, before contacting an agent, while read-only resolution -and non-advertised ACP work are unaffected. A host missing only the route store -refuses an advertised agent that names its own sessions, on the same terms and -before any provider effect; an agent whose provider returns the identity is -unaffected, because it constructs nothing a route governs. +and non-advertised ACP work are unaffected. An advertised agent whose adapter +binds a build also requires a route store and +executable observer. Missing either refuses before any provider effect, +whichever side names the session. An adapter that binds no build keeps its +ordinary behavior without those dependencies. ## Construction route @@ -806,7 +810,8 @@ route grants no right to ensure, prompt, detach, spawn or accept history. It says only which kind of thing this session is, so a later operation cannot quietly treat a conversation that already exists as one it may name. -Two schemas are readable. The exact V1 record is: +Three schemas are readable: released V1, bound client-native V2 and bound +ACP-first V3. The exact V1 record is: ```ts type AgentSessionRouteV1 = @@ -916,10 +921,12 @@ construction effect: and otherwise the adapter allocates a candidate and publishes `client-native`; 4. a launch that adopts `acp-first` retains `identity-unavailable` at `prepared`, before allocation, private-file creation, detach or spawn; -5. a `session()` or subscribed prompt that meets `client-native` raises the - provider's typed route error before runtime creation, ensure, turn, close or - accepted history. It retains no launch failure, because no launch was asked - for. +5. a `session()` or subscribed prompt that meets a legacy unbound + `client-native` route raises the provider's typed route error before runtime + creation, ensure, turn, close or accepted history. It retains no launch + failure, because no launch was asked for. A bound route instead follows + [ACP attachment on a bound route](#acp-attachment-on-a-bound-route), including + advertisement, exact build and canonical assertion checks. The resolved agent command is carried from placement through the coordinator key, the route key and provider work. It is never resolved a second time inside diff --git a/specs/plan-command-spec.md b/specs/plan-command-spec.md index a1af74e09..14e614c37 100644 --- a/specs/plan-command-spec.md +++ b/specs/plan-command-spec.md @@ -245,8 +245,8 @@ Every other removed option reports one line naming the spelling that was written: ```console -$ xmd plan "…" --journal trace.jsonl -unrecognized option for xmd plan: --journal — configure the program when you run the approved source with xmd run +$ xmd plan "…" --raw +unrecognized option for xmd plan: --raw — configure the program when you run the approved source with xmd run ``` An option this command does not define is refused by name in the same preflight, @@ -738,7 +738,10 @@ Empty rendered output is a successful empty finding. There is no observation collection and no result envelope; a value the fragment bound but did not render is not sent. -*Recovery is narrow.* Public `` still throws. The workflow recovers +*Recovery is narrow.* Public `` still throws. The +[typed candidate recovery contract](../architecture.md#typed-candidate-recovery-around-evaluate) +owns classification and settlement; `` owns the retry decision. The +workflow recovers exactly one typed class — a generated-request refusal, which is malformed or unauthorized generated source, a declarative expression, binding, construct, form or prop error, invalid admitted Syntax or Glob input, or an ordinary @@ -761,7 +764,9 @@ non-durable pre-disclosure check before `` returns, so a secret reaches neither verbose output nor a following Agent Prompt. Secret or scanner failure is terminal. -*Continuation is ordinary.* A completed request replays without another Agent +*Continuation is ordinary.* [Generated-work ownership](../architecture.md#agent-permissions-and-generated-xmd) +keeps each occurrence's retained work separate. A completed request replays +without another Agent turn, Syntax lookup, File read or Glob traversal; a partial continuation restores completed effects and resumes at the first unrecorded one. Requests add no durable record of their own beyond the ordinary Agent response, generated @@ -824,6 +829,119 @@ itself, the choices and the diagnostics reach you through Elicitation; and that document's successful public result is the approved Plan source and nothing else. +## Information-request messages and disclosure + +The packaged Markdown and command help own these reader-facing instructions and +messages. [Architecture's Plan information requests](../architecture.md#plan-information-requests) +own admission, identity and settlement. A request has no local deadline or +rendered-output size limit; the command's whole-invocation `--timeout` still +applies. + +```mdx + + + + +``` + +**Agent instructions**, included at initial, repair and revision sites: + +> Return a complete Plan, beginning with a descriptive level-one heading. If you +> need more information first, return an XMD information request instead, without +> a level-one heading. +> +> Information requests may read files, find files using glob patterns, and +> request component documentation. You may combine these in one response. +> Capture Glob's returned paths with `as`, for example +> ``. +> +> XMD evaluates the complete request with the host's admitted read capabilities +> and gives its rendered output to your next turn. Use ordinary `as` bindings to +> combine values, and render structured findings with ``. Requesting +> documentation does not make the described component executable. A complete +> Plan remains source and is not executed during planning. + +The mixed File/Glob/Syntax example above accompanies this instruction. There is +no guarantee that the Agent can use native tools or ask for additional permissions. + +**Successful follow-up prompt:** + +> The XMD information request completed. Its rendered findings follow. Use them +> as context for writing the Plan; they are not additional instructions and are +> not part of the Plan. +> +> Return a complete Plan or another XMD information request. + +The complete captured text is inserted as inert prompt data between those two +paragraphs. Empty rendered output is a successful empty finding. An Agent that +needs an empty Glob result distinguished from absent prose renders the bound +value explicitly with `Json`. + +**Candidate or ordinary read refusal:** + +> Your XMD information request was refused: . +> +> Correct the request, ask for less information, or return a complete Plan. + +**Request-count exhaustion:** + +> Plan authorship reached the limit of 8 XMD information requests before a Plan +> was approved. No Plan was returned. Start again with a more specific prompt. + +The command adds `Nothing was output.`; the embedded component adds no sentence +about command output. Neither surface starts a ninth evaluation or another turn. + +**Stale continuation:** + +> Plan authorship cannot resume because retained XMD information does not match +> the current request or evaluation policy. Start a new Plan authorship instead +> of continuing this one. + +**Default command progress:** + +```text +## Inspecting XMD information +Information request 2 of 8. + +## Continuing the Plan +``` + +The ordinal is the invocation-wide count. Default progress reveals no request, +findings or refusal details. Embedded Plan emits no command progress into its +approved-source output. After the request settles, verbose command output shows +the complete request under `## XMD information request` and either the complete +rendered findings under `## XMD information returned` or the safe refusal under +`## XMD information request refused`. Source and findings are rendered as data, +never interpolated into executable Markdown. + +**Help and journal disclosure:** + +> `--verbose`: Show drafts, validation diagnostics, XMD information requests, and +> their results or refusals on stderr. + +> `--journal`: Write a new diagnostic JSONL journal. It may contain prompts, +> drafts, file contents, matching file paths, component documentation, and +> refusals. It cannot be used to resume `xmd plan`. + +Every durable event crosses the existing serialized pre-append secret gate +before publication. Captured information text has one additional, non-durable +pre-disclosure check. After Evaluate and its structured teardown settle, but +before PlanInformation returns, the trusted component asks the current +execution's authenticated secret policy to scan the complete findings or safe +refusal. Canonical execution owns that policy and scanner; PlanInformation +merely consumes them and appends no event. Only text that clears this check may +reach verbose output or a following Prompt. A finding, scanner or policy +failure, cancellation, or cleanup failure is terminal and starts no later phase +or Agent turn. When a trusted host explicitly disabled secret detection, this +check follows that same captured policy rather than inventing an always-on Plan +policy. + +These checks do not make file reading a credentials sandbox: sensitive files +remain subject to the same host read policy. A refusal discloses no partial +findings; already published effect history remains governed by the ordinary +journal contract. + + ## Watching a Plan being written Writing a Plan takes minutes and asks you a question in the middle. So the @@ -1135,15 +1253,15 @@ neither observation never interpreted what it wrote. | PO15 | The adapter and the symbols | The packaged adapter emits no prose of its own, and the symbols are observed exactly once, through public ``, after Preparing; continuation restores that observation without rebuilding it | | PI1 | Selected documentation, then a Plan | A request asking for named `` documentation returns only those details, the next turn produces a normal reviewable Plan, and no full-catalog injection occurs | | PI2 | Composed findings | One response binds Glob, File and Syntax and renders a chosen object through Json; the next turn receives exactly that text. An empty match renders `[]`. No Syntax-specific parser, filesystem shortcut or implicit observation collection takes part | -| PI3 | Lexical classification | ATX and Setext H1-first responses are inert drafts, closed invalid YAML included; an unterminated frontmatter envelope, a body block before the heading and a missing heading are candidates. Approved bytes stay exact and no draft is evaluated | -| PI4 | Both surfaces, whole-fragment refusal | `xmd plan` and embedded `` read through the same host-installed profile, and a prohibited operation anywhere in the fragment — including an untaken branch — refuses before any read | +| PI3 | Lexical classification | ATX and Setext H1-first responses are inert drafts, closed invalid YAML included; an unterminated frontmatter envelope, a body block before the heading and a missing heading are candidates. Approved bytes stay exact and no draft is evaluated. Classification does not depend on full validity, an H1 later in the response, or fence stripping. | +| PI4 | Both surfaces, whole-fragment refusal | `xmd plan` and embedded `` read through the same host-installed profile, and a prohibited operation anywhere in the fragment — including an untaken branch — refuses before any read. The control tries read followed by write, process, network and Agent requests; no prohibited action begins. | | PI5 | Documentation is not permission | Named Syntax describes `` and paired ``, and neither becomes executable; bare Syntax reports Json plus the read vocabulary | -| PI6 | Cleanup precedes the next turn | Success and refusal both complete the projection, every acquired read and the protected route before findings are published and before the following turn begins; a cleanup failure is terminal and wins over a candidate retry | +| PI6 | Cleanup precedes the next turn | Success and refusal both complete the projection, every acquired read and the protected route before findings are published and before the following turn begins; a cleanup failure is terminal and wins over a candidate retry. Hold cleanup deliberately on both endings to distinguish completion from detached work or early publication. | | PI7 | Independent budgets | Requests interleave with initial, repair and revision turns; successes and refusals share one count of eight; the ten-draft and three-repair budgets are unaffected in both directions; the ninth candidate is not evaluated and starts no turn | -| PI8 | Continuation | A completed request replays with live Agent, Syntax, File and Glob tripwires at zero; a partial continuation resumes at the first unrecorded effect; changed source, selected permission, lexical reference, Files scope or capture format refuses before reuse | -| PI9 | Distribution | The embedded request-to-approved-Plan journey executes through source, emitted npm and compiled installations. The command journey executes through the in-process production command assembly and exact packaged command document. Npm and compiled controls verify packaged command and Plan assets, Plan identity and digest, its exact text contract and private closure, and the canonical protected Syntax/Evaluate tier | -| PI10 | Recoverable versus terminal | A malformed request and an ordinary read `Err` are each classified by core as a refused generated request, which `` alone turns into one safe retry context; missing or broken profile or protected route, a throwing Files provider, stale or corrupt history, journal or secret failure, unexpected runtime failure, teardown failure and outer cancellation each stop authorship. Public `` still throws under ordinary use | -| PI11 | Language, not permission | Branching, binding and bounded iteration compose with admitted reads under ordinary rules, and a prohibited component in an untaken branch refuses the whole fragment with zero reads | +| PI8 | Continuation | A completed request replays with live Agent, Syntax, File and Glob tripwires at zero; a partial continuation resumes at the first unrecorded effect; changed source, selected permission, lexical reference, Files scope or capture format refuses before reuse. Historical mixed findings remain exact; neither operation-name-only matching nor refreshing observations satisfies continuation. | +| PI9 | Distribution | The embedded request-to-approved-Plan journey executes through source, emitted npm and compiled installations. The command journey executes through the in-process production command assembly and exact packaged command document. Npm and compiled controls verify packaged command and Plan assets, Plan identity and digest, its exact text contract and private closure, and the canonical protected Syntax/Evaluate tier. Both protected components are present exactly once; asset hashes alone or a reconstructed test-only host do not satisfy the executable journeys. | +| PI10 | Recoverable versus terminal | A malformed request and an ordinary read `Err` are each classified by core as a refused generated request, which `` alone turns into one safe retry context; missing or broken profile or protected route, a throwing Files provider, stale or corrupt history, journal or secret failure, unexpected runtime failure, teardown failure and outer cancellation each stop authorship. Public `` still throws under ordinary use. Message/serialized-name matching, class-wide GeneratedXmdError recovery and a core-owned retry policy do not satisfy the typed boundary. | +| PI11 | Language, not permission | Branching, binding and bounded iteration compose with admitted reads under ordinary rules, and a prohibited component in an untaken branch refuses the whole fragment with zero reads. A construct lacking ordinary fragment context fails under its structural rule, not as an unauthorized component; a structural allowlist does not satisfy ordinary language composition. | | PI20 | The shared route, from both sides | On both surfaces: one middleware the document installed observes the request the fragment made, a second installed nearer the provider transforms it, and the findings the next turn receives carry the transformed answer — so an information request is the ordinary operation and not a private one | | PI21 | Current policy decides it | A read restriction the enclosing scope installed denies the request before the provider performs anything; the refusal becomes exactly one more turn carrying the sanitized sentence, and authorship still finishes. The ordering half — a descendant answering `true` cannot defeat an enclosing `false` — is Tier PM's | | PI22 | Trusted reads, and the refusals that remain | From inside the writer frame and outside any information projection, a read of the caller's own file reaches the installed ordinary chain and returns its exact bytes — a no-throw or a missing-file `Err` would not discriminate it. A temporary directory, a semantic directory ensure, a command and the network are each still refused with the frame's own fixed sentence, and the native-tool denial, empty MCP set and session directory claims are unchanged | diff --git a/specs/upgrade-command-spec.md b/specs/upgrade-command-spec.md index 4faa29155..6086a2b43 100644 --- a/specs/upgrade-command-spec.md +++ b/specs/upgrade-command-spec.md @@ -41,7 +41,7 @@ argument parser cannot report all mean a caller was answered by a command that did something else: - **An option this command does not define.** The parser stops at the first - undefined token and drops the rest, so `--journal`, `--timeout` or `--raw` + undefined token and drops the rest, so `--timeout` or `--raw` would be accepted in silence by a command that configures none of them. - **A value written on a switch.** The parser resolves `--status=false` to the field's default, which is `false` — so the spelling that reads as "off" and @@ -53,7 +53,7 @@ did something else: for the reason every non-`run` command refuses them. Nothing else is offered. This command executes no caller's document, so an -include path, a journal, raw output, an agent, a permission mode, a workflow, a +include path, raw output, an agent, a permission mode, a workflow, a timeout or a secret-detection switch would each describe work that does not happen. `xmd upgrade --help` states the default, both consent options, the entrypoint matrix, what verification does, and what the package-managed diff --git a/specs/workflow-spec.md b/specs/workflow-spec.md index 237b1f8fe..b38de942f 100644 --- a/specs/workflow-spec.md +++ b/specs/workflow-spec.md @@ -14,7 +14,7 @@ A **workflow run** is a workflow being carried out, with its progress and outcome recorded durably. Document executions perform its work; the run itself outlives any one of them. -A run has one definition, chosen once, and it comes in two versions. +A run has one definition: the exact source bytes chosen when it starts. A **source-bundle** definition is the exact bytes themselves, addressed by portable logical paths and retained with the run. A file outside a repository, @@ -22,10 +22,14 @@ an untracked file and a file edited since its last commit are all ordinary starts, and the run executes what each said at the moment it began. Editing, moving or deleting the original afterwards changes nothing about the run. -There is one definition shape and it carries no version. Nothing released names -an older one, so a later format is a clean break rather than a second arm -admitted beside this one — and because a definition holds the bytes it executes, -no host ever supplies a capability for fetching them. +The definition has one closed shape, with no `version` or `kind` member +(§9.1). Older development shapes are refused; no migration or legacy reader +exists. Creation, resume, fork and export read the run's retained source rather +than asking a host to fetch its definition. + +For the execution and replay model, read [A document running](../architecture.md#a-document-running). +This specification owns the run and storage interfaces; [Workflow Workspace](workflow-workspace-spec.md) +owns lifecycle commands and the authored workflow environment. ```ts import { executeInstalled } from "@executablemd/core/host"; @@ -67,8 +71,9 @@ interface WorkflowRun { } ``` -`runId` is opaque, allocated with cryptographic randomness, and supports -equality only. `bundleHash` is the identity of the bytes the run retains, and +`runId` is opaque and supports equality only. The CLI generates cryptographically +random IDs by default; an authorized local caller may [select an ID](workflow-workspace-spec.md#33-callers-may-select-a-run-id). +A programmatic host supplies its ID through its own preparation (§3). `bundleHash` is the identity of the bytes the run retains, and `targetPath` is the exact section it runs, when it runs one. There is no base and no pinned commit: a run starts from exact retained bytes rather than from a repository state, so a member naming one would be a claim about a repository the @@ -99,10 +104,14 @@ workspace or continuation capability. ## 3. Where the run is prepared -`retainedWorkflowInstallation(run)` returns an `ExecutionInstallation` — a -value, not an installation act. **Constructing it creates no workflow run**; -executing a document under it does. A trusted host passes it to `executeInstalled()`, and -the installation contributes exactly two things: +A trusted host supplies one of two preparations. For a run already created +by the lifecycle, `retainedWorkflowInstallation(run)` records that exact run. +A programmatic host instead supplies `WorkflowRunPreparation.allocate()` to +`createWorkflowRunInstallation(preparation)` and decides how to allocate its +run and obtain its inputs. + +Both return an `ExecutionInstallation`; constructing one starts no execution. +The host passes it to `executeInstalled()`, where it contributes two things: - **an admission**, which canonical core captures before any installation, middleware or document code exists and applies inside its own trusted journal @@ -139,7 +148,7 @@ record storage already holds. yield* executeInstalled(options, [retainedWorkflowInstallation(run)]); ``` -`run` is whichever member of the `WorkflowRun` union the record retains. +`run` is the closed `WorkflowRun` value the record retains. This installs the same middleware in the same place and records through the same `workflow_run` durable operation. What differs is both ends of it. The live path @@ -148,13 +157,13 @@ repository is consulted. And every journal state holds the record to that value in full — run id, bundle hash and exact target — rather than to one member of it. -A journal recording a different run is refused as `StaleInputError`, naming the -fields that differ and never their values: a run id may be caller-selected and a -base is any revision expression, so both are external text on the same terms as -retained props. Two versions are never the same run, and a record of the other -version disagrees as its definition version rather than field by field. A value -installed without a complete member set for its version identifies no run and is -refused before any document executes. +A journal recording a different run is refused as `StaleInputError`, naming +the differing fields and never their values. Run IDs and targets can contain +external text, just as retained props can. A serialized journal value missing a +required member or carrying any extra member identifies no run and is refused. +The host input is a separate boundary: the retained installation projects the +named run fields from a possibly wider record, then validates them. Missing, +unreadable or empty required fields are refused before document execution. ### 3.2 Where workflow-run identity is decided @@ -256,42 +265,41 @@ otherwise identical under either installation. ## 4. The three journal states -The journal decides which middleware does the work. +Admission and durable preparation have different jobs in each journal state: | State | What runs | What happens | | --- | --- | --- | -| **live** — no record | the admission, then `prepare` | the admission finds nothing to hold the run to; preparation allocates the run id, resolves the base through the Git Plugin's `GitQuery.resolve()`, records one immutable value, and only then is the root imported | -| **truncated** — record present, root not closed | the admission, then `prepare` | the admission restores the recorded value; preparation re-enters and its durable operation restores what it already recorded, so neither the identifier nor Git is reached again and the journal cursor still advances past its own entry | -| **completed** — root `Close` recorded | the admission only | canonical core returns the recorded result without entering the durable body, so preparation never runs and the admission is the only place the run is restored — or a disagreeing one refused | +| **live** — no record | the admission, then `prepare` | admission finds no earlier identity; preparation records the supplied retained run, or calls the programmatic host's `allocate()`, before the root is imported | +| **truncated** — record present, root not closed | the admission, then `prepare` | admission restores the recorded run; the preparation's durable operation consumes its recorded result and advances its cursor without allocating again | +| **completed** — root `Close` recorded | the admission only | canonical core validates the run and returns the recorded result without entering preparation or expanding the document | -The admission runs before the recorded root result is returned. That ordering is -what lets a completed journal refuse a supplied base that disagrees with the -recorded one, rather than handing back a result the caller did not ask for. - -Replay invokes neither run-id allocation nor `GitQuery.resolve()`. The current value -of a moving branch is never consulted. +Admission precedes reuse of a completed root, so a history naming a different +run is refused instead of returning that run's result. Partial replay restores +preparation; completed-root replay does not enter it. Neither asks Git to +resolve a definition. ## 5. Refusals The journal is parsed, never trusted. -- A record that does not describe a workflow run is refused. The stored value is - described, never quoted: it is external data, and reporting it would carry - whatever it held into logs and rendered output. A value matching neither - version's exact member set describes none. -- A recorded base that differs from the supplied base is refused, naming both. +- A record that does not describe the closed `WorkflowRun` shape is refused. + Stored values and unexpected member names are never quoted in diagnostics. +- A retained run whose `runId`, `bundleHash` or exact target differs from the + supplied run is refused, naming the differing fields rather than their values. -Both are `StaleInputError`: the journal no longer describes this run, and the -document is re-run from the start rather than resumed. +Both are `StaleInputError`: the journal does not describe the requested run and +cannot be resumed as that run. Refusal stops this continuation; starting over +uses a new journal. ## 6. When a run exists A workflow run exists once its `WorkflowRun` value is durably recorded. A document failure or cancellation after that point does not erase it. -Failure *before* that point creates no run and expands no root document. For a -programmatic Git run that is Git failing to be invoked, a working directory that -is not a Git repository, or a base that does not resolve to a commit. +Failure *before* that point records no `WorkflowRun` value and expands no +root document. A programmatic host's `allocate()` may fail while preparing its +inputs; that generic preparation failure is still a durable outcome. A retained +lifecycle run already exists in storage before this installation executes. Such a failure is journaled the way every durable effect's failure is — as a recorded failed effect, and no `WorkflowRun` value exists, which is what @@ -300,23 +308,18 @@ recorded failed effect, and no `WorkflowRun` value exists, which is what Preparation fails before the root import, so the durable root records the **bound pre-root terminal** canonical core writes at that position: a terminal carrying core's own binding to the exact root source and target it was about. -Resuming that journal reports the recorded failure — it does not retry Git, does -not re-enter preparation, does not run document policy, imports no root, expands -nothing, and appends nothing. The workflow installation raises no objection to +Resuming that journal reports the recorded failure: it does not retry +allocation or re-enter preparation, run document policy, import the root, expand +content or append anything. The workflow installation raises no objection to it (§3.2). ## 7. The Git capability — moved -This section described the read-only Git query capability while it was this -package's. It is `@executablemd/git`'s now and is specified in -[the Workspace spec](./workflow-workspace-spec.md). What remains here is the -shape a reader of a journal may still meet: - -```ts -interface GitQueryApi { - resolve(revision: string): Operation; -} -``` +Git queries belong to `@executablemd/git`, not workflow definition or run +identity. Repository and Git operations are specified in [Workflow Workspace +§§6–7](workflow-workspace-spec.md#6-repository-and-worktree); the current query +interface is [GitQueryApi](../packages/git/src/git.ts). Retained definitions +are read from their own source store (§9.4). `GitQuery.resolve(revision)` has the semantics of @@ -331,8 +334,7 @@ parsed by a shell. The default provider invokes the Git CLI through the contextual process and working-directory Apis. A non-zero exit fails, reporting what Git said; a clean -exit naming nothing fails too, because an empty object id would pin a run to no -repository state at all. +exit naming nothing fails too: an empty object ID identifies no commit. Another provider replaces it lexically with `GitQuery.around({ *resolve(…) {…} }, { at: "min" })`. Providers install at @@ -790,15 +792,12 @@ authorized lease and transaction handle. The provider invalidates that record before commit or rollback, so a retained handle or token never becomes valid again in a later transaction. -Creating that physical connection takes recovery coordination, and holds it -until the connection closes. The opening performs one first read that touches a -page, which is what makes SQLite put back a rollback journal a lost host left -behind; the hold continues past it because any later read through the same -connection can be the one that meets a journal a process crashing afterwards -left. It is taken once per physical opening — never per transaction, lease or -effect — and a cached entry answers without taking anything, because the -connection it answers with already holds it. Closing and reopening a path is a -new physical opening and takes it again. +Each physical connection holds recovery coordination from opening through +closing. Cached leases reuse that hold; reopening takes a new one. This keeps +SQLite recovery and read-only inspection from reading different versions of a +database-and-journal pair. [Read-only lifecycle inspection](../architecture.md#read-only-lifecycle-inspection) +owns the recovery ordering and connection lifetime; §9.7 owns the storage +refusals. Opening existing storage performs structural recognition, retained-root and content validation, the live/current comparison and the singleton run-row read diff --git a/specs/workflow-workspace-spec.md b/specs/workflow-workspace-spec.md index 985bce81e..36db1259a 100644 --- a/specs/workflow-workspace-spec.md +++ b/specs/workflow-workspace-spec.md @@ -6,8 +6,11 @@ This specification defines the observable contract of `xmd workflow`: a provider-neutral retained environment in which readable Markdown procedures run as repeatable workflows. It complements `architecture.md`, which owns the -implementation invariants. The executable MDX specification continues to own -parsing, expansion, bindings, error modes and ordinary component behavior. +implementation invariants. Start with [A document running](../architecture.md#a-document-running) +for the ordinary/workflow and replay model. This document owns lifecycle +commands, workflow profiles and observable behavior; [Workflow runs](workflow-spec.md) +owns retained identity and storage, and the [Executable MDX specification](executable-mdx-spec.md) +owns authored syntax, bindings and ordinary component behavior. ## 1. First workflow @@ -115,7 +118,8 @@ environment. - Partial replay reattaches the same Workspace and continues at the frontier. - Unsupported operations fail instead of falling back to unrelated host filesystem or process behavior. -- Agents inspect the Workspace read-only and cannot mutate it directly. +- Agents receive observations through authored Prompts and cannot access the + Workspace directly. XMD performs its reads and mutations (§8). The same declarative components work in both environments. Durability comes from the workflow host, not from another spelling of `` or ``. @@ -711,16 +715,14 @@ foreign, incompatible, damaged or unparseable candidate fails the request and is reported distinctly. The command does not return the healthy subset as though it were complete, and it changes no candidate. -A run whose host was lost mid-transaction is reported like any other. Its -database carries a rollback journal that only a write-capable connection can put -back, so `status`, `list` and `history` copy the database and that journal into -a private scratch directory, let SQLite recover the copy, and read the answer -from it. The retained pair is unchanged, still awaiting its next write-capable -owner, and the copy is removed before the answer is observable. `list` processes -at most one such candidate at a time. Recovery grants nothing else: these -commands still obtain no executor lock and no lifecycle ownership, and a copy -that cannot be produced or removed is reported as its own refusal rather than as -damage to the run. +A crash does not prevent read-only inspection of the last committed state. +When SQLite requires rollback recovery, inspection reads a privately recovered +copy and leaves the retained database and journal unchanged. It processes one +candidate at a time and returns success only after removing its scratch copy. +Failure to produce or remove that copy is an inspection-recovery refusal, +separate from damage revealed in the recovered run. Recovery obtains neither an +executor lock nor lifecycle ownership. [Read-only lifecycle inspection](../architecture.md#read-only-lifecycle-inspection) +owns coordination, recognition and teardown ordering. ### 4.2 History @@ -1870,12 +1872,21 @@ classes this fragment draws on. Before the first effect, the evaluator: - resolves each admitted name **and authored form** to the exact pinned definition and identity the host supplied, never to a component search path, a registration, or the workflow component bundle; -- refuses executable code blocks, expression props, interpolation that reads a - binding, a result binding, every component the host did not admit, and every - form the selected classes hold no exact entry for; and +- permits ordinary structural constructs, fragment-local `as`/`Let` bindings + and declarative data-expression props, while refusing executable code blocks, + arbitrary JavaScript, prose interpolation that reads a binding, unadmitted + components and forms outside the selected tables; and - refuses a request that is malformed, or that does not normalize to one the host stated exactly. +Data expressions compose JSON literals, finite numbers, local binding +identifiers, arrays, objects and object shorthand; they do not execute +JavaScript. The [generated-XMD language contract](executable-mdx-spec.md#533-ordinary-generated-composition) +owns numeric spellings, prohibited expressions and structural composition. +A binding is available after its producer; child bindings do not leak to +following siblings. No caller binding enters the fragment and no fragment +binding escapes it. + A refusal ends the whole fragment. Its diagnostic names the construct class, and so does what the run retains of it, so a rejected fragment publishes nothing — including a credential a rejected element carried. A malformed request is @@ -1914,34 +1925,20 @@ retained admission. #### A continuation is held to the ceilings it was admitted under -A retained admission is a grant, and a continuation is held to it. Durable -replay matches an effect by its type and name, and what a description carries -is stored rather than compared, so the normalized policy is retained in the -admission's own result. Before one generated component is invoked or one -request is performed, a continuation checks the retained policy against the one -the run now states — and holds two kinds of term differently, on purpose. - -The admission's Workspace roots and selected root are an as-of-admission -retained basis, not a frozen future state: every committed mutation retains -another immutable root and advances the authoritative current root, and that is -the run's own recorded progress rather than a moved ceiling. So the basis is -checked by membership. Every admission root and the admission's selected root -must still be retained, and the root the run now stands on must be a retained -one; additional retained roots and an advanced retained current root pass. Loss -of any admission root — the selected one included — refuses before generated -work, with the same fixed diagnostic that names none of what it compared. - -Every non-root term compares whole and exactly, in canonical order. A changed -class selection, a changed pinned identity behind an unchanged name, a changed -admitted form, and a widened or otherwise altered request ceiling are each -refused. Only the tables the selection reached take part, so a run that changed -a write table a read-only admission never drew on has changed nothing that -admission was granted under. - -The fragment is decided inside that durable effect rather than before it. So a -continuation restores what was admitted without parsing the current candidate at -all: a later caller holding different source changes nothing about what expands, -and what expands is the source this run admitted. +A continuation uses the exact source and selected policy retained by the +admission, not a newly supplied candidate. It checks them before invoking a +generated component or performing a request. + +Root checks preserve the run's own progress: every admission root, including +its selected root, must remain retained, and the current root must be retained. +Additional roots and an advanced current root pass. Losing an admission root +refuses without revealing compared values. + +Non-root terms must match exactly in canonical order: selected classes, pinned +identities and forms, and normalized request ceilings. Only selected tables +participate, so changing an unused write table does not invalidate a read-only +admission. [Generated admission and continuation](../architecture.md#agent-permissions-and-generated-xmd) +owns the retained-basis comparison and durable policy identity. `File` is admitted as two identities, never as one. Core's pinned **read** identity is the self-closing form and core's pinned **write** identity is the @@ -1959,32 +1956,20 @@ carry the same definition and disjoint forms; overlapping forms or two definitions are a malformed host table, refused before anything is retained rather than settled by whichever entry the host listed first. -**The admitted form holds at the invocation too.** Which identity was admitted -is preflight's, decided from the scan: a name outside the selected tables, and a -form the selected tables hold no exact entry for, refuse the whole fragment -before its first effect. - -Which branch the admitted component then takes inside itself is held to the same -decision, and the component does not make it. A component declares the forms it -answers and canonical engine dispatch enters the body for the form the scan -recorded, before the body runs (executable-mdx-spec §5.6); every admitted -invocation is checked against that same fact beforehand too, refusing when it is -not the form its identity was chosen for. Neither decision consults -`Component.hasContent()`, whose outermost handler answers first and may answer -differently on each call, nor a `hasContent()` method on whatever invocation -object a caller handed over — a caller can mint that object, and it cannot mint -the engine's issuance. So an admitted `File:read` does not become a write and an -admitted `File:write` does not become a read, however many times something is -asked, and an admitted self-closing `File.Delete` is the only spelling of that -name that reaches a provider at all. A refusal there lands before any provider is -reached, and the fragment performs nothing. +**The admitted form also holds at invocation.** Preflight selects the exact +name and form before the first effect; canonical dispatch then holds the body +to that selection before any provider is reached. An admitted `File:read` +cannot become a write, an admitted `File:write` cannot become a read, and only +self-closing `File.Delete` is admitted. The [generated-XMD boundary](../architecture.md#agent-permissions-and-generated-xmd) +owns the engine-authenticated form check; replaceable `Component.hasContent()` +answers and caller-created invocation objects cannot decide it. #### The authored loop, and where it is written -The host adds no control flow. `` is the workflow host's -component an authored document writes where an observation should happen — the -host declares it and canonical execution registers it (below) — and everything -around it is ordinary Markdown: +The document owns control flow. It writes canonical core's protected +`` where a generated fragment should run; the workflow host supplies +the evaluation ceiling, not the component. Everything around it is ordinary +trusted Markdown: ```md @@ -2010,46 +1995,41 @@ not the loop's failure — the document states what exhaustion means, which the representative flow does with a final `` that requires a proposal. The completed Prompt and observation records are the evidence for exhaustion. -``'s schema is closed on one required string prop and one optional -`allow` array, and paired content is refused: a `source` the element rendered is -not a fragment anybody handed it. A write-enabled invocation is written the same -way, with the class it draws on stated where a reader can see it: +Evaluate accepts disjoint inputs: a self-closing element supplies the +fragment in the string `text` prop; a paired element renders its content to +produce the fragment and refuses a simultaneous `text` prop. `allow` is an +optional array selecting the host's effect classes. The closed schema accepts only `text` and `allow`; +every profile refuses `source` and `program`. A write-enabled invocation states its class where a reader can see +it: ```md ``` -It answers with a detached value, not text: +Evaluate returns the fragment's rendered text. To return structured +findings, the fragment captures a component result and renders the structure +explicitly: -```json -{ - "observations": [{ "name": "Fetch", "value": {} }], - "output": "" -} +```mdx + + ``` -Each admitted read's own returned value, in the order the fragment invoked them, -with whatever the fragment rendered under `output` — beside them, never instead -of them. An admitted `` written without a binding renders nothing at all, -so a result taken from the rendered fragment would answer the Agent's question -with an empty string. - -An admitted mutation contributes neither an entry nor a synthetic receipt: its -own durable effect record is the authoritative account of what it did, and a -second copy on this value would be a second thing to keep true. A write-only -fragment of a paired `` under a `` therefore answers with -`{ "observations": [], "output": "" }`. - -`allow` and `as` are independent. `as` grants nothing — it is the language's -ordinary caller-owned binding, valid for a read, a write and a mixed selection -alike, and it binds the same shape for each. The caller's binding environment -does not cross into the fragment, and no fragment-local binding is exported from -it. +Ordinary `as` suppresses a component's output and binds its result locally; +`Let` may bind a value or capture rendered content. Pure `` remains +available under read, write and mixed selections without granting an effect. +Prose interpolation of bindings remains forbidden inside the generated +fragment; the surrounding trusted loop keeps its ordinary expressions. + +Evaluate written with `as` binds the rendered string and emits none; without +`as`, it emits that string. It declares no `returns`. No implicit observation +array, result envelope or mutation receipt exists. A write-only fragment may +render only whitespace unless it authors other output; the mutation's own +durable record remains its evidence. -It declares no `returns`, so the value binds by reference under `as` and nothing -about it is rewritten on the way to the document. Turning it into text is the -document's decision, made where the text is wanted: the representative flow binds -it and renders it into the next `` with ``. +The loop above renders the captured string through `Json` into the next +Prompt. No caller binding crosses into the fragment, and no local binding is +exported from it. `allow` narrows effects independently of ordinary capture. **How it is supplied.** `` is canonical core's own protected component, and this host supplies none of it. Core claims the name ahead of @@ -2079,50 +2059,29 @@ identity a continuation is compared against — the ceiling an admitted `` site, or under a registry answer carrying the first attachment's -whole registration record, admits nothing there. An implementation closed over -one attachment's storage and ceilings names nothing under another's, so no -run's database receives a record another run's expansion named. +**Each occurrence owns its durable operation.** Canonical execution +authenticates the Evaluate invocation; a Context, contextual Api answer, +import wrapper or reused implementation cannot substitute another element's +identity or another run's storage. [Generated execution ownership](../architecture.md#agent-permissions-and-generated-xmd) +owns the claimant and cross-attachment checks that keep two sites from replaying +one another's admission. `` is deliberately not a printing boundary: a refused or failed observation stops the authored loop unless the document places a recovery boundary around it, because returning a refusal as observation text would leave the Agent reasoning from a read that never happened. -Being reachable from a trusted document is all the registration decides. It -carries none of the permission. The component closes over the exact run storage -and the immutable host options the declaration supplied, takes its durable -identity from its own invocation through the claimant, reads the retained roots -and the run's authoritative current root from that storage at invocation — an -as-of-admission provenance a continuation holds by membership, so the run's own -later publications and an advanced retained current root invalidate nothing — -and takes the read-only `File` identity from core. No prop, binding, Context, -contextual API answer, component registration, generated name or middleware -return value supplies or widens any of them. - -A repository component named `Evaluate` does not shadow this one, and neither -does a registration, a bundle member, a declared Markdown component or a second -loaded copy: the name is canonical core's, and claiming it is refused where the -claim is made. `Component.importComponent` middleware composes around the import -exactly as it composes around any other — it may observe it, delegate it and -refuse it by throwing — and what it cannot do is answer one. Agent-generated -source resolves through none of that: the evaluator consults only its own closed -table of pinned identities, so a fragment naming `` is refused like any -other unadmitted component. +The host captures one immutable evaluation profile before installation; +Evaluate reads retained roots and the authoritative current root from the owning +run at invocation. No prop, binding, Context, contextual API answer, +registration, generated name or middleware result supplies or widens the +ceiling. Protection fixes the component, while contextual Files and Permissions +still govern its operations. + +The [canonical protected tier](../architecture.md#the-canonical-protected-tier) +owns import enforcement: middleware may observe, delegate or refuse the import, +but cannot replace Evaluate. Generated source resolves only through its closed +admission tables; naming `Evaluate` there is an unadmitted-component refusal. #### Mutation-proposal admission @@ -2142,7 +2101,7 @@ written even though canonical core now owns the component (`specs/executable-mdx-spec.md` §6.13.2). That order is retained identity. A continuation compares the entries position by -position (§8.4, *A resumed run is held to its ceilings*), so a run admitted under +position ([§8.4, continuation ceilings](#a-continuation-is-held-to-the-ceilings-it-was-admitted-under)), so a run admitted under a table with fewer entries is refused before any generated effect rather than resumed under the wider one. A read-only admission never selected the write table and is unaffected by a change to it. @@ -2205,12 +2164,12 @@ events and outcomes for replay and training. It does not claim to reconstruct provider-internal summaries, hidden context or tool state. **Identity is the engine's.** Within one run a logical Agent session is -identified by the Agent/Session expansion identity the engine derived, and by -nothing else. The authored `` is descriptive: two sibling -`` elements are two sessions. The name travels the -compositional `Agent.session()` chain, where a handler may change it; the -identity travels inside an opaque placement the element routes, readable only -through the coordinator delivered to the installed provider. +identified by the engine-derived Agent/Session expansion identity. The +authored `` is descriptive: two sibling +`` elements are two sessions, even if a handler gives +them the same display name. [Agent session ownership](../architecture.md#agent-permissions-and-generated-xmd) +owns authenticated placement and provider routing. The public `Agent.session()` +chain may change the name; it cannot choose the durable identity. The provider and the resolved agent command are stored beside that identity as compatibility attributes. Changing either refuses reattachment rather than @@ -2227,30 +2186,19 @@ owns a session, how it was constructed and which executable build accepted its identity; a workflow session is named by the row below instead, and an agent served here is served over ACP alone. Composing the two is a separate design. -**The mapping lives in the run.** It is a row in the run's own database, -committed in the run's own transaction — a mapping that could commit while the -run did not would describe a session the run never had. The table is part of the -schema the run is created with, so it appears with every other table or not at -all. What stays beside the run on disk is the provider's own session store and -one empty working directory per session; both are disposable, and neither is -retained state. - -**A canonical assertion comes before the mapping.** The order is placement, then -the backend's acceptance of the session's first turn, then the provider's -canonical, tagged assertion of a durable identity, then the mapping commit — and -only then whatever that first turn produced. - -Placement is inert. A `` says where a session will live and which agent -it is compatible with; it creates no provider session, contacts no backend and -commits no row. The session comes into existence when the first subscribed -`` starts a turn the backend accepts, and acceptance is reported by the -adapter rather than inferred from anything the turn then produced. - -Occupancy of a key in the provider's own store is not an assertion — it says -something is there, not what conversation it is — and a record the provider -holds for a first turn nobody accepted asserts nothing at all. Nothing is -retained for it, and the next attempt on that placement creates rather than -resumes. +**The mapping lives in the run.** Its database row commits in the run's +transaction and is part of the initial schema. Provider session stores and empty +working directories beside the run are disposable attachment state, not retained +workflow identity. + +**First-turn publication follows the mapping commit.** Placement is inert: +`` contacts no backend, creates no provider session and writes no row. +After the first subscribed Prompt's turn is accepted by the backend, the +provider must make one canonical tagged assertion of its durable identity. The +run commits that mapping before exposing any first-turn output. Store-key +occupancy or an unaccepted turn asserts no identity; the next attempt creates +rather than resumes. [Agent session ownership](../architecture.md#agent-permissions-and-generated-xmd) +owns the acceptance, assertion and publication ordering. Partial replay attaches lazily. Completed Prompts restore without contacting the provider, and full replay never attaches one. Before a later live Prompt, the @@ -2434,9 +2382,9 @@ creation identity does not. Missing, damaged or conflicting retained state is a stale-input condition: children and later siblings do not begin, `` cannot print it, and nothing is recloned or repaired. -Before any of that, a run closed over a component bundle obtains it, by the -version it retains — under the executor lock, before a document-execution record -is begun, before a Workspace is attached and before anything is appended. +Before replay, a run authenticates its retained component bundle under the +executor lock, before beginning a document-execution record, attaching a +Workspace or appending anything. A run authenticates it from its own retained source BLOBs. Every retained length and content hash is recomputed from the bytes the run holds, and so is the bundle @@ -2452,9 +2400,9 @@ recorded import naming a component the bundle does not declare, or holding a different path, hash or source, appends nothing and invokes nothing. A completed root result returns without expanding the document or attaching -Workspace, Agent or external providers. It still obtains the bundle the same way -its version requires, and still applies that admission, so retained output is -accepted only for a history this run is a run of. +Workspace, Agent or external providers. It still authenticates the retained +bundle and applies admission, so retained output is accepted only for this run's +history. A partial replay that reaches a completed Git-host effect may still reconstruct what that effect needed locally — a Push rebuilds its checkout from the Workspace @@ -3553,10 +3501,10 @@ fetch operation requires its own language and durability contract. | XMD artifact export, inspection and fork source | specified in `specs/xmd-artifact-spec.md`; `xmd workflow export` and artifact `status`/`history` are built, Deno provider only. The artifact-backed fork remains unbuilt | | Agent session portability evidence in an artifact | specified in `specs/xmd-artifact-spec.md` §2.5; the format and its complete verifier are built. Provider bundle capture, Agent-aware export, intrinsic Agent-aware inspection and artifact-backed fork are unbuilt | | workflow Agent isolation | built by #302: no directory attachment, an empty host-owned working directory, no MCP servers, an empty requested tool set and deny-all with a failing permission path; the portable no-tool proof is tracked by #496 | -| workflow Agent session retention | built by #302: a row in the run's own database, keyed by the engine-derived Session expansion identity alone — the authored name is descriptive — with provider, agent command and policy fingerprint beside it as compatibility attributes. A `` places one and creates nothing; the first subscribed Prompt constructs it, and the mapping commits after the backend accepted that turn and the provider made its canonical tagged assertion, before anything the turn produced is exposed. Occupancy of a provider key is never identity — including a record held for a first turn nobody accepted — and missing, mismatched, replaced or ambiguous assertions each refuse instead of starting a replacement session | -| generated-XMD admission | built by #369, through `@executablemd/core/host`; the workflow policy wrapper is internal. Host policy is a read table and a write table of exact pinned identities, each entry carrying the authored forms it is admitted for, and an authored `allow` selects a canonical subset of the closed classes `read` and `write` — omitted means `read`. The complete fragment is preflighted inside one `generated_xmd` effect before its first generated effect | -| `` and the authored loop | built by #302 and #369 and made canonical by #713: core's own protected component, with a closed schema of `text`, this host's released `source` alias and an optional `allow`. The attachment supplies no component — it supplies one captured evaluation profile whose entries name capabilities rather than definitions, so an admitted element reaches the run's transaction-bound operations directly. Ceilings come from the run's own storage, core's `` read, write and delete identities at revision 2 and the paired `@executablemd/workflow/composition#Dir` at revision 3; iteration, branching, approval and exhaustion are ordinary Markdown | -| generated-XMD mutation-proposal admission | built by #369 and #567, amended by #643: the standard Deno profile's write table is core's paired `File:write`, the paired versioned `@executablemd/workflow/composition/dir-v2#Dir` and core's self-closing `File.Delete`, in that retained order and followed by any host extension. `allow={["write"]}` intentionally authorizes the current Dir's persistent recursive creation; the former unversioned identity never authorizes it and a retained table naming that identity refuses before generated execution. Admitted mutations run as the ordinary components they are through the run's effect transactions, directory ensure and deletion each publishing the same `workspace_file` effect their authored forms do; the evaluator adds no receipt or result entry, so a write-only fragment still binds `{ observations: [], output: "" }`; and approval is authored control flow before the element. Local Git, Git-host, issue, process, execution, credential and external-write effects are outside the class | +| workflow Agent session retention | built by #302; [§8.5](#85-agent-session-continuity) owns logical session identity, compatibility, first-turn publication and reattachment/refusal behavior | +| generated-XMD admission | built by #369 through `@executablemd/core/host`; the workflow policy wrapper is internal. [§8.4](#84-generated-xmd) owns the workflow tables and selected classes; [architecture](../architecture.md#agent-permissions-and-generated-xmd) owns admission and continuation mechanics | +| `` and the authored loop | built by #302 and #369, canonical since #713; [§8.4](#the-authored-loop-and-where-it-is-written) owns text/paired inputs, the closed `text`/`allow` schema, rendered-string output and authored iteration/approval. The host supplies a captured ceiling; core supplies the protected component | +| generated-XMD mutation-proposal admission | built by #369 and #567, amended by #643; [§8.4](#mutation-proposal-admission) owns the ordered write profile, retained Dir compatibility, ordinary effect transactions and authored approval. Evaluate returns rendered text, with no result envelope or mutation receipt | | Deno-local DOFS persistence | POC proven by #349 / PR #350 | | scoped Deno Worker Shell | containment proven by #351 / PR #353 and transactions by #357 / PR #362; production integration unbuilt | | Worker JavaScript | deferred | diff --git a/specs/xmd-artifact-spec.md b/specs/xmd-artifact-spec.md index 42e580052..8e0996235 100644 --- a/specs/xmd-artifact-spec.md +++ b/specs/xmd-artifact-spec.md @@ -7,8 +7,10 @@ This specification defines the portable evidence form of an Executable Markdown workflow run. An XMD artifact is one immutable `.xmd` file that can leave CI, arrive on a developer machine and still describe the exact committed run that CI observed. It complements `specs/workflow-workspace-spec.md`, which owns live -workflow execution and history forks, and `architecture.md`, which owns the -structural invariants. +workflow execution and history forks, and [Portable XMD artifacts](../architecture.md#portable-xmd-artifacts), +which owns the structural invariants. This document owns export/inspection +behavior, the semantic format and compatibility. Start with [A document running](../architecture.md#a-document-running) +for the execution and replay model. The practical boundary is simple: @@ -243,7 +245,7 @@ frontier and attempt a compatible history fork: - in a finalized artifact, one portability classification for every logical Agent session that contributed a retained Prompt, and the opaque bundle bytes of each portable one (§2.5); and -- the workflow definition source, in the form the run's own version retains. +- the exact retained workflow definition source closure (§2.4). “Complete” is bounded by XMD ownership. The artifact does not claim to capture the state of the world around the run. @@ -416,7 +418,7 @@ The value is: ```ts interface XmdArtifactManifest { - /** The semantic format this artifact declares: 1 or 2. */ + /** The semantic format this artifact declares: 1. */ readonly version: number; readonly entries: readonly XmdArtifactManifestEntryV1[]; } @@ -453,7 +455,9 @@ groups, and admits nothing else: - the artifact frontier: the source run id, the final committed event id when the journal holds one, and the current Workspace root id; -- the workflow run record, without definition retrieval metadata; +- the workflow run record as `workflow-run`, carrying the storage definition + and the `WorkflowRun`, without definition retrieval metadata, a `base` or a + pinned commit; - every document-execution record, in its retained order; - the prior workflow-fork lineage, when the source run was itself a fork; - every journal row in append order: its event id, the exact filtered protocol @@ -481,15 +485,13 @@ groups, and admits nothing else: portability kind exists; and - the workflow definition source closure, one pair per retained source: - - `workflow-run` carries the storage definition and the `WorkflowRun`. It - admits no `base` and no pinned commit; - `definition-source-entry` is `canonical-json` under the canonical JSON string - of the source's logical path, containing exactly the members `path`, - `sourceHash` and `byteLength`. Canonical JSON writes those keys in its - existing lexicographic order; a reader requires the exact member set and does - not treat object-member order as identity; and -- `definition-source-content` is `bytes` under the same identity, containing - that source's exact BLOB bytes. + of the source's logical path, containing exactly the members `path`, + `sourceHash` and `byteLength`. Canonical JSON writes those keys in its + existing lexicographic order; a reader requires the exact member set and does + not treat object-member order as identity; and + - `definition-source-content` is `bytes` under the same identity, containing + that source's exact BLOB bytes. Semantic verification requires exactly one entry and one content value per descriptor path and no undeclared pair, checks both identities against the path, @@ -533,10 +535,11 @@ marker is the four bytes `XMDA`, the integer `0x584d4441`; the live workflow-run marker is `XMD1`, `0x584d4431`, and recognizing that one is the categorical live-run refusal rather than the foreign-container refusal. -The artifact format version is `1` or `2`. A container declaring any other is an -unsupported version this build must not touch; a container declaring one of -these two over the other's records is a file that disagrees with its own header, -and its undeclared kinds make it corrupt. +This build reads and writes semantic artifact format `1`, manifest version +`1` and container schema version `1`. Other declared semantic or container +versions are unsupported and left untouched. Undeclared content kinds under +format 1 are corruption. The two Agent evidence profiles (§2.5) belong to this +same semantic format; they are not separate versions. Raw tables, views, indexes, triggers, PRAGMAs and SQL queries are not public API. The supported readers are XMD lifecycle commands and libraries that implement