Skip to content

fix(deploy): stop the deploy lock opens from truncating the lock file - #9323

Merged
iamwhatever merged 1 commit into
mainfrom
fix/deploy-lock-truncate-9265
Sep 8, 2026
Merged

fix(deploy): stop the deploy lock opens from truncating the lock file#9323
iamwhatever merged 1 commit into
mainfrom
fix/deploy-lock-truncate-9265

Conversation

@chenmingwei23

@chenmingwei23 chenmingwei23 commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Problem / Motivation

Five deploy lock sidecars are opened with open(lock_path, "w"), which truncates the file at open time, BEFORE the platform_compat.file_lock acquire on the handle. Three sites are in src/kiro_crew/deploy/pending.py (add_pending, remove_pending, claim_pending) and two in src/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 reaches file_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. POSIX flock tolerates 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-dir mkdir, add lock_path.touch(exist_ok=True), and open "r+" instead of "w" -- writable (which msvcrt.locking requires; "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 the touch + "r+" pair introduces no new failure window.

Tests

  • New 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.
  • Existing deploy suites pass (test_deploy_pending_replace_retry.py, test_deploy_profiles_cov80.py, test_deploy_web_profiles.py: 57 passed, 1 skipped).
  • Local gates: isort, flake8, mypy, black, and the repo scan scripts all pass.

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

  • At most two commits (one is the norm), with a Conventional Commits title (feat|fix|docs|refactor|perf|test|chore|ci|build|revert: ...)
  • Existing tests pass and new tests added for new functionality
  • Self-review completed; code follows project style guidelines
  • Documentation updated (if applicable)
  • No secrets, credentials, or internal references in the diff

@chenmingwei23
chenmingwei23 requested a review from a team as a code owner September 7, 2026 23:19
@chenmingwei23
chenmingwei23 requested a review from Zedmor September 7, 2026 23:19
@github-actions github-actions Bot added readiness: checking Automated validation is still running readiness: action required A blocking check or review needs attention and removed readiness: checking Automated validation is still running labels Sep 7, 2026
@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Design Review (Fable 5) — ✅ PASS

Design-level review of 76e23eb53153956dee91b96486cc9332fa1139e1 — updated in place on each push. A BLOCK verdict blocks PR readiness; PASS/CONCERNS are advisory.

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

@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

GPT 5.6 Review — ✅ no blocking findings

GPT 5.6 completed its review of 76e23eb53153956dee91b96486cc9332fa1139e1 and found no blocking issues.

This comment is updated in place on each push.

Review details

No findings.
[GPT-REVIEWED] 76e23eb

False positive or not applicable? A repository writer can comment:
/ai-review override gpt 76e23eb53153956dee91b96486cc9332fa1139e1: <one-sentence reason>

@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Opus 4.8 Review — ✅ no blocking findings

Reviewed 76e23eb53153956dee91b96486cc9332fa1139e1 — this comment is updated in place on each push.

Review details

No findings.

[OPUS-REVIEWED] 76e23eb

Verdict parsed from the review's SHA-scoped output markers for commit 76e23eb53153956dee91b96486cc9332fa1139e1.

False positive or not applicable? A repository writer can comment:
/ai-review override fable 76e23eb53153956dee91b96486cc9332fa1139e1: <one-sentence reason>

@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

First Principles Review (Fable 5) — 🟡 CONCERNS

Premise-level review of 76e23eb53153956dee91b96486cc9332fa1139e1 — why this exists and whether the shipped surface is the smallest honest version. Updated in place on each push. A BLOCK verdict blocks PR readiness; PASS/CONCERNS are advisory.

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 (aws_control ×4, cron.py:4514), and work_ledger._open_lock already provides this exact mechanism as a context manager. Here is the review.

First-Principles-Verdict: CONCERNS

The fix is real and minimal, but it inlines a mechanism work_ledger._open_lock already provides, and five counted siblings of the same defect stay unfixed.

What this change ships

Intent: 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.

  1. Contending Windows acquirers of the pending-deploy lock now wait instead of crashing — justified
  2. Same for the profile-registry lock (locked_registry, save_registry) — justified
  3. Lock sidecar contents now survive every acquisition instead of being emptied — justified
  4. New test pins non-truncation once per public entry point (all five) — justified
  5. Sixth inline spelling of touch+"r+"+file_lock — duplicate of work_ledger._open_lock

Watch

  • The mechanism already exists: work_ledger._open_lock (work_ledger.py:532, 3 consumers) does exactly mkdir+touch+"r+"+file_lock, and none of the five deploy sites uses the fd for anything but fileno(). required=True is the only delta, and platform_compat.file_lock's docstring says it "no longer changes the outcome". The inline shape now has ~6 spellings (work_ledger, session_pid ×2, _McpFileLock, issue_radar, deploy) that will drift independently.
  • Counted unfixed siblings of the same root cause (open(lock_path, "w") fd handed to file_lock): apps/builtins/aws_control/backend/library.py:196, backup.py:177, shares.py:202, shares.py:222 — all required=True — plus cron.py:4514 (lock.open("w")try_acquire_lock, same msvcrt path). Deferral to Lock files opened truncating before the acquire: five sites still carry the Windows defect fixed in 9237 #9248 is declared and acceptable, but the description says the umbrella "enumerates the instances by hand" — confirm these five are on it.

Subtractions

  • Replace the five inline touch+"r+" blocks (and their three near-identical comment blocks) with one call each to the existing _open_lock shape from work_ledger.py:532 — behaviorally identical, since required is documented as intent-only.

[FIRST-PRINCIPLES-REVIEWED] 76e23eb

@github-actions github-actions Bot added readiness: checking Automated validation is still running and removed readiness: action required A blocking check or review needs attention labels Sep 7, 2026
@github-actions github-actions Bot added readiness: action required A blocking check or review needs attention and removed readiness: checking Automated validation is still running labels Sep 7, 2026
@chenmingwei23
chenmingwei23 force-pushed the fix/deploy-lock-truncate-9265 branch from 630a556 to 76e23eb Compare September 7, 2026 23:57
@github-actions github-actions Bot added readiness: checking Automated validation is still running and removed readiness: action required A blocking check or review needs attention labels Sep 7, 2026
@github-actions github-actions Bot added readiness: passed Eligible automated validation passed for the current revision and removed readiness: checking Automated validation is still running labels Sep 8, 2026
@iamwhatever
iamwhatever enabled auto-merge (squash) September 8, 2026 01:05

@iamwhatever iamwhatever left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@iamwhatever
iamwhatever merged commit 193d24e into main Sep 8, 2026
64 checks passed
@iamwhatever
iamwhatever deleted the fix/deploy-lock-truncate-9265 branch September 8, 2026 01:06
@github-actions github-actions Bot removed the readiness: passed Eligible automated validation passed for the current revision label Sep 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

deploy lock sidecars are opened truncating before the acquire (5 sites, #9248 sweep)

2 participants