fix(deploy): stop the deploy lock opens from truncating the lock file - #9323
Conversation
Design Review (Fable 5) — ✅ PASSDesign-level review of Design-Verdict: PASS Real Windows mutual-exclusion defect, fixed at root with the exact pattern already merged in three sibling subsystems, pinned by RED/GREEN tests per site. [DESIGN-REVIEWED] 76e23eb |
GPT 5.6 Review — ✅ no blocking findingsGPT 5.6 completed its review of This comment is updated in place on each push. Review detailsNo findings. False positive or not applicable? A repository writer can comment: |
Opus 4.8 Review — ✅ no blocking findingsReviewed Verdict parsed from the review's SHA-scoped output markers for commit False positive or not applicable? A repository writer can comment: |
First Principles Review (Fable 5) — 🟡 CONCERNSPremise-level review of All evidence is in. The fix is derived from a real OS rule (msvcrt sharing violation), the test pins it, and nothing rides along — but two counted findings remain: the same defect pattern survives at five other sites ( First-Principles-Verdict: CONCERNS The fix is real and minimal, but it inlines a mechanism What this change shipsIntent: stop Windows deploy-lock contention from crashing (and the lock files from being emptied) by making the five deploy lock opens non-truncating — a FIX.
Watch
Subtractions
[FIRST-PRINCIPLES-REVIEWED] 76e23eb |
630a556 to
76e23eb
Compare
iamwhatever
left a comment
There was a problem hiding this comment.
Tier 1 auto-approve: fix (3 files). Criteria: no conflict, no requested changes, security path denylist clean, design-doc gate clean, SAST annotations clean, security checklist all-NO, AI reviewers green. Category: the deploy lock sidecars were opened with a truncating "w", which empties a lock file a prior holder already wrote and on Windows raises a sharing violation instead of waiting; fixed to touch + non-truncating "r+" at all five sites in pending.py/profiles.py, with a truncation-observable regression test.
Problem / Motivation
Five deploy lock sidecars are opened with
open(lock_path, "w"), which truncates the file at open time, BEFORE theplatform_compat.file_lockacquire on the handle. Three sites are insrc/kiro_crew/deploy/pending.py(add_pending,remove_pending,claim_pending) and two insrc/kiro_crew/deploy/profiles.py(locked_registry,save_registry).Why it matters
On Windows the acquire routes to
msvcrt.locking, and a truncating open of a lock file whose first byte another holder already locked raises a sharing violation instead of waiting: the contending acquirer crashes before it ever reachesfile_lock, and the mutual exclusion these sidecars exist to provide silently does not happen. These locks guard the pending-deploy store (double-deploy protection) and the profile registry (lost-write protection), so the failure mode is exactly the data race they were added to prevent. POSIXflocktolerates the truncate, which is why this is invisible on Linux.What changed (motivation -> approach -> change)
Applied the exact fix shape the sibling subsystems landed in
work_ledger._open_lock(#9237),session_pid.py(#9250), and the hooks.json pair (#9279): keep the existing parent-dirmkdir, addlock_path.touch(exist_ok=True), and open"r+"instead of"w"-- writable (whichmsvcrt.lockingrequires;"r"would trade this bug for a silently-unlocked critical section) but non-truncating. The acquire on the handle is unchanged, including the deploy stores'required=True. None of the five sites reads the lock file's contents, so the substitution discards nothing. Nothing in the repo unlinks these sidecars (both pre-push review lanes verified), so thetouch+"r+"pair introduces no new failure window.Tests
test/test_deploy_lock_truncate.py: the seed-bytes-survive property the sibling PRs pinned, once per public entry point (all five sites). Each test seeds the lock sidecar with bytes, drives the function that takes the lock, and asserts the bytes survive. Verified RED against the parent commit (all five fail: the file is emptied) and GREEN with the fix -- the truncation is the direct, platform-independent observable of the Windows defect.test_deploy_pending_replace_retry.py,test_deploy_profiles_cov80.py,test_deploy_web_profiles.py: 57 passed, 1 skipped).Manual verification
N/A -- unit coverage sufficient: the truncation property is directly observable in the tests on every platform, and the changed lines are exercised by every existing deploy-store test.
Related Issues
Closes #9265
Refs #9248 (the umbrella issue stays open until the last subsystem lands)
Note: open PR #9269 touches these same files as part of a multi-subsystem change; this PR carries only the deploy subsystem, per the one-subsystem-per-PR structure the #9248 sweep asks for, and matches the merged siblings' fix shape.
Pattern harvest
Rule candidate: semgrep
Pattern: truncating open(path, "w") whose handle flows into file_lock() before the acquire (Windows sharing violation); the #9248 sweep enumerates the instances by hand today.
Checklist
feat|fix|docs|refactor|perf|test|chore|ci|build|revert: ...)