Add <Repository>, <Worktree>, <Issue>, and <PullRequest> to xmd run - #689
Add <Repository>, <Worktree>, <Issue>, and <PullRequest> to xmd run#689taras wants to merge 5 commits into
<Repository>, <Worktree>, <Issue>, and <PullRequest> to xmd run#689Conversation
Make the complete repository-composition vocabulary available to an ordinary `xmd run`, with a live Deno provider and the current Git repository as the ambient Repository, while the retained workflow implementation keeps its own provider of the same component contract. The thirteen components — Repository, Worktree, Dir, the four Git operations, PullRequest and its three evidence reads, IssueTracker and Issue — become one shadowable declaration array that `xmd syntax`, `xmd plan`, a workflow attachment and an ordinary document execution all consume, so one language is described and resolved everywhere. What a name does is the installed provider's. Components now observe a profile-neutral `RepositorySelection`: an opaque provider-minted identifier, a display name, the credential-free repository identity and the selected checkout path, and no authority at all. Every operation authenticates the selection it was handed against private provider state, so a replaced contextual Repository can misname a checkout and be refused but can never reach one. The ordinary provider discovers the ambient repository once before root expansion, keeps managed checkouts under `~/.xmd/repositories` behind version 1 sidecars and execution-owned non-blocking advisory locks, performs local Git directly with no transaction and no replay, and authorizes `<PullRequest>` from private Push evidence that crosses no execution. Node and Bun register the same declarations and install no operational provider.
) Executes the rest of the frozen ORC1–ORC21 matrix and corrects who an ordinary commit is by. An ordinary `<Git.Commit>` now records the invoking user's own effective Git author and committer identity, captured once from the trusted host's environment and configuration before a document expands and read back off the object it wrote. A workflow run keeps its one fixed identity — its retained state must not depend on whose machine made it — and an ordinary run inverts that, because the commit lands in that person's checkout. Nothing else is borrowed: hooks, file-system monitors, signing programs and repository-supplied credential helpers stay disabled. A host that can name no identity refuses that one component and names the commands that fix it rather than substituting the workflow name; every other component stays usable. Three defects the new evidence found, all fixed: - a managed-checkout lease was released when the task that took it completed rather than when the provider's scope ended, so a second process could take a slot an interactive Session was still working in; - the managed root canonicalized to one path before it existed and another afterwards, so the execution that created it and every execution after it computed different slots — and therefore never contended; and - the shared CLI module statically imported the Deno adapter, whose graph reaches `node:sqlite`, which stopped `xmd` loading under Bun at all. A working directory reached through a symbolic link now matches the checkout it is inside, and `scripts/smoke-run-composition.ts` runs two compiled binaries at once to prove placement, persistence, cross-process refusal and release.
Evidence for ORC5, ORC8, ORC15 and ORC17, written to discriminate rather than to confirm. - ORC15: a diagnostic trace of an execution that really published grants nothing. A `<PullRequest>` handed one is refused, so what authorizes a pull request is evidence an execution holds rather than a record describing one. - ORC5: a run with no origin does local work and refuses to publish before reaching anything; a run with an origin opens a session and transports; a run with a Git host configured reaches it. Three counters that can each fail on their own, so no single stub makes the set pass. - ORC8: a managed Repository survives an authored failure inside its body, and a Push result handed back by middleware that never performed one grants nothing. - ORC17: all three pull-request read collections are read, each from its own route.
An `<Execution host="run">` child is a root execution in a scope that does not descend from the document's, so it inherited no `API.Env` handler and stood in the *process* directory. A `<Dir>` or `<TempDir>` around the element scoped every component inside it except the child, and the child's own repository provider discovered its ambient Git from whatever checkout the process happened to be launched in. The harness now reads the contextual directory on the last line still inside the invocation and carries it on `ChildInvocation`, the private value the terminal hands the trusted provider. Deliberately not on `HostProfileRequest`: middleware that could see it could swap it, and the directory a child resolves its root and its repository in would become composed policy's choice rather than the document's. The run profile installs it first, ahead of the root, the provider and the execution, so all three agree with the document that asked. ORC19 now asks this of a real `xmd run` in a subprocess whose process directory is a temporary directory that is not a Git checkout, with every repository, managed root and document fixture-owned and `HOME` redirected. A break in propagation therefore refuses for want of a repository rather than operating on the checkout the suite runs from. The fourth case proves that refusal directly, which is what stops the first from passing for the wrong reason. Two claims are asked of siblings rather than of a parent and its child, and the tests say why: a managed checkout stays leased for the whole run of whoever touched it, so a parent and a child cannot share one; and a run discovers its ambient repository once, when its provider is installed, from the process directory — which here is deliberately not a repository. The parent's side of the isolation is the lease case, where a parent's hold survives its child's teardown.
The previous commit claimed parent/child Push-evidence isolation could not be asked in one process. That was wrong, and the claim is removed. Only reselecting the same *managed* slot conflicts with the parent's lease; caller-owned ambient Git carries no lease at all, so two executions can use one checkout in sequence. That makes ambient Git the correct fixture for this criterion, not a barrier to it. A separate subprocess now stands in a fixture-owned temporary Git checkout and asks the boundary both ways, over the same checkout, origin, branch and head: - the parent performs a real Push, and a `host="run"` child immediately asks for a `<PullRequest>` from that same head, and is refused; and - a child performs the Push, tears down, and the parent asks from the head its own child just published, and is refused. Both refusals are for missing successful Push evidence and both land before any Git host is reached. The implementation states that itself — "Nothing was observed at the Git host, and no pull request was created" — so the test asserts that sentence rather than inferring the ordering. A second, independent reading corroborates it: the origin here is a local path, so an execution that had got past the evidence gate would have failed with "no usable origin" instead, and that sentence is asserted absent. Standing in a repository keeps the fixture escape-safe on its own terms: a regression in contextual-directory propagation can reach only the disposable clone this fixture made. The non-Git-cwd case is kept unchanged and carries the other half of the argument — that the propagation is real rather than incidentally agreeing with where the process stands. The contextual-cwd, sibling-evidence and parent-lease cases are kept as accepted.
PR #689: ✨ Give an ordinary xmd run the repository-composition vocabulary (#643)91 files, +8928 / -768 Scope🔴 PR has 9696 lines changed. Split into focused PRs. 🟡 9696 lines changed. PRs under 400 receive more thorough review. 🟡 91 files changed. Are all changes related? 🟡 Changes span 11 directories. 🟡 PR mixes config and source changes. 🟡 New abstraction files: packages/workflow/src/deno/run-composition/provider.ts. Verify 3+ consumers. Structural🟡 Interface declarations with no consumers: Committed, Switched.
🟡 1 console statements. Oxlint structural signals:
SlopOxlint slop signals:
Static AnalysisOxlint: 67 diagnostics across 25 files (14 rules) no-shadow (24): packages/workflow/src/composition/components/Repository.ts, packages/workflow/src/composition/components/PullRequest.ts, packages/workflow/src/composition/components/GitSwitch.ts (+10) CorrectnessNo extraneous code patterns detected. |
<Repository>, <Worktree>, <Issue>, and <PullRequest> to xmd run
|
Superseded by a reviewable three-PR stack. Closing unmerged; nothing here is abandoned.
Merge strictly bottom-up: #694 → #695 → #696. The stack's final tree differs from this PR's head ( The partitions preserve their inventories exactly: the 1,928-line workflow tier suite became three capability files with 15 describe blocks and 51 cases before and after, and an identical sorted set of every describe and ORC1–ORC21 pass across the stack, each block observed individually rather than inferred from an aggregate. This head and its backup refs are left intact. |
Closes #643.
Why
An ordinary
xmd runcould not use the repository components. A document could launch an Agent, but it could not create a worktree for that Agent, operate on Git, read or update an issue, or publish a pull request without becoming a retained workflow.This prevented documents such as the roles in
AGENTS.mdfrom expressing the repository work they need to perform.What changes
Before:
<Repository>,<Worktree>,<Dir>, theGit.*components,<Issue>, and thePullRequest.*components resolved only inside retained workflows. An ordinary run could not use them.After:
An ordinary
xmd rununder Deno or the compiled binary can use all of those components. The Git checkout containing the contextual working directory becomes the ambient<Repository>, so a root-level<Worktree>creates a worktree from the repository where the document is running.Node and Bun expose the same components through
xmd syntax, keeping the document language consistent across runtimes. Operational use there fails before local or remote state changes because those runtimes do not yet install a repository provider.Retained workflows keep their existing provider, identity, durability, and behavior.
How it works
A component selects a repository through
RepositorySelection, which identifies a target but grants no authority. The installed provider authenticates every selection against private state before accessing a checkout or remote service.Managed Repository checkouts and Worktrees live under
~/.xmd/repositoriesand remain after the run ends so work is not lost. A non-blocking advisory lock gives one execution exclusive use of a managed checkout. Another process selecting the same checkout is refused immediately, and process termination releases the lock without deleting the checkout.A pull request requires evidence that the same execution successfully pushed the exact repository, checkout, branch, destination, and commit. That evidence cannot be supplied by a document or reused by another execution.
Review guide
Start with:
packages/workflow/src/composition/selection.tsThen review:
packages/workflow/src/composition/api.ts— provider contracts and refusing defaultspackages/workflow/src/deno/run-composition/provider.ts— ambient repository discovery and ordinary-run operationspackages/workflow/src/deno/run-composition/leases.ts— exclusive checkout ownershippackages/cli/src/run-repositories.tsandpackages/cli/src/deno-repositories.ts— runtime registration and Deno provider installationLook carefully at:
node:sqlite, which Bun cannot load.<Execution host="run">must use the contextual working directory of the document that launched it, not the process directory.What must stay true
How to verify it
deno task test packages/cli/tests/run-composition-deno.test.ts deno task build deno run --allow-all --frozen scripts/smoke-run-composition.tsscripts/smoke-run-composition.tsuses two compiled binaries to prove that ambient discovery, managed-checkout persistence, cross-process exclusion, and lock release survivedeno compile.xmd runoutside a Git process directory to prove that nested runs receive the document working directory and that Push evidence remains execution-local.Scope
Included
Repository,Worktree,Dir,Git.Switch,Git.Add,Git.Commit,Git.Push,IssueTracker,Issue,PullRequest,PullRequest.Reviews,PullRequest.Comments, andPullRequest.Checksunder ordinary runs.Intentionally unchanged
New abstractions
RepositorySelectioncarries a profile-neutral repository target without carrying authority.New dependencies
@executablemd/runtime(workspace:*), added to@executablemd/testingAPI.Env, which owns the contextual directory, belongs to@executablemd/runtimeRisks and limitations
Scope confirmation