fix(native): the fleet pass has been a no-op since 2026-08-05 #1088

Merged
coilysiren merged 1 commit from aos/claude/gq98-worktree-guards into main 2026-08-16 05:18:12 +00:00
Member

Implements Kai's "I want to do all 5 things" on #1084 and #1086. Four land here. Implementing them surfaced a sixth that supersedes the diagnosis in both issues, so it leads.

The pass was disabled, not merely skipping

I told Kai the fleet pass skipped her checkout because thirteen worktrees made it read as active. That was wrong, and the truth is worse.

nativePathKey resolves through EvalSymlinks, which fails on a path that no longer exists. add() read that as uncertain identity and set live.uncertain, and contains() answers true for every path once set. One purged session worktree therefore made every repository read as live, and normalizeNativeRepository returned at its first line for all of them.

The oldest such lease has been dead since 2026-08-05. That is why no checkout was swapped, no main fast-forwarded, and no worktree released.

Absence is an answer rather than uncertainty, so it now says "not live". Only a path unreadable for another reason still fails closed, which the existing fail-closed test now exercises with an ENOTDIR path rather than a missing one.

A worktree on main locks it, and can repoint it

releaseNativeDefaultBranch detaches a squatting worktree at its current commit, live ones included, leaving files untouched. Live ones matter: cleanUnleasedNativeWorktrees skips them, so nothing freed main in deploy.

repairNativeMainUpstream resets branch.main.merge when it points elsewhere. Branch config is repository-global, so a worktree on main pushing with -u rewrote it for Kai's checkout, and with pull.rebase her next pull rebased main onto a session branch.

Reaping and lease reconciliation

reapNativeMergedBranches deletes local branches already wholly on origin, which nothing did before: 429 branches across eleven repositories, 253 reapable.

The aos/ namespace is skipped because session-ID uniqueness reads it. I did not reason that out - TestNativeLaunchRetriesOccupiedShortSessionIDs failed and showed me the reaper had deleted the branch marking an ID as taken.

reconcileNativeArtifactBranch reads the worktree's actual branch rather than the lease's record, which a mid-session switch can leave stale.

Tests

Four new, and each verified to fail without its fix rather than merely passing with it. Full suite green.

What is deliberately not here

The AGENTS.md doctrine line. That file is already 293 lines against its 290-line cap on main, so any edit to it fails documentation-layout. Making room means moving someone else's doctrine out of the always-loaded base, and moving a rule to docs/ is not equivalent to moving an explanation - agents read AGENTS.md every session and docs on demand. That is an editorial call, not a formatting one, and it is Kai's. Filed separately.

Review notes

  • releaseNativeDefaultBranch detaches live sessions. Non-destructive by construction, but it changes HEAD under a running agent. The alternative leaves deploy wedged for as long as that session lives.
  • reapNativeMergedBranches deletes branches. Guarded by rev-list --not --remotes=origin, the same test the existing code uses, and stricter than git branch --merged.
  • Explanation lives in docs/native-default-branch.md; the code carries pointers, per this repo's comment discipline.

Refs #1084, #1086

