fix(core): CAS write gateway + per-path advisory locks for TOCTOU safety - #68
Open
bigknoxy wants to merge 1 commit into
Open
fix(core): CAS write gateway + per-path advisory locks for TOCTOU safety#68bigknoxy wants to merge 1 commit into
bigknoxy wants to merge 1 commit into
Conversation
Closes #21 Problem: Every tier (AST, hash, diff) does read→transform→write with no re-validation between the read and the write. Concurrent edits silently lose work (last-write-wins). The hash tier's stale-anchor check defaulted to auto-recovery instead of aborting. Solution: 1. CAS (compare-and-swap) write gateway in router.ts: - routeRead() returns file hash for CAS gating - casWrite() re-reads file hash immediately before write, aborts with STALE_ANCHOR if stale, returns fresh hash for retry - Applies to all three tiers (AST, hash, diff) 2. Per-path advisory lock (src/core/lock.ts): - Atomic file creation (.hashpilot/locks/<sha256>.lock) - PID-liveness stale lock breaking - Sorted lock acquisition order prevents deadlock - LOCK_TIMEOUT error code with bounded wait 3. Hash tier noRecovery when oldHash provided: - replaceHash() called with noRecovery: true when oldHash is set - Dry-run bypasses stale check for simulation 4. editMany conflict distinction: - BatchSummary.conflicts counts stale-anchor failures separately - editManySerial also tracks conflicts Tests: 23 new toctou tests covering: - Stale hash detection across all 3 tiers - CAS success with correct hash - Backward compat (no oldHash) - Dry-run bypass - LOCK_TIMEOUT error code - Per-path advisory lock acquire/release - Stale lock breaking (dead PID) - Deadlock prevention (sorted lock ordering) - editMany conflict count Sabotage run: reverting CAS changes → tests fail, confirming they catch the absence of the fix.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Closes #21
Every tier (AST, hash, diff) does read→transform→write with no re-validation between the read and the write. Concurrent edits silently lose work (last-write-wins). The hash tier's stale-anchor check defaults to auto-recovery instead of aborting.
Approach
1. CAS (Compare-And-Swap) Write Gateway
routeRead()returns file content + hash for CAS gatingcasWrite()re-reads file hash immediately before write, aborts withSTALE_ANCHORif stale, returns fresh hash for retry2. Per-Path Advisory Lock (
src/core/lock.ts).hashpilot/locks/<sha256>.lock)LOCK_TIMEOUTerror code with bounded wait3. Hash Tier noRecovery
replaceHash()called withnoRecovery: truewhenoldHashis set4. editMany Conflict Distinction
BatchSummary.conflictscounts stale-anchor failures separately from logic failuresTests (23 new)
Sabotage run verified: reverting CAS changes → tests fail, confirming they catch the absence of the fix.
Risk
oldHashnow fails instead of auto-recovering. Callers relying on auto-recovery need to handleSTALE_ANCHORand retry.routeEditwithoutoldHashstill works (CAS is opt-in via the hash parameter).Exclusions