test(worktree-helper): strict registration check, positive no-cleanup check, and a real askpass readiness deadline - #2674
Conversation
…skpass deadline Three test-only fixes in the worktree helper regression suite (#2670). Late-collision registration check: Wait-ForScheduledWorktreeRemoval now takes the repository whose registration list it polls and waits, on the one 90 s deadline, until both the worktree directory is gone and the normalized registration is gone, returning Removed, Unregistered, Registrations and ElapsedSeconds. Git prints registration paths with forward slashes on Windows, so the old backslash Contains check at the first call site was always true and never caught a stale admin entry. Both call sites now assert Removed and Unregistered with the elapsed time and the final list in the message. Refused-cleanup paths: replaced the 500 ms settle before the ignored-content assertions with a positive Assert-NormalizedNotContains that the initializer did not report a scheduled detached removal, and applied the same assertion to the hidden-index refusal loop. Askpass probe server: raised the fixed 10 s readiness deadline to the suite's 90 s host-start order and put the elapsed seconds in the failure message.
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
… in the readiness comment
Review disposition (beta-platform-integrity lane, T3 gate)Fresh-context adversarial review at 1381065: SHIP. No CRITICAL or HIGH. Confirmed by the reviewer: test-only change, no production helper touched; the extended wait polls directory-gone and registration-gone under the same 90 s deadline, and since the old collision-site check was vacuously true (backslash path against forward-slash porcelain), the normalized comparison can only detect more, never less; the not-scheduled assertion is non-vacuous by construction (the phrase is printed only after the initializer's scheduling step, and every refusal or could-not-schedule path exits first), so it cannot false-fail on a correct run. Fixed (78719b5, comment only)
Declined, with reasoning
GateNot measured locally: either 90 s deadline near its limit (both paths finish well inside the old bounds on this box), and item 3 has no red proof for the same reason. The hosted Windows helper job at the exact head is the proof; merge once exact-head |
Summary
Three test-only fixes in
scripts/git/Test-New-CodexIssueWorktree.ps1, the worktree helper regression suite. No product script changed;scripts/git/New-CodexIssueWorktree.ps1andscripts/git/Initialize-CodexIssueWorktree.ps1are untouched, so no test-only marker was needed for item 2.Wait-ForScheduledWorktreeRemovalnow takes the repository whose registration list it polls (-RegistrationRepositoryPath) and waits, on the one existing 90 s deadline with the same 100 ms poll, until both the worktree directory is gone and the normalized registration is gone. It returnsRemoved,Unregistered,RegistrationsandElapsedSeconds. Both call sites in theinitializer-validationcase (the late branch collision and the separate-Git-dir sibling) now assertRemovedandUnregistered, each message carrying the elapsed seconds and the final registration list.Start-Sleep -Milliseconds 500before the ignored-content assertions is gone. In its place, a newAssert-NormalizedNotContainshelper asserts the initializer's captured output does not containremoval of the unused helper-created worktree was scheduled, so a regression that wrongly started the detached cleanup host fails immediately instead of passing because the host had not got round to deleting the canary yet. The same assertion is applied to the hidden-index (assume-unchanged/skip-worktree) refusal loop, which shares the shape. The existence and registration assertions are kept unchanged.$serverProcess.HasExitedare left as they are.Root cause
Item 1 was vacuous, not merely weak.
$initializerWorktreeis a backslash-separated Windows path built withJoin-Path, whilegit worktree list --porcelainprints registration paths with forward slashes, so$registrationsAfterCollision.Contains($initializerWorktree)was false whatever the registration state. Proved with a temporaryWrite-Hostof both strings, from a single-Case initializer-validationrun:The separate-Git-dir sibling already normalized with
.Replace('\', '/'); this site did not. The print was removed before committing.Making the check strict on its own would have reintroduced the timing red that #2664 fixed, because
git worktree removedeletes the working directory before the admin entry under.git/worktrees, and the scenario asserts on the directory first. Waiting for both facts on the same deadline avoids that: a leftover admin entry now fails as a stale registration rather than as a race.For item 2, a 500 ms sleep cannot observe the failure it guards. The detached cleanup host would delete the canary one host start plus seven Git invocations later, so both
Test-Pathassertions would still pass. The sleep was dropped, not lengthened; no reason to keep it was found, since the surviving assertions no longer depend on elapsed time.Verification
All commands run from the worktree root
C:/Users/jekyt/source/Taskdeck-Beta/.worktrees/codex-2670-helper-suite-waitsin a native Windows PowerShell 5.1 host, not via Git Bash.Parser check:
Full suite:
Targeted runs of the two changed cases, before the full run:
-Case initializer-validationgavePASS: 1/1,-Case askpass-suppressiongavePASS: 1/1, both exit 0.Red proof, item 1. With the fix in place, a temporary patch re-registered the exact worktree path with Git after the real scheduled removal completed and then deleted only the directory, leaving the stale admin entry the old check could never see, and re-ran the wait with
-TimeoutSeconds 5. The old expression was printed alongside the new field:So the same stale registration that the old assertion passed through now fails the case. The temporary patch was reverted with
git checkout --and the working tree confirmed clean.Red proof, item 2. The new
Assert-NormalizedNotContainswas temporarily pointed at a diagnostic the refusal path does emit (cleanup was refused because the helper-created worktree contains tracked, untracked, or ignored content) and-Case initializer-validationfailed withEXIT=1and the assertion'sForbidden substring:report, confirming the assertion is live and would fire if a scheduling diagnostic appeared on this path. This proves the assertion mechanism against real initializer output; it does not simulate an initializer that actually schedules the cleanup host.Whitespace:
git diff --check 140213288c07a1d96d48ecc9f810e23040b74ca9...HEADis clean. Diff is one file, 59 insertions and 13 deletions.Not verified
scripts/git/Initialize-CodexIssueWorktree.ps1.Risk notes
git worktree list --porcelainon every 100 ms poll iteration instead of once after the loop. In the normal case the loop exits on its first or second iteration, and the observed full-suite time (254.8 s) is in line with the suite's usual cost. The helper usesInvoke-Git, so a genuine Git failure during the wait throws the suite's standard fixture-git error rather than being silently absorbed.-RegistrationRepositoryPathis mandatory, so any future call site must name the repository whose registration list it means. The two existing call sites pass$callerPathand$separateGitCallerrespectively.Closes #2670. Refs #2664, PR #2669, #2425.