Implements Kai's "I want to do all 5 things" on #1084 and #1086. Four land here. Implementing them surfaced a sixth that **supersedes the diagnosis in both issues**, so it leads. ## The pass was disabled, not merely skipping I told Kai the fleet pass skipped her checkout because thirteen worktrees made it read as active. That was wrong, and the truth is worse. `nativePathKey` resolves through `EvalSymlinks`, which fails on a path that no longer exists. `add()` read that as uncertain identity and set `live.uncertain`, and `contains()` answers **true for every path** once set. One purged session worktree therefore made every repository read as live, and `normalizeNativeRepository` returned at its first line for all of them. The oldest such lease has been dead since **2026-08-05**. That is why no checkout was swapped, no `main` fast-forwarded, and no worktree released. Absence is an answer rather than uncertainty, so it now says "not live". Only a path unreadable for another reason still fails closed, which the existing fail-closed test now exercises with an `ENOTDIR` path rather than a missing one. ## A worktree on main locks it, and can repoint it `releaseNativeDefaultBranch` detaches a squatting worktree at its current commit, **live ones included**, leaving files untouched. Live ones matter: `cleanUnleasedNativeWorktrees` skips them, so nothing freed `main` in `deploy`. `repairNativeMainUpstream` resets `branch.main.merge` when it points elsewhere. Branch config is repository-global, so a worktree on `main` pushing with `-u` rewrote it for Kai's checkout, and with `pull.rebase` her next pull rebased `main` onto a session branch. ## Reaping and lease reconciliation `reapNativeMergedBranches` deletes local branches already wholly on origin, which nothing did before: **429 branches across eleven repositories, 253 reapable**. The `aos/` namespace is skipped because session-ID uniqueness reads it. I did not reason that out - `TestNativeLaunchRetriesOccupiedShortSessionIDs` failed and showed me the reaper had deleted the branch marking an ID as taken. `reconcileNativeArtifactBranch` reads the worktree's actual branch rather than the lease's record, which a mid-session switch can leave stale. ## Tests Four new, and **each verified to fail without its fix** rather than merely passing with it. Full suite green. ## What is deliberately not here **The AGENTS.md doctrine line.** That file is already **293 lines against its 290-line cap on `main`**, so any edit to it fails `documentation-layout`. Making room means moving someone else's doctrine out of the always-loaded base, and moving a *rule* to `docs/` is not equivalent to moving an *explanation* - agents read AGENTS.md every session and docs on demand. That is an editorial call, not a formatting one, and it is Kai's. Filed separately. ## Review notes - **`releaseNativeDefaultBranch` detaches live sessions.** Non-destructive by construction, but it changes HEAD under a running agent. The alternative leaves `deploy` wedged for as long as that session lives. - **`reapNativeMergedBranches` deletes branches.** Guarded by `rev-list --not --remotes=origin`, the same test the existing code uses, and stricter than `git branch --merged`. - Explanation lives in `docs/native-default-branch.md`; the code carries pointers, per this repo's comment discipline. Refs #1084, #1086
fix(native): the fleet pass has been a no-op since 2026-08-05
All checks were successful
ci / ward-doctor (pull_request) Successful in 12s
ci / aos-cli-tests (pull_request) Successful in 17s
ci / gate (pull_request) Successful in 51s
623589902a
Kai asked for all five proposals on agentic-os#1084 and #1086. Four land
here. Implementing them surfaced a sixth that supersedes the diagnosis in
both issues, so it leads.

THE PASS WAS DISABLED, NOT MERELY SKIPPING

nativePathKey resolves through EvalSymlinks, which fails on a path that no
longer exists. add() read that failure as uncertain identity and set
live.uncertain, and contains() answers true for every path once it is set.
So a single purged session worktree made every repository read as live,
and normalizeNativeRepository returned at its first line for all of them.

The oldest such lease has been dead since 2026-08-05. That is the real
reason no checkout was swapped, no main was fast-forwarded and no worktree
was released, not the inactivity scoping I proposed on #1084.

Absence is an answer rather than uncertainty, so it now says "not live".
Only a path unreadable for another reason still fails closed, which the
existing fail-closed test now covers explicitly with an ENOTDIR path
rather than a missing one.

A WORKTREE ON MAIN LOCKS IT, AND CAN REPOINT IT

Git allows one checkout of a branch per repository, so a session worktree
holding main stops the canonical checkout returning to it, and the pass
fails on the same fatal rather than repairing it. Live worktrees are
skipped by cleanUnleasedNativeWorktrees, so nothing freed it.

Worse, branch config is repository-global: a worktree sitting on main that
pushes with -u rewrites branch.main.merge for every checkout. With
pull.rebase set the operator's next pull rebases main onto a session
branch and reports divergence against a ref they never chose. That is what
happened to deploy.

releaseNativeDefaultBranch detaches such a worktree at its current commit,
live ones included, leaving files untouched. repairNativeMainUpstream
resets branch.main.merge when it points elsewhere.

REAPING AND LEASE RECONCILIATION

reapNativeMergedBranches deletes local branches already wholly on origin,
which nothing did before: 429 across eleven repositories, 253 reapable.
The aos/ namespace is skipped because session-ID uniqueness reads it, and
reaping it hands out an ID still in use - caught by an existing test
rather than by inspection.

reconcileNativeArtifactBranch reads the worktree's actual branch instead
of the lease's record, which a mid-session switch can leave stale.

The AGENTS.md doctrine line is NOT here. That file is already 293 lines
against its 290-line cap on main, so any edit to it fails the hook, and
making room means moving someone else's doctrine out of the always-loaded
base. That is an editorial call rather than a formatting one. Filed
separately.

Refs coilyco-flight-deck/agentic-os#1084, coilyco-flight-deck/agentic-os#1086

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Kai Siren <coilysiren@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
coilysiren deleted branch aos/claude/gq98-worktree-guards 2026-08-16 05:18:13 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
coilyco-flight-deck/agentic-os!1088
No description provided.