You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A failed folder create frees the global lock, and a tracked-file keep refusal says why
Small fixes from CodeRabbit's review of the v1.12.3 range (thread t114) and from a flaky test teardown. Nothing a user could do before is taken away, and nothing new is exposed. The installed plugin moved: scripts/lib/apply.mjs, scripts/lib/keeps.mjs, scripts/lib/repo-fs.mjs (a comment only), skills/coalwash/references/method.md and their plugin/ twins.
Fixed
The global lock is released when the transaction folder cannot be created.applyPlan took the global lock, then ran mkdirSync on .claude/coalwash; a throw there (for example EACCES) left the lock held until its 30-minute stale window, and every other run deferred meanwhile. The create is now inside a try: on a throw the lock is released and the run returns { ok: false, error: 'apply: cannot create the transaction folder (<code>)' }. — test: scripts/lib/apply.test.mjs, global-scope lock: a transaction folder that cannot be created refuses the run AND releases the global lock
A bin-stash failure no longer blames the lock for every cause.recordBinItem returns null for several causes (its per-bin lock was busy, the bin's catch-all .gitignore, the bin folder, or the item or index write failed), but the flag applyPlan raised said only that the lock could not be acquired. It now reads "recordBinItem could not record it (its per-bin lock was busy, or the catch-all .gitignore of the bin, the bin folder, or the item or index write failed)". The recoverDangling partial-recovery message likewise says "refused as out-of-root or as a create whose copy could not be binned": its count (refusedOutOfRoot) also counts a create that could not be binned, and the field name is unchanged because a test pins that count. — test: scripts/lib/apply.test.mjs, fire 63: a bin-stash failure that is NOT the lock is flagged without blaming the lock
Contributor-facing: CodeQL alerts CoalWash v1.12.2 - reads in CoalWash's working folders refuse links that leave them #53 to CoalFace v0.14.3 - a planted link can no longer redirect the project config #56 (js/file-system-race, self-ignore.test.mjs). The test read a file's inode by path and then wrote and read the same path in a loop, which the analyzer reads as a check followed by a use. The inode is now read through an open file descriptor. A single-process fixture had no second actor; the alert was a shape, not a race. The two junction tests also probe whether the volume can make a junction and skip visibly when it cannot, and the old dual-outcome directory-link test is split into two tests that each decide their branch by probing whether a rename over a directory link is refused. — test: scripts/lib/self-ignore.test.mjs
Contributor-facing: the EBUSY teardown flake on the Windows CI leg (PR ci: bump github/codeql-action/upload-sarif from 4.38.1 to 4.38.2 in the codeql-action group #22, job 116559157309).apply.test.mjs's ENOBUFS fail-closed test (R14 D3 bounce 2) removed its temp project while a git process still held it. Git for Windows runs a launcher, cmd\git.exe, which starts the real git.exe; when spawnSync kills the launcher on ENOBUFS, the real process lives on for 2.9 to 7.6 s (n = 6, measured on one box) with its working folder inside the project, and Windows will not remove a held folder. The cure is in the teardown: removeTree in scripts/lib/test-plan.mjs retries a removal that fails with EBUSY, EPERM or ENOTEMPTY with fresh calls for up to 30 s and throws after that, so a real leak stays loud. The 32 teardown removals in seven test files go through it. The shipped engine is unchanged: its one git spawn (git --no-optional-locks ... ls-files -z) was measured to write nothing in the project, 3 of 3. One residual remains and is named: on Windows, after an ENOBUFS or timeout kill of the Git for Windows launcher, that read-only git ls-files can hold the user's folder busy for a few seconds (2.9 to 7.6 s, n = 6), and it writes nothing. — test: scripts/lib/test-plan.test.mjs, removeTree removes a temp project while a child process still holds it as its cwd
Changed
recordKeep now returns the reason it refused, in an error field. The result keeps its status fields (ok, anchorDropped, anchorStored) unchanged and gains error on a failed write; a refusal reads tracked: ... or self-ignore: ... (the same prefixes applyPlan uses), and the sandbox-folder refusal carries one too. The early guard returns (no target, a newer schema) are unchanged. The field is additive and no shipped code calls recordKeep: its callers are tests. The v1.12.3 entry and the SECURITY.md record name these refusals; until v1.12.4 the caller of recordKeep could not read the reason. — test: scripts/lib/self-ignore.test.mjs, the two L58-1 keeps legs
references/method.md §3, item 2 (and its dist twin) says what the catch path returns. It said the early guards and the catch path share the shape { ok: false, anchorDropped: false, anchorStored: false }; the catch path now also carries error.
Forward note on the [1.12.3] entry (CodeRabbit t114). That entry and the SECURITY.md record cite apply.mjs:786 for the planted-.gitignore flaw. That is the line at v1.12.2, where PR ci: bump the npm_and_yarn group across 4 directories with 5 updates #18's thread was filed (inside acquireLock's stale-takeover branch by v1.12.3); the function is ensureSelfIgnore, which delegates to ensureCatchAllIgnore in repo-fs.mjs. The released entry is not edited.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
A failed folder create frees the global lock, and a tracked-file keep refusal says why
Small fixes from CodeRabbit's review of the
v1.12.3range (threadt114) and from a flaky test teardown. Nothing a user could do before is taken away, and nothing new is exposed. The installed plugin moved:scripts/lib/apply.mjs,scripts/lib/keeps.mjs,scripts/lib/repo-fs.mjs(a comment only),skills/coalwash/references/method.mdand theirplugin/twins.Fixed
applyPlantook the global lock, then ranmkdirSyncon.claude/coalwash; a throw there (for exampleEACCES) left the lock held until its 30-minute stale window, and every other run deferred meanwhile. The create is now inside atry: on a throw the lock is released and the run returns{ ok: false, error: 'apply: cannot create the transaction folder (<code>)' }. — test:scripts/lib/apply.test.mjs,global-scope lock: a transaction folder that cannot be created refuses the run AND releases the global lockrecordBinItemreturnsnullfor several causes (its per-bin lock was busy, the bin's catch-all.gitignore, the bin folder, or the item or index write failed), but the flagapplyPlanraised said only that the lock could not be acquired. It now reads "recordBinItem could not record it (its per-bin lock was busy, or the catch-all .gitignore of the bin, the bin folder, or the item or index write failed)". TherecoverDanglingpartial-recovery message likewise says "refused as out-of-root or as a create whose copy could not be binned": its count (refusedOutOfRoot) also counts a create that could not be binned, and the field name is unchanged because a test pins that count. — test:scripts/lib/apply.test.mjs,fire 63: a bin-stash failure that is NOT the lock is flagged without blaming the lockjs/file-system-race,self-ignore.test.mjs). The test read a file's inode by path and then wrote and read the same path in a loop, which the analyzer reads as a check followed by a use. The inode is now read through an open file descriptor. A single-process fixture had no second actor; the alert was a shape, not a race. The two junction tests also probe whether the volume can make a junction and skip visibly when it cannot, and the old dual-outcome directory-link test is split into two tests that each decide their branch by probing whether a rename over a directory link is refused. — test:scripts/lib/self-ignore.test.mjsEBUSYteardown flake on the Windows CI leg (PR ci: bump github/codeql-action/upload-sarif from 4.38.1 to 4.38.2 in the codeql-action group #22, job 116559157309).apply.test.mjs's ENOBUFS fail-closed test (R14 D3 bounce 2) removed its temp project while a git process still held it. Git for Windows runs a launcher,cmd\git.exe, which starts the realgit.exe; whenspawnSynckills the launcher on ENOBUFS, the real process lives on for 2.9 to 7.6 s (n = 6, measured on one box) with its working folder inside the project, and Windows will not remove a held folder. The cure is in the teardown:removeTreeinscripts/lib/test-plan.mjsretries a removal that fails withEBUSY,EPERMorENOTEMPTYwith fresh calls for up to 30 s and throws after that, so a real leak stays loud. The 32 teardown removals in seven test files go through it. The shipped engine is unchanged: its one git spawn (git --no-optional-locks ... ls-files -z) was measured to write nothing in the project, 3 of 3. One residual remains and is named: on Windows, after an ENOBUFS or timeout kill of the Git for Windows launcher, that read-onlygit ls-filescan hold the user's folder busy for a few seconds (2.9 to 7.6 s, n = 6), and it writes nothing. — test:scripts/lib/test-plan.test.mjs,removeTree removes a temp project while a child process still holds it as its cwdChanged
recordKeepnow returns the reason it refused, in anerrorfield. The result keeps its status fields (ok,anchorDropped,anchorStored) unchanged and gainserroron a failed write; a refusal readstracked: ...orself-ignore: ...(the same prefixesapplyPlanuses), and the sandbox-folder refusal carries one too. The early guard returns (no target, a newer schema) are unchanged. The field is additive and no shipped code callsrecordKeep: its callers are tests. Thev1.12.3entry and theSECURITY.mdrecord name these refusals; untilv1.12.4the caller ofrecordKeepcould not read the reason. — test:scripts/lib/self-ignore.test.mjs, the twoL58-1keeps legsreferences/method.md§3, item 2 (and its dist twin) says what the catch path returns. It said the early guards and the catch path share the shape{ ok: false, anchorDropped: false, anchorStored: false }; the catch path now also carrieserror.[1.12.3]entry (CodeRabbitt114). That entry and theSECURITY.mdrecord citeapply.mjs:786for the planted-.gitignoreflaw. That is the line atv1.12.2, where PR ci: bump the npm_and_yarn group across 4 directories with 5 updates #18's thread was filed (insideacquireLock's stale-takeover branch byv1.12.3); the function isensureSelfIgnore, which delegates toensureCatchAllIgnoreinrepo-fs.mjs. The released entry is not edited.Release page · CoalWash
All reactions