Skip to content

Add repository operations and recorded-result replay to xmd repl - #899

Merged
taras merged 31 commits into
mainfrom
agent/retained-repl-repositories
Oct 11, 2026
Merged

taras merged 31 commits into
mainfrom
agent/retained-repl-repositories

Conversation

@taras

@taras taras commented Oct 11, 2026 •

Copy link
Copy Markdown
Owner

Why

xmd repl needs the repository vocabulary used by the supervised adversarial implementation workflow (#181). An entry can select a Repository/Worktree, write authored or generated files, stage and commit, publish, open a pull request, read its evidence and file an issue, then leave at a question and continue from the printed location.

What changes

Before: REPL entries have no entry-owned repository provider for that journey.

After: Deno and compiled REPL entries receive their own provider and recorded results. Completed operations replay without repeating effects. New operations run against the current environment. If a recorded write produced A and the user later writes B, replay preserves B; subsequent work sees B.

How it works

Deno/compiled REPL → entry task + private canonical mutation delivery
  → ordinary live repository/File operation → acknowledged durable result
  → saved result on continuation; next unrecorded operation runs live

The private fileMutations host seam delivers the canonical authored/generated occurrence. Git/Dir components claim their own occurrence. Minimal result records belong to one execution/entry and canonical site. External requests freeze their resolved branch/commit/destination before contacting the service; existing Git-host and Issue records remain the only outcome authority. A pull request requires this entry's exact successful Push receipt.

The provider and leases belong to the entry task. Cold/frozen readings build no host; continuing the live head builds one once. Generated relative writes use the directory of their lexical producer. Node/Bun retain explicit repository-provider absence.

Review guide

Start with the REPL execution model and retained entry host in architecture.md, then packages/git/src/deno/retained-repository/{provider,local,files,external}.ts, the private File seam in core, and the CLI entry factory/session assembly.

Planning PASS: 402ab3c78be27aa8d28d029f7302933b6470f0c4, carried through the mechanical cleanup at 6ee9e2fccc08ad05e9310e45a769ff0605b2a71b. The delivery merge preserves current main's History/lifecycle behavior and the accepted entry-host lifecycle.

What must stay true

  • Saved local results replay without inventorying, restoring or validating the checkout. No scratch prediction or local pre-state intent.
  • Journal integrity, entry/site ownership and configured generated permissions remain enforced. Unreadable saved File/Delete/Dir outcomes end continuation even through PrintErrors.
  • Result acknowledgement precedes later document work; an append failure is fatal.
  • External reconciliation retains its existing absence/adoption/conflict/ambiguity rules, owner identity and lazy credentials.
  • Ordinary run and workflow contracts retain their existing behavior.

How to verify it

  • Protocol/native/File/generated/external/entry suites passed the amended frozen matrix. The final R5 correction's affected command passed 205 steps; portable records passed under Node/Bun. Mechanical cleanup reran 85 steps and the original three outcome probes.
  • Both source and compiled public journeys leave and reopen at 13 boundaries plus the final Issue question, asserting actual files, indexes, refs and forge counters.
  • Delivery merge tests: repl-repositories, repl-history, repl-lifecycle, repl-source-reading, repl-terminal — 21 passed / 152 steps.
  • deno task check, deno task lint, deno task check:jsr passed after the delivery merge.
  • Bounded cost check: two Dir, five File and one Add operations retain 9 records / 11,383 bytes, unchanged by about 640 KiB of unrelated ignored content. No checkout inventory, recovery scan or scratch clone. Timing samples are descriptive, not a speed guarantee.
  • Delivery fixture correction 559f446d: the Node typecheck passes; the four affected entry/local/external/lifecycle suites pass 62 steps with global Git configuration disabled; source and compiled public journeys pass all four rows under the same restriction. Test helpers now have explicit array element types and use the configured ES2022 library; managed-commit fixtures supply their own invoking identity. Production behavior is unchanged.
  • Exhaustive runtime jobs, required delivery CI and runner test-weight refresh are in progress; no result is claimed before they finish. The separate automated PR Review currently fails on its compiled code-review-agent/mod.ts import, the same failure preserved in prior unrelated PR run 38090349206; it is not a required merge check.

Scope

The upstream capability is delivered here as one coherent change. Updating #181 and rerunning its blocked benches follows this upstream merge; no downstream stage edits are included.

New abstractions

ExecutionInstallation.fileMutations is the accepted private host boundary used by authored/generated File delivery and the per-entry repository provider. Minimal owned request/result records distinguish replayed execution state from external outcome reconciliation. No new dependency is introduced.

Risks and limitations

Recorded values can disagree with the live environment. The foreground user/agent inspects and handles that difference. Native local work whose result was never acknowledged may be attempted again; local replay makes no exactly-once or restoration promise. Unpublished prototype intent/completion journals are refused as unsupported, preserved rather than migrated.

Scope confirmation

  • Every changed file supports the capability described above.
  • Unrelated cleanup is excluded.
  • The mechanical type cleanup is identified above.
  • The description distinguishes focused evidence from pending delivery checks.

taras added 30 commits October 9, 2026 08:03
Slice A of the retained REPL repository host: the closed record protocol and
the admission that decides, before anything touches a checkout, which effects
already completed, which one was interrupted, and what state each checkout was
left in.

`records.ts` is the protocol. Version 1 is closed: an intent freezes the owner,
the canonical site, the operation, its lane position, the logical selection, the
authored request and its fingerprint, the resolved inputs, the state it expected
to find, the state it predicted and the evidence its result must match; a
completion names the intent it finishes and repeats that request's fingerprint.
A frontier carries semantic Git state and a content-and-mode worktree inventory
and has no member for a stat cache, a pack layout or a reflog time, so two
semantically identical checkouts compare equal. The owner is `repl:v1:` and
canonical JSON of the execution and entry together, which keeps it distinct from
the `runId` the Git-host and Issue records continue to spell.

`admission.ts` reads the exact frozen snapshot core admits and answers with
detached data. It selects nothing, leases nothing and installs no context, so a
wrong answer can refuse a mutation but never reach one. A record of this
feature's own type that does not parse refuses the continuation rather than
being skipped: replaying past it would mean guessing whether a commit, a push or
a file write already happened. A pending intent's predicted state is offered as
an attachment candidate beside the state its predecessors established, because
the interrupted operation may have reached either side of its own transition.

Nothing is installed yet. Slice A validates against supplied fixture
identities and grants no live authority; the engine-authenticated occurrence and
the provider that consumes this arrive with slice B.
Three R1 admission defects the slice A review demonstrated.

**An intent's fingerprint was read as given.** Nothing recomputed it, so a
request changed after the work was saved kept the original's name and the
changed request consumed the saved work. Admission now derives every intent's
request name from the record's own owner, site, operation, selection and
authored request, and refuses when the saved fingerprint is not that name.
Pending intents are held to it too, which is where a resumed operation would
otherwise act on a request the history never prepared.

**A completion could establish any state for any checkout.** Owner, site and
fingerprint were checked and the supplied frontier was then folded in under its
own checkout, so a completion naming the right intent could advance an unrelated
checkout to a state nothing prepared. A completion's resulting state must now be
one of exactly two the intent froze: the state it predicted, or the state it
started from — the latter being the already accepted rule for an operation that
proved it changed nothing. An intent's own frozen transition is also held to one
checkout inside the lane it claims to advance, so the two ends of a transition
cannot name different repositories.

**Shared Git state did not travel between linked worktrees.** Each checkout's
frontier was folded independently, so a commit in a linked worktree left every
sibling reporting the ref it had moved, and the next correct mutation of the
primary checkout was refused as a broken chain. Refs belong to the common
directory, so a completion now carries its ref list to every established
checkout sharing that directory while each keeps its own HEAD, index and
worktree inventory — only the worktree holding a branch can move it. Attachment
candidates are built from the folded state, so the final-plus-pending rule still
offers both sides of an interrupted transition.

The portable suite grows from 20 steps to 30. Every fixture's fingerprint is now
computed from the request beside it through the module's own encoders: the
placeholder strings the first version used would have kept the positive cases
green with the fingerprint check missing altogether. The new regressions cover a
changed request, a changed selection and operation, a substituted checkout, a
state the intent never froze, a proven nonmutating refusal, both lane
incoherences, shared-ref folding across a linked worktree, the primary mutation
that follows it, and the containment that keeps those refs out of a checkout in
another common directory.
The two unfinished parts of the slice A correction.

**A pending sibling mutation's shared state reached no other attachment.**
Candidates were built only from a pending intent whose own checkout was the one
being described, so an interrupted commit in a linked worktree left the primary
attachment offering nothing but the ref it had before. The interrupted operation
may already have moved that ref, and attachment would then refuse the state it
actually finds. Both ends of a pending mutation now reach every established
checkout sharing its common directory, as that checkout's own state under each
ref list: its HEAD, index and worktree stay its own and do not travel. The
established frontier is still the completed one — the prediction stands beside
it rather than replacing it.

**Unchanged state was accepted from any result.** Either frozen endpoint was
allowed for every completion and the result was never read, which turned the
nonmutating-refusal exception into a general one: an ordinary File write
carrying `FILES_WRITE_SUCCESS` and `publication: "host-committed"` could sit at
the state the file had before it, and the entry would resume believing the write
was done. An intent already freezes the exact result its operation will produce,
so that is what tells the two apart. A completion carrying the frozen evidence
performed the transition and has to stand at the far end of it; a completion
carrying anything else is a refusal and has to stand at the unchanged end. Where
an operation's two ends coincide there was nothing to change and both hold.
Admission compares the result rather than taking a new member, so the record
stays closed at seven members and the private completion data is what the
distinction is read from.

The portable suite grows from 30 steps to 35. The test that claimed a proven
nonmutating refusal supplied the same success result as every other completion,
which is what let the broadened exception pass; it now carries a real closed
refusal. Beside it are the two counterexamples — a successful write standing at
the state it was meant to change, and a refusal standing at the state its intent
predicted — the coincident-endpoint no-op, the pending sibling projection with
its HEAD-stays-put assertion, and the containment that keeps a pending mutation
in another common directory out of this checkout's candidates.
…ment

The last part of the slice A outcome correction.

Disagreeing with an intent's frozen evidence was treated as proof that the
operation refused, so any result that was not the predicted one was admitted at
the unchanged state. A File write reporting an ordinary `transaction-staged`
success — a write that happened — was therefore retained as a write that did
not, and so was arbitrary JSON.

A refusal is now parsed rather than inferred, in the vocabulary the operation's
own component already speaks. File operations go through the runtime's
`parseFileWriteFailure` and `parseFilesFailure`, so the words for operations,
phases, reasons and write targets stay the runtime's single definition of them;
the retained data is handed to those parsers the way every other consumer hands
it over. Repository, Worktree, Switch, Add and Commit retain one word from
`RepositoryFailureReason`, `WorktreeFailureReason` or `GitFailureReason`, held
to a compiler-checked map of each vocabulary so neither a missing word nor an
invented one survives. The word is the only part of such a refusal that is safe
to retain: the sentence beside it is written for a reader, and the component
that produced it keeps everything the platform said out of both.

A write refusal is held to more than being well formed. Its data says what
became of the target, and only `unchanged` and `rolled-back` are states nothing
was left in — `commit-unknown` is the runtime's word for not knowing, which is
exactly what cannot stand as proof that nothing changed.

An operation with no closed local refusal vocabulary retains none, and that is
deliberate: ambient selection fails the run rather than refusing, and the three
external operations complete through the existing `git_host_effect` and
`issue_effect` records. A managed-placement refusal has words of its own, but
they live with the provider that owns them, so admitting them belongs to the
slice that brings that module in.

The record is unchanged at seven members. The portable suite grows from 35 steps
to 39: the two fixtures that passed arbitrary JSON off as a refusal now carry a
real vocabulary word and real failure data, beside a different ordinary write
success, a well-formed-but-uncertain write failure, a delete refusal in its own
operation's failure data, three nonvocabulary results, and a coincident-endpoint
no-op that still has to carry a result its operation can produce.
One parser answers for every non-write Files operation, and the word it read was
never checked against the operation whose completion carried it. A closed read
failure — well formed, recognized by the runtime, and saying nothing whatever
about a delete — therefore completed a delete at its unchanged state, and the
entry would resume believing the file was still there when the delete may have
removed it.

A delete's refusal must now name `delete`, and a directory ensure's must name
`ensure-directory`, which are the words the host already emits for those
failures. The runtime stays the owner of the vocabulary; what changes is that
the answer it gives is read rather than discarded.

Two regressions, both over a genuinely changing transition so the unchanged end
is reachable only by a refusal: a delete whose file is present before and absent
after accepts its own failure and refuses a read, an ensure-directory and a glob
failure in its place; an ensure accepts its own and refuses a delete's. The
portable suite goes from 39 steps to 41.
Not a feedback commit. The authored half of the accepted fileMutations seam,
green against the core group B regressions (37 passed, 368 steps).
B1 of slice B: the accepted `fileMutations` seam, authored and generated, with
no native provider yet.

A host that journals the mutations its documents make has to know which
occurrence wrote, because that is what names the record a later open restores.
Everything a component could read that from is replaceable, so nothing is read:
the engine verifies the live invocation, keeps its identity in a terminal
private to that one mutation, and the host asks the terminal for it through a
continuation that exists only as the argument of one call. The pattern is the
Git host's — one descriptor per invocation under the Files Api's shared name, so
public middleware still applies while the chain ends in this invocation's own
default.

**Eligibility belongs to an implementation, not to a name.** Adoption attaches a
per-execution body to the definition core already registers or already built, so
`<File>` keeps the precedence it had: a repository component, a declaration or a
registration shadows it exactly as before, and a shadow is a different function
object, so it reaches nothing. Authored `<File>` and the `<File>` a fragment's
captured capability answers for are two definitions that legitimately share one
name, and a name-keyed table could hold only one of them — so the adopted
category has its own table, keyed by the exact function, while the registered,
private and protected categories keep their existing name rules and are
untouched by it. Two adoptions of one function are two owners for one body and
the second is refused.

The generated half carries unbound adoption declarations out of private profile
preparation — where the capability definitions are built, before identity
installation — and binds them when the execution mints its claimants. Final
sealing follows, unchanged. A read-only name offers no adoption at all.

Entering a routed body bypasses `formDispatcher`, and its own
`issuance.selection` check means a second dispatcher cannot be wrapped, so each
routed body reads the form off the engine's own issuance instead: the fact the
scanner recorded, beyond reach of the contextual chain. A `<File />` still
cannot reach a write and a paired `<File.Delete>` still refuses.
`files.ts` gained `writeOutcome()` so the routed and ordinary paths cannot come
to classify one provider answer differently, and the runtime names its Files Api
so core builds a same-name descriptor rather than repeating the string.

One consequence worth naming: the terminal is innermost, so a Files provider
that answers a write without delegating blocks it. That is the fail-closed case
the contract describes — a direct public call authors no retained completion —
and it means a retained entry's provider delegates its mutations. The new
fixtures install exactly that shape.

Evidence is 24 new steps in the four existing suites that own these contracts:
delivery of the frozen request and its occurrence, the host performing or
declining the write, read forms reaching no handler, sibling occurrences,
a substituted `expand.current`, middleware narrowing, middleware answering
without delegating, a handler submitting nothing, copied and out-of-order
terminal calls, duplicate and half handler sets, a refused path, authored and
generated coexisting under one name, generated writes and deletions on captured
terms, and adoption keying and revocation.
…iver

The three B1 gaps the review reproduced.

**One entry per mutation, consumed before the handler runs.** The only outer
guard was whether a result had already settled, and that is assigned after the
handler returns — so two overlapping calls to a public `next(input)` both
reached the default while the first was still suspended, and opened two private
terminals for one occurrence. The descriptor's entry is now consumed before
anything is yielded to, and closed in the router's own synchronous `finally`, so
a continuation a middleware kept and called after the invocation unwound finds
the entry gone instead of opening a fresh terminal under a finished occurrence.
Both the write and the delete router have it. Neither case reauthenticates
anything or mints another occurrence: both refuse before any provider work.

**The final request is parsed, not trusted.** Whatever the public chain handed
over went to the handler frozen but unread, so middleware that replaced `path`
with a number was passed straight through. The invocation-owned default now
parses the final request against the Files input contract — exactly the declared
members, each a string — and builds a detached frozen request from the admitted
values. Legitimate narrowing is unaffected; emptiness stays the provider's to
refuse, in the vocabulary it already has a word for.

**Captured methods keep their receiver.** Each method was read once and then
called as a bare function, so a handler set whose method reaches its own
receiver failed before it could inspect or answer. The methods are now bound to
the supplied set at capture time, which is what reading them once was always
meant to mean. Nothing is reread when the mutation happens.

Four regressions join the suite that owns these contracts: overlapping
continuations entering one handler, a continuation kept past a failed mutation,
a method that counts its own receiver, and a final request that is not a write.
Not a feedback commit. Steps 1 and 2 of B2, each verified against real state by a
disposable probe that is not kept:

- frontier.ts inventories a real checkout — symbolic and detached HEAD, refs, the
  complete index with assume-unchanged and skip-worktree flags, and every worktree
  path with its mode and bytes, including untracked files and empty directories.
  Administration is identified rather than inventoried. Re-reading one checkout
  compares equal; an untracked file or an empty directory makes it differ.
- local.ts holds the order the contract turns on: inspect, prepare without touching
  the target, prove the preparation changed nothing, append the intent, perform once,
  check the result against the frozen evidence and the state against the predicted
  frontier, then acknowledge. A resume adopts an exact post-state, performs from an
  exact pre-state, and refuses anything else. One lane per shared Git directory,
  which a probe showed serializes its own directory, runs beside another, and
  releases when its holder is cancelled.

The provider, the Files handlers, the Git/Dir adapters and the two native suites
remain.
Not a feedback commit. Steps 3 and 4, with the first native suite passing six
steps end to end through canonical execution.

Integration found four things reading could not:

- A lazily selected ambient checkout retained no baseline, so the history began
  with a mutation and admission refused it. Selection is now itself retained; it
  changes nothing, and it is the record every later mutation is held to following.
- The lane was held by the caller's scope, so a selection taken inside a mutation
  waited for the mutation it was selecting for. Each operation scopes its own.
- Lane positions were fixed at zero, and the durable name was a digest of the whole
  record — which moves with the state the operation observed, so a resume named a
  different operation. Positions are counted per lane, and the name is the request's
  own fingerprint.
- A resume looked its saved intent up by that same state-dependent digest and so
  never found it, then skipped the intent append and put its completion where the
  intent belonged. One path now covers both: the intent is appended either way, the
  saved preparation is used as saved, and a finished operation replays its result
  without re-checking a snapshot that later work has legitimately moved.

The suite proves a write inside the ambient checkout retains intent and completion,
a completed write replays with no native mutation, an interrupted write performs
from its saved state, adopts its predicted state, and refuses anything else without
repairing it, and a write where there is no repository stays ordinary.

The lifecycle suite, the six claimant adapters and complete group B remain.
Not a feedback commit. Two of six adapters, each authenticating its own
invocation and routing through the retained protocol.

Dir keeps its order and its sentences: the occurrence is claimed in its own
frame, the ensure goes through an invocation-private Files terminal so public
middleware still sees it, the directory exists before any content expands, and a
nested File authenticates its own occurrence rather than borrowing Dir's.

Git.Add predicts the index it will leave from read-only commands — --dry-run for
what the pathspec matches, hash-object without -w for what each file becomes — so
the object store is untouched when the intent is appended, and the real add is
checked against that index.

Integration found that containment excluded a checkout's own root, so a Git.Add
written at the top of a checkout found no selection. Selecting a checkout and
addressing a file inside one are different questions, and only the second one
excludes the root.
Not a feedback commit. Three of six adapters.

Git.Commit freezes everything a commit is a digest of — the tree, the ordered
parents, the exact message bytes, both identities and the instant with its
offsets — and computes the object id in a disposable copy of the administration,
so the intent records the exact commit that is about to exist. A retry reads the
message, instant and identity back out of the intent rather than asking the clock
again, because a different second is a different object. The empty-index refusal
stays what it was, decided from the checkout's own state before anything is
predicted or recorded.

The suite proves the commit Git wrote is the one the intent predicted, and that
an interrupted commit whose completion never landed is adopted rather than
committed twice — one commit, not two.
Not a feedback commit. Four of six adapters.

Git.Switch predicts a transition only Git can compute by copying the checkout,
switching in the copy, and freezing what the copy ends up in. The refusals a
document can act on happen in the copy first, so a switch that would overwrite
local changes refuses before the original is touched and nothing is forced — the
suite proves HEAD and the local file are both exactly where they were, and that
nothing was retained for an operation that never started.
Not a feedback commit. The six are in and the local suite passes fifteen steps.

Repository and Worktree are fenced differently from the mutations beside them,
and deliberately so. A mutation freezes the state it expects to find; a selection
has no such state, because the checkout it establishes does not exist yet. What
fences a selection is its placement — the managed slot is leased before anything
is created, a compatible existing checkout is observed rather than reset, and a
half-created one is preserved and refused. What it retains is the identity and
initial state it established: the baseline every mutation in that lane is held to
following, with its two ends one state so a resume adopts it.

Neither reimplements placement, metadata, reuse or refusal: both wrap the existing
managed selections and add the record and the occurrence that names it.

The lifecycle suite, generated-file coverage and complete frozen group B remain.
…p B green

Not a feedback commit. B2 feedback waits on the open absent-destination decision
for Repository/Worktree creation; the two selection adapters stand as they are
until that is settled, and the work here is independent of it.

`retained-repository-lifecycle.test.ts` carries R9 permanently. The lane is
exercised directly as well as through a document, because one document expands
its elements one at a time — which is the thing the lane makes safe, so a
document alone cannot show it doing anything. Six steps: one shared Git
directory serialized while another runs beside it, a cancelled holder releasing
its lane, one entry's mutations advancing one lane in order, a managed slot
another holder has refused rather than waited for, an entry's leases released
when it ends so the next entry may hold them, and a cancelled entry leaving
neither a lane nor a lease behind.

Generated coverage joins the local suite: a fragment's write inside the selected
checkout is retained on captured terms, with its own occurrence distinct from the
authored write beside it under the same name, following it in the same lane; and
a completed generated write replays with no native mutation.

Both native suites are registered as Deno-only with the existing
repository-provider reason and its #144 record. The reason states what is lost
and what still covers it: the portable half of this protocol — the records, their
admission and the sequence rules — is proven across all three runtimes by the
records suite.
…ssion fixed

Not a feedback commit.

**A regression dating to slice A.** packages/git/tests/module-partition.test.ts
declares every production module to exactly one of this package's three halves,
and a module named nowhere fails it. Slice A added src/retained-repository/
{records,admission}.ts without declaring them, and B2 added nine more — so that
suite has been red since slice A, which neither frozen group runs. Its first
failure output is preserved. All eleven are now declared: the records and their
admission are provider-neutral, and the retained entry host is the Deno adapter.

That declaration is what surfaced the next thing: records.ts is provider-neutral,
so it may not reach into the Deno adapter for the managed metadata parsers the
amendment's creation preparation is built from. Those parsers import only
composition/parse.ts and composition/records.ts and name no host, so they belong
in the neutral half; they move to src/composition/managed-metadata.ts with their
one importer and their partition entry following them.

The accepted record extension is in. An intent's preState is RetainedFrontier |
null, null meaning one thing — the managed slot is absent, checkout and metadata
together — and permitted only where a Repository or Worktree is being created.
A null pre-state must carry the closed creation preparation: the trusted slot,
the managed metadata, and for a worktree its owner's own two states, since adding
one advances refs the owner shares. Null on any other operation, or a preparation
this cannot read, refuses the record.

Admission follows it: an absent destination places no baseline, a creation whose
destination this history already established is refused, a worktree's owner must
already have a baseline in the state the creation says it found, a creation's
completion may stand only at the state it predicted, and an unresolved creation
offers its destination no attachment candidate while still offering its owner's
two ends. The protocol gains the amendment's three-way observation — adopt an
exact completed creation, perform from proven absence, preserve and refuse a
partial one — and preparation now proves it changed nothing by re-observing that
the destination is still not there.

The records suite is green at 41 steps. The selections adapters still create
before acknowledging; reworking them onto this is the remaining B2 work.
B2: the retained REPL entry's native repository provider, with all six claimant
adapters and both native suites.

Creating a checkout changes persistent state, so it is fenced like every other
mutation. `<Repository>` and `<Worktree>` now decide the whole request first, in
isolated scratch: the clone or the worktree add is performed against a
disposable directory, its inventory is mapped onto the placement the real one
will have, and a moving base is resolved exactly once. Only then is the intent
acknowledged — before a slot is made, a clone runs, a worktree is added, shared
refs move or managed metadata is written. The managed slot's lease is taken
before any of it is even looked at.

The state such an intent starts from is absence, recorded as the amendment
settled it: `preState: RetainedFrontier | null`, null meaning one thing — the
slot is absent, checkout and metadata together — and permitted only for these
two operations. Its closed preparation carries the trusted slot, the managed
metadata that will identify the destination, and for a worktree its owner's own
two states, because adding one advances refs the owner shares. Absence is never
an empty Git snapshot and establishes no baseline; only a matching completion
does, and later mutations then advance it normally.

A resume reads the destination rather than the clock: an exact completed
creation is adopted without cloning again, proven absence permits the saved
request from the saved commit, and a partial destination is preserved and
refused. The slot's metadata is written last, which is what makes a checkout
without it recognisably interrupted rather than finished.

Integration found two things reading could not. A linked worktree's `.git` is a
*file* holding an absolute path to its administrative directory, so inventorying
it as worktree content put a host path into the state — two checkouts that are
the same work would never have compared equal across two places on disk. It is
administration and is excluded, with its relationship recorded where the other
administration facts are. And Git derives that directory's name from the
destination's own basename, so the scratch prediction adds its worktree at a
directory with the same name and resolves collisions against the same copied
owner.

Evidence. `retained-repository-local.test.ts` is 21 steps: the retained file,
directory, staging, commit and switch paths; a commit's object id predicted
before it is written; an interrupted write performing, adopting and refusing;
generated writes on captured terms; and the five creation cases — an absent
destination recorded before any clone, a refused acknowledgement creating
nothing at all, an interrupted creation adopted without cloning again, a
resumed creation holding its saved commit after the base moves, and a partly
created destination left byte for byte. `retained-repository-lifecycle.test.ts`
is 6: lane serialization, a cancelled holder releasing its lane, one entry's
lane order, a slot refused rather than waited for, leases released with the
entry, and a cancelled entry leaving neither behind. The portable records suite
is 51 steps across Deno, Node and Bun, ten of them the new creation rules.

`module-partition.test.ts` gained the new module and caught it when it was
missing, which is what it is for.
B2 revision: the six findings of the review at `e6e504ca`.

A new operation no longer takes whatever is on the disk as the state it starts
from. Its own admitted history says what this checkout may be in — the folded
final state, and the two ends of any unresolved mutation of it — and a live
state that is none of them is drift nothing here recorded, refused before it
becomes anything's pre-state. A Worktree creation's destination and its owner
are one pair: finding the destination absent is not enough while the owner has
moved on, and finding it created is not enough while the owner is not where the
creation left it. Neither half decides alone.

Creation performs what its intent pinned. The saved performers clone or add from
`metadata.creationCommit` and the saved branch, verify the commit they reached
and write metadata last; resolving the authored base again a minute later can
answer with a different commit, and that is a different creation from the one
this history acknowledged. Adoption compares everything except
`refs/remotes/*`, because a creation cannot pin what a remote has since
advertised.

Preparation no longer writes into the checkout it is predicting. A commit makes
its scratch administration first and computes the staged tree only there,
deciding "nothing staged" from that tree against the one HEAD already has. A
linked worktree's prediction rewrites both ends of the reciprocal `.git`
pointer inside the copy, so switching in scratch cannot reach the real
administration through an absolute path.

Every shared Git directory this entry works in is now leased, at the selection
that makes the entry the one working there: the ambient checkout's, a managed
repository's own, and the owner's when a worktree is added to it. Taken
together with the placement leases in one order — sorted by the path each one
locks — and held for the entry, so they go when it goes. A lane waiter that is
cancelled now removes only its own barrier rather than resolving its
predecessor's, so a third entrant cannot overtake the holder.

A failure after native work may be printed only where it proves nothing was
left changed: the operation's own vocabulary says the file was not replaced and
the checkout bears that out, or for a creation the destination is still absent.
Anything else is work this history cannot describe, and it ends the entry
rather than becoming a line `<PrintErrors>` prints over. The retained operation
and lane failures are `StaleInputError`s for the same reason.

And a creation's preparation is checked as a relationship, not as fields: the
owner's predicted state may advance the refs an owner shares and nothing else,
so a prediction cannot invent checkout-local state and then have admission
offer it as a candidate.

Evidence. The four non-discriminating native assertions are repaired rather
than supplemented: adoption asserts a successful replay, its result and the
exact predicted state with no drift added first; the saved-commit case uses the
same moving locator and asserts native HEAD at the pinned commit; the partial
destination is the slot the document's own request derives, refused, preserved
and with no clone into it; and the acknowledgement fence rejects this feature's
own intent and asserts the barrier was reached once. Clones into a placement
are counted apart from the ones preparation makes in scratch.

`retained-repository-local.test.ts` is 26 steps and
`retained-repository-lifecycle.test.ts` is 8, the new ones covering: a new
mutation refusing a checkout its history cannot account for; a drifted owner
refusing a pending worktree creation; a nonmutating refusal printed with the
entry going on, beside a failure that left the checkout changed ending it under
the same `<PrintErrors>`; a commit writing no object into the checkout before
its intent stands; and two independent providers over one ambient checkout —
the second refusing while the first holds it, and working normally once it has
ended.
…print away

B2 revision: the eight remaining failures of the review at `3103e039`.

A checkout is authenticated once, when this entry first touches it, against
what its own history accounts for — the folded final state and the two ends of
anything left unresolved. Replaying results is held to it too: an entry whose
every mutation is resolved still has to find the checkout those records
describe before it answers with them, because answering otherwise is answering
about a checkout that is not there any more. From then on the entry tracks what
it expects rather than what it opened with: each acknowledged operation
advances its checkout's expected state, and the refs it moved fold into every
sibling of the same administration. Two new mutations in one entry now each
build on the last instead of the second being refused as drift.

A creation establishes the refs it froze. A clone takes whatever the remote
advertises when it runs, so one taken after a branch moved carried a
remote-tracking ref the prediction never saw — and the completion it wrote was
a state its own intent did not predict, a history the host could not reopen.
The saved performers now bring the new checkout to the predicted ref list
before finishing, and the adoption comparisons are exact again:
`sameRetainedCheckout` and its remote-ref exemption are gone.

A commit in a linked worktree is predicted from that worktree's index. The
scratch copy carries the whole shared administration, so the linked
checkout's own directory is in it; what was wrong was pointing Git at the
shared one, which computes the primary checkout's tree and reports that
nothing is staged.

A shared Git directory names its own lock, under one machine-wide directory
keyed by its canonical path, rather than one derived from the host root this
entry places its managed checkouts in. Two cooperating processes may be working
under different roots and still be moving the refs of the same `.git`, and
before this they took two different locks for one directory.

A proven printable refusal is written down. `<File>` being refused a path its
provider will not write is something the document is shown and carries on past
— but leaving its intent unresolved made the next write in that lane the second
unresolved mutation of it, which is a history that cannot be reopened. Such a
refusal now completes against the unchanged state it started from, in the
closed encoding admission already validates, and a replay restores it as a
refusal instead of reporting the refusal's own data as a value. A failure whose
vocabulary does not prove the checkout was left alone is uncertainty, and ends
the entry. `<Dir>` keeps that classification instead of replacing it with a
sentence about a directory the document could not use.

And a creation's preparation is read as one thing: a Worktree's owner and its
destination describe one repository, so they share an administration, an object
format and one predicted ref list, the destination is a linked checkout of that
owner, and the metadata names the administration and the commit the
destination stands on. Which slot a creation may name, and where that slot puts
its checkout, is checked by the host against the root it was trusted with,
because a record cannot know that.

Evidence. `retained-repository-local.test.ts` is 31 steps and
`retained-repository-lifecycle.test.ts` is 9; the portable records suite is 53
across Deno, Node and Bun. The new ones: a completed entry authenticating its
checkout before answering again; each new mutation building on what the last
was acknowledged to leave, with the resulting history admissible; a printable
refusal written down, reopened and replayed as a refusal; a changed-state
directory failure that `<PrintErrors>` cannot turn into a printed line; a
commit in a linked worktree predicted from that worktree's index with the
primary's HEAD unmoved; two entries under different host roots excluding each
other from one Git directory; and two record rules — one predicted ref list,
one object format — for the halves of a worktree creation.

The worktree-creation fixtures in the records suite described a pair that
cannot exist: an owner predicting `refs/heads/topic` at one commit beside a
destination created at another. They now agree, which is what the rules above
require of a real one.
…eation's branch

B2 revision: the two remaining findings of the review at `fcb0a573`.

A managed Repository or Worktree is authenticated at its own selection, the
same way the ambient checkout already was: once the ordinary managed selection
has decided the slot holds a checkout of the right repository, this entry asks
the separate question of what state that checkout is in, against the
final-plus-pending candidates its history accounts for. Before, nothing asked —
the selection's completion was replayed, which answered from records without
reading a byte, and marked the checkout attached on the way through, so even a
later file in it found the question already settled. A completed creation whose
checkout somebody has since changed now refuses there, with nothing replayed,
nothing expanded and nothing written.

A replayed acknowledgement no longer advances what the entry expects either. A
replay performs nothing and reads nothing, so the state it names is one this run
has not seen and an intermediate state of a finished operation is not what the
checkout is in: what it is in is whatever attachment authenticated. A first
acknowledgement still advances it, because that one got there by proving the
checkout reached the state it predicted.

And a creation's saved metadata is read against the request that asked for it.
The branch a Worktree creation saves is the branch that would be created and
checked out, so it has to be the branch the document named and the branch the
destination is predicted to stand on. All three agreeing is what makes the saved
metadata a description of this creation rather than of another one.

Evidence. `retained-repository-local.test.ts` is 35 steps and the portable
records suite is 55 across Deno, Node and Bun. The new ones: a managed checkout
whose bytes changed refusing before its results are replayed; a managed
checkout authenticated at selection even though nothing in the document reaches
it again; an undisturbed one replaying and going on to perform its remaining
write; a resumed entry whose shared refs moved in a sibling still writing in the
owner against an admissible history; and the branch disagreement beside the
agreeing control.

Two notes on what the controls show. Removing the selection-time authentication
fails the selection-boundary test; removing it *and* the replay guard fails the
reviewed byte-drift shape, which either one closes on its own. The replay guard
has no test that fails on its own account, because attachment now runs before
any replay can reach it — it is kept as the invariant the review states, so a
future adapter that forgets to attach cannot quietly answer from records again.

The records suite's creation fixtures now carry the authored request their own
components write, since that request is part of what the saved metadata has to
agree with.
B2 revision: the presence gap of the review at `cb9147c6`.

Attachment ran only where the native managed selection had found a checkout, so
whether the destination was there decided whether this entry asked what state it
was in. That is the one case where asking matters most: a destination whose
creation completed in this history and has since been removed is stale state,
and skipping the question let the completed selection replay its saved result
and the work after it carry on outside a checkout that is not there.

Both managed selections now ask unconditionally, and what the history holds
answers. A checkout with admitted frontier candidates must be in one of them —
and a destination that cannot be read at all is in none, so it refuses before
the saved result, the content or anything later. A destination with no
candidates is the absence a creation saved, which is what that creation's own
recovery is for; the pending paths, the exact adoption and the paired owner
reconciliation are untouched.

Evidence. `retained-repository-local.test.ts` is 36 steps: the new one completes
a Repository creation, removes the whole managed slot, resumes from the prefix
through the later write's own records, and expects the refusal, no later write
and the slot still gone. Gating attachment on an established checkout again
turns it red, and the existing creation cases — an absent destination recorded
before any clone, an interrupted creation adopted, a resumed one holding its
saved commit, a partly created destination preserved, a drifted owner refused —
stay green beside it.
Slice C of the retained repository host: `<Git.Push>`, `<PullRequest>`,
`<Issue>` and the three evidence reads, retained under the entry's own owner.

These are named by what they *ask*, and part of the ask is resolved rather than
authored — the branch the checkout is on, the commit it holds, the repository
identity its selection pinned. Resolving it again on a later open asks a
different question, consumes no retained result and performs the effect twice.
So the resolved request goes through the ordinary intent/completion protocol
before anything is contacted, and because none of these mutations changes the
checkout it was written in, the two ends of that record are one state and the
pair is complete before the service exists in the story. A later open reads the
request back instead of resolving it again. An issue is the one with nothing
resolved in it, so what its record adds is the barrier rather than the inputs,
in the lane of the checkout it was filed from; filed outside every selection
there is no such lane and none is invented.

What the service did stays where it already is. `git_host_effect` and
`issue_effect` remain the only authority on an external outcome, now reachable
through `reconcileOwnedGitHostEffect()`, `readOwnedIssue()` and
`upsertOwnedIssue()`: the same workers, the same invocation-private terminal,
the same publishable conditions and the same keys, under an owner a host states
instead of one derived from `getWorkflowRun()`. Borrowing stays where forks need
it, on the workflow entrypoints alone. A completed effect replays without
building a control repository, opening a credential session or reaching a
provider at all; an interrupted one observes under the frozen identity and
natural key, adopts a proven compatible completion, performs proven absence or
an attested ancestor advance once, refuses conflict and permanent ambiguity, and
fails on temporary unavailability. A push verifies its checkout afterwards too:
a remote accepting a branch does not excuse a local state this entry's history
can no longer account for.

A pull request is admitted from publications rebuilt privately out of accepted
records — each a retained push request, which says which checkout was published
from, paired with an accepted successful Git-host record, which says it
happened. Neither is evidence alone, and a successful record this version cannot
read refuses the admission rather than being skipped. The exact repository,
checkout, origin, destination, branch and commit have to match, so a commit made
after the push needs its own publication, and another entry may select, lease
and publish the same checkout while inheriting nothing.

The ambient selection now freezes what it resolved about the checkout it found.
A resume that rediscovered it would pin the identity at whatever HEAD is now, so
a pull request opened after a resume would name a repository this history never
recorded.

Evidence. New `retained-repository-external.test.ts`, 20 steps: both
interruption gaps for each mutation, compatible adoption, proven absence, an
attested ancestor advance with no fetch, a conflict that replaces nothing,
unavailability that is never absence, local drift after a published branch,
completed replays that ask the forge nothing and open no credential, the three
evidence collections answering from the journal while a later read observes what
changed, the same issue text at two sites as two effects, and publication
admission rebuilt after a reopen, refused without a push, refused after a later
commit, and not borrowable by another entry. Frozen group C is 31 passed/237
steps; group B native 23/127, group B core 41/396, group A 27/158 with the
portable records 55/55 on Node and Bun.
C revision: the two ownership findings of the review at `b80d75a1`.

A publication was a pair — a retained push request, which says which checkout
was published from, and an accepted successful Git-host record, which says it
happened — matched on the Git inputs alone. So a receipt performed under another
owner, or at another site in this entry, still stood for this request's
publication: the branch and the commit agreed, and nothing asked whose
publication it was. Each half carries an identity, and the pairing now requires
them to be the same one: the owner and the canonical occurrence the request was
frozen under have to be the identity the receipt was performed under. The exact
repository, checkout, origin, destination, branch and commit checks, the strict
refusal of an unreadable successful record, and the rule that an unpaired
receipt grants nothing are unchanged. Which site asked for the pull request is
still not compared, because a push and the pull request that names it are two
sites of one entry.

An evidence read was the one retained effect whose durable name leaves the owner
out, and that omission is a workflow fork's requirement: a fork reaching the
same position with the same question has to consume the record its source
wrote. In an entry it meant a segment holding somebody else's observation
answered with it — with no forge request, which is exactly what a legitimate
replay looks like, and with a well-formed collection nothing downstream could
question. The entry now reads each retained read's own request where it already
reads its whole history, and refuses one retained under another owner, or one
whose request it cannot read, before anything is attached or any credential is
opened. The workflow names and fork behaviour are untouched; every other
external record is named by a digest that includes its identity, so no live
request here can ask for one somebody else wrote.

Evidence. The five Planner probes pass (first failure — three steps across the
two findings — preserved in `probes.first.log`). The external suite is 25 steps:
two receipt negatives, one per identity member, each beside the positive control
that pairs; the foreign-owner read negative beside the read that succeeded; and
the two pre-mutation recovery controls for a pull request and an issue the
service refused. Disabling the identity comparison turns both receipt steps red
and skipping the observation admission turns the read step red; both were
restored. Frozen group C is 31 passed/242 steps, group B native 23/127, group A
27/158, lint exit 0. The two B2 probe sources that stranded test discovery are
copied outside the repository and left in place under a `.test.ts.txt` suffix,
so discovery and the exclusion registry are both green.
The three slices before this one built the retained host and proved it against
hosts a suite installed. This one makes `xmd repl` the thing that installs it,
and proves it the only way that claim can be proven: the public command, on a
real terminal, in a real checkout of a real remote, left and reopened.

`xmd repl` carries a factory rather than a host. The runtime-named entrypoints
that can answer for the platform pass one — the Deno source entrypoint and the
compiled binary — and the session calls it once per submitted entry, inside that
entry's own task. So a command that only draws a catalog builds nothing, a
settled entry projected from records builds nothing, a refused entry builds
nothing, and two entries of one session are two owners by construction.

The entry's own file access is rebound into the existing evaluation ceiling
rather than added beside it, so one execution still offers one maximum
permission. For that seam to be reachable at all, the host's ordinary Files
provider now delegates each mutation inward before performing it: it sits
outside the entry and used to answer writes itself, which left the entry's
private terminal unreachable in production while every injected test passed.
Only the condition that nothing was beneath it is caught, read from the closed
failure data rather than by class.

Evidence this commit is accountable for:

- `packages/cli/tests/repl-repositories.test.ts` — seven rows over the real
  Deno entry host: one host per entry, a cold view that builds none, a retained
  generated write, a refused entry and an unresolvable location, an interrupted
  entry continued once, and one entry's whole journey — Repository, Switch, Dir,
  File, generated write, Add, Commit, Push, PullRequest, its three evidence
  reads, a Worktree and a deferred Issue — left twice at an ordinary question
  and reopened twice, with no boundary performed again.
- `packages/cli/tests/repl-repositories-pty.test.ts` — the actual command on a
  pty. Under Deno an entry publishes out of an ambient checkout into a bare
  remote, `[exit]` ends it, and the location it printed reopens it in a second
  invocation that extends the same history. Under Node and Bun the same file
  proves repository work refuses before a lock, a credential or a directory
  exists, while ordinary file work keeps working.
- `scripts/tests/repl-repositories-compiled.test.ts` — the same journey against
  `dist/xmd`, which is where a dropped module graph would show. It runs in the
  `smoke` job that builds the binary, and is excluded from the shards that do
  not.
- The terminal machinery and the repository world are fixtures both terminal
  rows drive, so there is one account of what a pty is and one of what an
  entry's repository is.

The `[ok]` marker is the only thing those rows read off the screen: a draft is
drawn as it is typed, so waiting for a document's own words matches the draft
and asserts nothing. What an entry did is asked of the repository.
Two findings from D's review, and the terminal proof the first version of this
slice did not have.

**A frozen view continues nothing.** A location may name a history position, and
a session opened at one used to hand its unfinished final entry back to the
engine anyway: the screen showed the past while the work behind it started, built
a host, took a lease and replayed. Opening a frozen position is now a reading —
the unfinished entry is projected and left alone — and continuing it is what
returning to the head asks for. The session offers that as one idempotent act, so
a driver may say it on every frame that stands at the head: an entry already
running is left exactly as it is, an entry that has settled is not started again,
and a history with nothing unfinished has nothing to start.

Continuing *does* reach the repository, and both documents now say so where they
used to claim otherwise: the host is built, the saved checkouts are selected and
authenticated again and their leases re-acquired, and only then are the completed
boundaries replayed from their records. The effect-free claim belongs to
presentation — a settled history, and a frozen view of any history — not to a
live continuation.

**The whole journey, at a real terminal, in three invocations.** The source CLI
and `dist/xmd` each now run the entire vocabulary — an explicit `<Repository>`, a
`<Git.Switch>`, an authored `<Dir>`, a generated `<Evaluate>` write, Add, Commit,
Push, a `<PullRequest>` creation, its reviews, comments and checks, a linked
`<Worktree>` and a deferred `<Issue>` — with an ordinary `<Elicit>` standing
between the local work and the external work and another after it. Each
checkpoint is left through `[exit]`, and the location the command printed is
reopened by a *separate* invocation that answers the question and carries on.
What the rows assert is the world rather than the screen: the branch at the bare
remote, one pull request and one issue at the forge, the requests the last
invocation did and did not make, and the acknowledged prefix growing without
rewriting.

Two leaves had to be substituted for that, both outside the product:

- `git` itself, through a wrapper first on the row's own `PATH` that forwards
  every invocation to the real Git with one command-line
  `-c url.<bare-remote>.insteadOf=<canonical locator>`. The stored
  `remote.origin.url` stays canonical exactly as the product records it, nothing
  in the locator parser or the Git security configuration is relaxed, and the
  objects really travel.
- the forge, as the Git package's own modeled GitHub behind its own listener,
  reached through the two endpoint variables an operator already writes and a
  fixture credential. Its request URL now names the address the client actually
  reached, because an adapter compares the repository a payload names against
  its own endpoint.

The fixture canonicalizes its temporary home. Without that, the host keys a
managed checkout by the path it builds from `homedir()` while a write is
attributed by the path it arrived on — and on a platform where the temporary
directory is a symlink those are two spellings of one directory, so a write
inside the managed checkout is attributed to the ambient selection and the next
operation refuses a state nothing accounts for. Reported rather than fixed here:
the canonicalization belongs where the managed root is constructed.
**One canonical placement.** The root managed checkouts live under is created
and then canonicalized, once, by the first managed selection that asks for it —
and every identity derived from it now comes from that one spelling: the slot a
`<Repository>` is placed in, the common directory its `<Worktree>`s key on, the
key its selection is remembered under, and the lock that keeps a second process
out. A write arrives as the path the filesystem answers with, so a root reached
through a symlink used to leave a selection nobody could match: the write inside
an explicitly selected checkout left as an ordinary one, and the next operation
found a state this entry's history could not account for and refused.

The resolution is the ordinary provider's own pattern, moved to where both
providers can share it, and it is lazy on purpose. Creating that root is
infrastructure rather than a destination — no slot, checkout or metadata is
touched by it, and a placement's own creation still happens inside its
acknowledged intent — and an entry that selects nothing managed never asks, so a
cold view, a preflight and an ordinary unselected entry still reach none of it.
Leases are handed the root the same way, which is what keeps an entry whose only
lease is a shared Git directory from creating a placement root it never uses.

**A question after every boundary, and a separate invocation for each one.** The
two terminal journeys now walk one shared table: `<Repository>`, `<Git.Switch>`,
an authored `<Dir>`/`<File>`, a generated `<Evaluate>` write, `<Git.Add>`,
`<Git.Commit>`, `<Git.Push>`, a `<PullRequest>`, its reviews, its comments, its
checks, a linked `<Worktree>` and a deferred `<Issue>`. Each row carries its own
claim about the world — bytes on disk, what the index holds, where a ref points,
what actually reached the forge — and the document is assembled from the table
with an ordinary `<Elicit>` after each boundary.

One public invocation per boundary. Each one finds the question the invocation
before it left standing, proves that boundary is *still exactly as it was* — the
same refs, the same head, index and worktree in both checkouts, the same forge
mutations and not one further request — answers it, waits for the next boundary
to happen live, proves that one, and leaves while its question is pending. So
every boundary is asserted twice: once when it happened and once after a fresh
public invocation replayed it. The final reading comes out of the file rather
than the screen, because a transcript is elided to the pane it is drawn in.

Two findings the per-boundary walk turned up are reported rather than fixed
here, because neither is a path identity and both predate this slice: a
generated fragment resolves its writes against the working directory the entry
captured when it began rather than the checkout the enclosing `<Repository>`
selected, and such a write leaves no retained record at all when it happens
inside a continuation. The table says where that write actually goes and claims
nothing about who owns it.
The Architect amendment of 2026-10-11 supersedes whole-checkout recovery.
Completed effects replay without validating or restoring anything; new local
work uses live state, and an operation with no acknowledged result may be
attempted again. External reconciliation, permissions and entry ownership are
unchanged.

What that removes is the experimental protocol: no intent written before an
effect, no checkout inventory, no transition predicted in a scratch clone and
no saved state compared with the live one. `frontier.ts`, `scratch.ts`,
`creation.ts` and `commit.ts` are gone, and the local boundary is one durable
operation whose name is the entry's own and whose value is what performing it
produced. The old `retained_repository_intent`/`completion` pair is refused as
a superseded protocol rather than read.

The honest window this buys is stated where it lives: native work can happen
and its result can fail to be appended, so a continuation takes the ordinary
live path and may attempt it again. That is durable-stream behaviour, not an
exactly-once local effect, and it is what the REPL trades for not snapshotting
anybody's working tree.

Assertions that encoded the old contract are updated rather than worked
around: a completed push now replays with no Git work at all, and a completed
pull request asks neither the forge nor Git for anything. The in-process
directory exclusion moved from a module-scoped table to a context default,
which is what `local/no-module-scoped-registry` asks for.

Architecture and both specifications now describe recorded results in the
present tense, including the generated write that resolves against the
directory the element producing it was standing in.
A saved result that was not a recognized refusal became success. The three
file operations then discarded the value and reported their own success, so a
Files failure naming another operation — a read, say — substituted into a
completed write, delete or ensure authorized everything standing after it.
Rejecting a value as a refusal never established that it was a result.

Each of those three operations now has its success written down in one place
and recognized from it: `retainedFileResult` produces the encoding and
`retainedRepositoryOutcome` reads the two branches a saved outcome has — that
result, or a refusal in the operation's own existing vocabulary. A value
matching neither is unreadable history.

Unreadable history is raised rather than answered. The result channel is for
refusals the operation reported, and `<PrintErrors>` may print one and carry on
past it, which is how the delete reproduced: its `Err` arrived as a printable
`FileDeleteError` and the later write ran. The three integrity failures in
`recorded` — an unreadable record, one describing another operation, and now an
unreadable outcome — all leave through the fatal path they belong to.

Operations whose result is their own data are untouched: a commit keeps its
SHA and a selection its placement, and the reader that owns those members is
still the one that parses them.
Code Rule 6: parse to infer a type rather than casting with `as`. The fixed
success lookup narrows on the union's own members, the portable fixture reads
its payload through `detachedJson` — the parser the records themselves are read
with, so a fixture cannot hand a row a value no journal could carry — and the
three reopened file operations are declared once as a typed table instead of
asserting each literal.
@taras
taras marked this pull request as ready for review October 11, 2026 15:38
@taras
taras merged commit afe8a5f into main Oct 11, 2026
44 of 45 checks passed
@taras
taras deleted the agent/retained-repl-repositories branch October 11, 2026 15:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant