Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ Existing documents and code get aligned to this section retroactively.
| definition base | the Git revision supplied to choose a workflow definition's pinned commit |
| 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 authority — 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 document execution was started inside, discovered once before root expansion from the invocation's starting directory. 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 document execution 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, captured once from the trusted host's own environment and configuration before a document expands and kept in the provider's closure. It is used for an ordinary `<Git.Commit>` 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 `<Git.Commit>` 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 |
Expand Down Expand Up @@ -2384,9 +2388,10 @@ 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,
is everything a selection is *not*: the canonical Git identity each selection
resolves to, the retained rows behind it, and the credentials and locators used
to reach a service. A selection that the provider did not mint, or one whose
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.

Expand Down
11 changes: 11 additions & 0 deletions packages/workflow/deno.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,14 @@ export type {
SuspensionControllerOptions,
SuspensionNotice,
} from "./src/deno/suspension.ts";
/**
* The ordinary run's repository provider.
*
* The installer alone, and the options a trusted entrypoint supplies to it.
* What the provider holds — the leases, the credential assembly, the selection
* registry, the live Push evidence and the metadata writer — stays inside it:
* a package that could reach one of those could authorize a publication this
* execution never made.
*/
export { useRunComposition } from "./src/deno/run-composition/provider.ts";
export type { RunCompositionOptions } from "./src/deno/run-composition/provider.ts";
168 changes: 168 additions & 0 deletions packages/workflow/src/deno/run-composition/ambient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
/**
* The repository the person running the document is standing in.
*
* A workflow document names every repository it touches, because a workflow is
* a program that runs somewhere else. An ordinary `xmd run` is a command
* somebody typed in a checkout, so the checkout is the obvious subject — and
* making it the default is what lets a document say
*
* ```md
* <Worktree name="issue-643" branch="issue-643">…</Worktree>
* ```
*
* and mean the repository the command was run in.
*
* ## Two identities, and they are not the same
*
* The **common Git directory** identifies the repository; the **checkout root**
* identifies which of its checkouts this invocation is in. They differ exactly
* when the caller is standing in a linked worktree — where `.git` is a file
* naming the primary repository's administration — and keeping them apart is
* what makes starting XMD in a worktree produce the same Repository identity as
* starting it in the primary checkout, while Git operations still act on the
* worktree the command was actually run in.
*
* ## Discovery is not an operation the document asked for
*
* It happens once, before root expansion, from the invocation's starting
* directory. Being outside a repository is not a startup failure: a document
* that never asks for a Repository-dependent operation runs exactly as it would
* anywhere else, and only an element that needs one refuses.
*
* The `origin` is read the same way, and its absence is likewise not a failure.
* A repository with no origin is a perfectly good Repository for a Worktree, a
* Switch, an Add and a Commit; it is only Push and PullRequest that need a
* destination, and each of those checks for one before it opens a credential.
*/

import { realpath } from "node:fs/promises";
import { basename } from "node:path";
import { until, type Operation } from "effection";
import type { GitObjectFormat } from "../../composition/records.ts";
import { admitLocator, locatorFingerprint } from "../composition/locator.ts";
import { currentBranch, readObjectFormat, resolveCommit } from "../composition/git.ts";
import type { GitSession } from "../composition/git.ts";

/** What one Git checkout on this host turned out to be. */
export interface AmbientRepository {
/** The display name a document sees: the checkout directory's own name. */
readonly name: string;
/** The canonical root of the checkout the invocation started in. */
readonly checkoutRoot: string;
/** The canonical common Git directory, which identifies the repository. */
readonly commonDirectory: string;
readonly objectFormat: GitObjectFormat;
/** The commit HEAD named when this invocation started. */
readonly head: string;
/** The locally recorded, admitted `origin`, or `undefined` when there is none. */
readonly origin: string | undefined;
readonly originFingerprint: string | undefined;
/**
* The branch a `<PullRequest>` defaults its base to.
*
* `refs/remotes/origin/HEAD` when the checkout records one, and the branch
* this invocation started on otherwise. Nothing is asked of a remote for it:
* a default branch this run had to fetch would make an ordinary document
* reach the network before it did anything.
*/
readonly defaultBranch: string;
}

/**
* The canonical directory this path resolves to, or `undefined`.
*
* Canonicalization matters more than usual here. `/var` on macOS is
* `/private/var`, and Git writes the resolved path into a linked worktree's
* administration — so a comparison against an unresolved path would report a
* worktree as belonging to no repository.
*/
function* canonical(path: string): Operation<string | undefined> {
try {
return yield* until(realpath(path));
} catch {
return undefined;
}
}

/**
* Discover the ambient repository from this directory, or answer `undefined`.
*
* Every step is a local Git question. Nothing here contacts a remote, opens a
* credential or writes anything.
*/
export function* discoverAmbientRepository(
git: GitSession,
from: string,
): Operation<AmbientRepository | undefined> {
const reportedRoot = yield* git.read(["rev-parse", "--show-toplevel"], from);
if (reportedRoot === undefined) {
return undefined;
}
const checkoutRoot = yield* canonical(reportedRoot);
if (checkoutRoot === undefined) {
return undefined;
}

const reportedCommon = yield* git.read(["rev-parse", "--git-common-dir"], checkoutRoot);
if (reportedCommon === undefined) {
return undefined;
}
// Without `--path-format=absolute`, which not every supported Git has: a
// linked worktree already answers absolutely, and a primary checkout answers
// `.git` relative to itself.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// `.git` relative to itself.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// `.git` relative to itself.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant comment — restates what the code does.

Suggested change
// `.git` relative to itself.

const commonDirectory = yield* canonical(
reportedCommon.startsWith("/") ? reportedCommon : `${checkoutRoot}/${reportedCommon}`,
);
if (commonDirectory === undefined) {
return undefined;
}

const objectFormat = yield* readObjectFormat(git, checkoutRoot);
const head = yield* resolveCommit(git, checkoutRoot, "HEAD");
if (objectFormat === undefined || head === undefined) {
// A directory Git recognizes but cannot say the shape of is not a
// repository this provider will act on. Refusing here is the same answer as
// being outside one, and for the same reason: nothing has been read that
// could name a checkout.
return undefined;
}

const branch = yield* currentBranch(git, checkoutRoot);
const recorded = yield* git.read(["config", "--get", "remote.origin.url"], checkoutRoot);
// Admitted on the way in, not on the way out: what is not a locator this
// provider would hand to Git is a repository with no usable origin, which is
// a state Push and PullRequest already know how to refuse.
const origin = recorded === undefined ? undefined : admitLocator(recorded);

const recordedDefault = yield* git.read(
["symbolic-ref", "--quiet", "--short", "refs/remotes/origin/HEAD"],
checkoutRoot,
);
const defaultBranch = defaultFrom(recordedDefault, branch);

return Object.freeze({
name: basename(checkoutRoot),
checkoutRoot,
commonDirectory,
objectFormat,
head,
origin,
originFingerprint: origin === undefined ? undefined : locatorFingerprint(origin),
defaultBranch,
});
}

/**
* The default branch, from what the checkout records.
*
* `refs/remotes/origin/HEAD` reads as `origin/main`, and what a base names is
* `main`. A detached HEAD with no recorded remote default leaves nothing to
* name, and the empty string is what a `<PullRequest>` then has to be given a
* `base` for.
*/
function defaultFrom(recorded: string | undefined, branch: string | undefined): string {
if (recorded !== undefined && recorded.startsWith("origin/")) {
return recorded.slice("origin/".length);
}
return recorded ?? branch ?? "";
}
Loading
Loading