docs: row 117 brief, fetch a role clone that is on another branch or dirty (#1611)

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-14 12:20:17 -05:00
co-authored by Claude Opus 5.5
parent fed66feff6
commit 559ef22875
@@ -0,0 +1,101 @@
# A role clone off the root's branch still gets fetched (2026-10-14)
Filed by Sage on Mos's report of 2026-10-14. Gitea #1611.
## Fetch a role clone that is on another branch or dirty
### Problem
At each launch the host updates a role's project clone only when the clone
is clean and on the root's branch (`packages/seat/src/checkout.mjs`,
`update()`). Every other clone is kept, and the host runs no more git in it.
For `other-branch` and `dirty` that is more than safety needs. By then
`unsafe()` has passed, the index has no gitlink and the root's branch is
known, so the host's fetch is as safe there as in a clean clone. Skipping it
leaves the clone's `refs/remotes/origin/*` as old as the last launch that
found the clone clean and on the root's branch.
A coder sits on a work branch for most of a task, so this happens all the
time. On 2026-10-14 the coder was blocked on 19/266 because its clone's
`origin/refactor` was 0c3826910, several landings behind. That clone was
also kept for `config-changed`, from a `[user]` section written at 19:53Z
on 10-13, before row 104 (#1598) gave sessions their identity through the
environment. The coder removes that section itself as a one-off. It isn't
part of this row, and `config-changed` stays as it is.
### Owner and reviewer
- Owner: darkwing, before row 116.
- Reviewer: dewey (decision 130: the host runs git in a clone the session
can write).
### Files owned
- `packages/seat/src/checkout.mjs`
- `packages/seat/tests/checkout.test.mjs`
- `packages/seat/README.md`, "The project clones"
- `packages/cli/src/launcher.mjs`, the record line after `ensureCheckouts`
- `packages/cli/tests/launcher.test.mjs`
- `packages/cli/README.md`, the checkout paragraph in "Sessions"
- `packages/harness/src/bundle.mjs`, `checkoutLines()`
- `packages/harness/tests/bundle.test.mjs`
Nothing in `unsafe()`, `allowed()`, `gitEnv()`, the `SAFE` arguments or the
sandbox changes. If the fix seems to need any of them, stop and tell Sage.
### What ships
1. In `update()`, a clone that would be kept for `other-branch` or `dirty`
gets the same fetch the clean path runs, with the same arguments and
environment, and no merge. Only those two reasons fetch. Every check
before them (`unsafe()`, the gitlink check, the root read) still keeps
the clone with no fetch, as today. A detached HEAD is `other-branch` and
fetches too.
2. The result is a new outcome, `fetched`, with:
- `reason`: `other-branch` or `dirty`;
- `branch`: the clone's own branch, or `null` on a detached HEAD;
- `commit`: the clone's HEAD (`null` when git can't read it);
- `origin`: `{ branch, commit }`, the root's branch and the commit of
`refs/remotes/origin/<branch>` after the fetch (`null` when unreadable).
A fetch that fails is `kept` with `fetch-failed`, as today. If you find a
better shape than a new outcome, record the choice and the reason in the
packet.
3. The launcher writes a `checkout-fetched` line to the launch log, with the
project and the reason, next to `checkout-kept`.
4. The bundle's prompt names a fetched clone's branch and commit, says it
wasn't fast-forwarded and why, and names `origin/<branch>` at its commit.
The shared sentence changes to say the host fetches every clone it can
safely run git in at a launch, and fast-forwards only a clean clone on
the root's branch. The rest of the sentence (never during a session,
don't set git config, identity from the environment) stays.
5. The READMEs say the same. The seat README's table gets the `fetched`
row, and the reason table says which reasons fetch.
### Not in this row
- Accepting `user.name` or `user.email` in a clone's config. Refusing them
is deliberate (row 104).
- Merging, rebasing or resetting anything in a fetched clone.
- Fetching a clone kept for any other reason.
### Acceptance
- For a dirty clone (modified, untracked, staged) and one on another branch
or detached: after a commit to the root, the launch returns `fetched`,
`refs/remotes/origin/<branch>` is the root's new commit, and HEAD,
`refs/heads/*`, the index and every file outside `.git` are byte-for-byte
what they were. The existing "kept as they are" test becomes this test,
with its fingerprint narrowed to those parts.
- A clone kept for each earlier reason (`not-a-clone`, `hooks-present`,
`submodule`, `config-changed`, `root-unreadable`, `root-detached`) still
runs no git, the fetch included. The rigged-clone test still passes
unchanged.
- A failed fetch in a dirty clone is `kept`, `fetch-failed`.
- The launcher test sees `checkout-fetched` with the reason, and the bundle
test sees the new line.
- Mutants, one per check, listed in the packet: fetch for a third reason;
merge after the fetch; drop the fetch; report the wrong `origin` commit;
fetch before the gitlink check. Each one fails a test.
- Full gate green. Dewey approves on #1611 naming the candidate manifest.
Sage reruns the gate at the landing target. Going live needs a re-pin on
Mos's go (decision 109).