Repository navigation
feat(overleaf): add the shared checkout barrier (step 3a) - #626
Conversation
An Overleaf apply and a bulk change of the same checkout now exclude each other (coordinator contract, section 9). A checkout is its Git top folder by physical identity. A bulk change closes new apply admission at once, waits for running applies, runs registered settlers inside its section, then mutates. It advances the checkout's epoch as it starts and as it ends. Applies asking to start meanwhile are refused, not queued. Bulk changes do not exclude one another. Hooks, each marked SCIENT-FORK overleaf-checkout-barrier: - Git driver method table: pull, switch, create-and-switch, pull-request worktree refresh, worktree removal (covers callers that bypass GitWorkflowService, such as startup auto-pull) - CheckpointServiceV2.restore - GitManager's local pull-request checkout (provider CLIs) - storage cleanup's worktree removal The barrier is a Context.Reference with one default instance per process. Nothing takes an application section yet; step 3b's coordinator will. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Thread transfer impact✅ Thread transfer remains within every enforced ceiling.
Baseline: Scenario and decoded snapshot size10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.
Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed. |
…heckouts Codex round 1 on #626: - Bulk changes of one checkout now run one at a time, so a settler's inline recovery never overlaps another restore or pull. A bulk change made inside another of the same checkout joins it (fiber-scoped held set). - removeWorktree resolves a relative path against the repository, as Git does. - A checkout inside another (submodule, nested repository) belongs to both: an outer bulk change waits for and suspends inner applications, and inner epochs include the enclosing ones. - Lookup failures are a typed CheckoutLookupError, mapped by every hook. - The cancellation test waits for real admission before interrupting. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex review, round 1 (gpt-6.1-sol, high reasoning)Verdict on
Each fix fails its test without it (mutation-checked, one failure each). What held:
Now: 14 barrier tests pass. The related suites pass, apart from the 14 orphan-report tests that fail on main's file on macOS too. Typecheck and lint are clean. Round 2 is reviewing the new head. 🤖 Generated with Claude Code |
Codex round 2 on #626: - A nested bulk change joins only while the inherited section is still held, so a detached task started inside one cannot bypass the barrier later. - A queued bulk change looks its path up again once its turn comes and refuses if the path now names another checkout. - Settlers get the inner checkout's full chain and summed epoch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex review, round 2 (gpt-6.1-sol, high reasoning)Verdict on
Each fix fails its test without it (mutation-checked, one failure each). Known limit: a nested bulk change of the same checkout made from a fresh fiber ( Now: 17 barrier tests pass. The related suites pass, apart from the 14 macOS orphan-report tests that fail on main too. Lint is clean. Round 3 is reviewing the new head. 🤖 Generated with Claude Code |
…hed paths Codex round 3 on #626: - Work that joins a live bulk section is counted, and the section is not released until it finishes, so joined work never runs after admission reopens. - A queued change whose path vanished meanwhile, with `missing: "skip"`, runs without settlers, which cannot settle a checkout that is gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex review, round 3 (gpt-6.1-sol, high reasoning)Verdict on
Each fix fails its test without it (mutation-checked). What held: round 1 and 2 fixes, refusal of stale handles, identity refusal before settlers, and epoch accounting on refusal. No new bypass or lock cycle was found. Known limits:
No production caller does either. Now: 19 barrier tests pass. The related suites pass, apart from the 14 known macOS orphan-report tests. Lint is clean. Round 4 is reviewing the new head. 🤖 Generated with Claude Code |
Codex round 4 on #626 found two more edges in cross-fiber joining (a non-atomic liveness check, and an interrupted owner waiting on a joined child). Production only nests bulk changes synchronously in the same fiber (a provider checkout calling the Git driver), so joining is now limited to the fiber that owns the section, while it holds it. Any other fiber, a forked child included, waits its turn. The join accounting is gone with both bugs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex review, round 4 (gpt-6.1-sol, high reasoning)Verdict on
Resolution: a simpler rule, not another patch. Each round from 2 to 4 found a new edge in joining across fibers, while production only nests bulk changes synchronously in the same fiber (a provider checkout calling the Git driver). So:
The join accounting is gone, and both bugs with it. Tests:
Known limit, now stated plainly: a section that awaits another fiber's change of the same checkout waits for itself. No production caller does this. Now: 20 barrier tests pass. The related suites pass, apart from the 14 known macOS orphan-report tests. Lint is clean. Round 5 is reviewing the new head. 🤖 Generated with Claude Code |
Codex review, round 5 (gpt-6.1-sol, high reasoning)Verdict on
Evidence: 20 barrier tests and 8 extra probes passed, plus 845 regression tests (one skipped). Storage cleanup shows the 14 known macOS orphan-report failures. 🤖 Generated with Claude Code |
…out-barrier-20261022
…r changes The Git driver now imports scient/workspace/CheckoutBarrier.ts, so Droid's qualification loads it. The managed-provider workflow guards every module its qualification runs; add the barrier file to Droid's guard list (managed-provider-runtime-workflow.test.ts). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CI fix after aligning with mainBringing this branch up to main pulled in 🤖 Generated with Claude Code |
What this adds
Step 3a of the Overleaf plan (#535, coordinator contract section 9). It adds a shared checkout barrier, so an Overleaf apply never runs at the same time as a bulk change of the same project. Bulk changes include a checkpoint restore, a pull, switching branch and removing a worktree.
No apply uses the barrier yet; the step 3b coordinator will. Until then, bulk changes register with it at the cost of a few file-status lookups (about 0.4 ms) and an epoch count. Nothing changes for users.
Rules
CheckoutSuspendedError).CheckoutRecoveryRequiredError). Step 3b registers the Overleaf settler.CheckoutLookupError. Hooks surface it as aGitCommandError, a restore error, or a kept storage-cleanup entry.Where it hooks in
Each hook is one wrapper marked
SCIENT-FORK:START overleaf-checkout-barrier, and they're listed in UPSTREAM.md.GitVcsDriverCore.ts)GitWorkflowService: startup and broadcaster auto-pull,GitManager's own calls, and providerswitchRef.CheckpointServiceV2.restoregit restore/git clean/git reset.GitManagerlocal pull-request checkoutglab mr checkout) that never reach the driver.worktree move/worktree remove. A refusal keeps the worktree.The barrier is a
Context.Referencewhose default is one instance per server process. Every layer build shares it.Not covered
Tests
CheckoutBarrier.test.tshas 20 tests:git switch, and a relative-pathgit worktree remove, each waiting for an application;Mutation checks: every rule and fix was removed in turn, and its tests failed each time.
The related suites pass, apart from 14 orphan-report tests in
storageCleanup.test.ts, which fail identically on main's file on macOS (/varvs/private/var). Typecheck, lint, knip andscient-seam-checkpass.Review
Five Codex rounds (gpt-6.1-sol, high reasoning), recorded in the comments.
Next: step 3b, the coordinator's apply, recover and adopt, plus kept-copy compare and restore.
🤖 Generated with Claude Code