FilesystemWorkerClient.execute() now rejects a plain write to an existing file when the caller doesn't supply expectedIdentity. Callers that go through FilesystemExecutor are fine — it captures the identity at lock acquisition and passes it down. Callers that construct the client directly are not, and scripts/verify-windows-sandbox-e2e.mjs is one of them, so the Release Windows check is currently failing on every PR.
Introduced by #3001 (ec1579bc).
Reproduction
scripts/verify-windows-sandbox-e2e.mjs:95-97:
const insidePath = join(workspace, 'inside.txt');
await writeFile(insidePath, 'seeded');
await execute({ kind: 'write', path: insidePath, content: 'packaged-relay-ok' }); // throws
FilesystemWorkerClientError: The target was created while this call waited for the lock; re-read before writing.
code: 'SANDBOX_FILESYSTEM_OPERATION_FAILED'
domain: 'filesystem'
reason: 'path_changed'
stage: 'validation'
at verifyWindowsSandboxWorkerE2E (scripts/verify-windows-sandbox-e2e.mjs:97:5)
There is no race here. The file is created by the line above, single-threaded and awaited. execute is client.execute({ operation, cwd, mode }) — no expectedIdentity.
Cause
packages/runtime/src/filesystem-worker/client.ts:209-219:
const identity =
input.expectedIdentity && target.targetType !== 'missing'
? input.expectedIdentity
: undefined;
if (!identity && target.targetType !== 'missing' && access === 'write') {
throw clientError('path_changed', 'validation', requestId, '...');
}
expectedIdentity is optional (client.ts:67) with no internal fallback — it appears only at lines 67, 194, 210 and 211. So the condition reduces to: any write to an existing target from a caller that didn't supply a T0 identity fails.
The reconciliation logic itself is right, and the comment at client.ts:194 correctly explains why the identity must not be re-derived after the lock is held. What's missing is the third case: a caller that has no T0 identity at all isn't a victim of a swap — it just isn't participating in the CAS. Today that's indistinguishable from "T0 missing, T1 existing".
Scope
- Not user-facing. Tool-driven writes reach the worker through
FilesystemExecutor, which captures identity via captureIdentityAtLockAcquisition before the lock wait (filesystem-executor.ts:270 for write/edit/format_json, :284 for apply_patch) and forwards it.
- Direct
FilesystemWorkerClient consumers are affected. The known one is the Windows sandbox e2e verifier; worth grepping for others before fixing.
- Ordinary CI is green, which is why this only surfaced on the Windows release job.
Suggested direction
Distinguish "caller supplied no identity" from "T0 saw nothing". Either make expectedIdentity required at the type level so every direct caller has to decide explicitly, or add an opt-out the verifier can pass to say it isn't participating in the CAS. The first is stricter and would have caught this at compile time.
Whoever picks this up: scripts/verify-windows-sandbox-e2e.mjs reproduces it directly, so the fix is verifiable without a full Windows packaging run.
Filed after this failure blocked #3472's Release Windows check. Reported by an AI-assisted review process; the reproduction and cause were verified against the source at ec1579bc.
FilesystemWorkerClient.execute()now rejects a plain write to an existing file when the caller doesn't supplyexpectedIdentity. Callers that go throughFilesystemExecutorare fine — it captures the identity at lock acquisition and passes it down. Callers that construct the client directly are not, andscripts/verify-windows-sandbox-e2e.mjsis one of them, so the Release Windows check is currently failing on every PR.Introduced by #3001 (
ec1579bc).Reproduction
scripts/verify-windows-sandbox-e2e.mjs:95-97:There is no race here. The file is created by the line above, single-threaded and awaited.
executeisclient.execute({ operation, cwd, mode })— noexpectedIdentity.Cause
packages/runtime/src/filesystem-worker/client.ts:209-219:expectedIdentityis optional (client.ts:67) with no internal fallback — it appears only at lines 67, 194, 210 and 211. So the condition reduces to: any write to an existing target from a caller that didn't supply a T0 identity fails.The reconciliation logic itself is right, and the comment at
client.ts:194correctly explains why the identity must not be re-derived after the lock is held. What's missing is the third case: a caller that has no T0 identity at all isn't a victim of a swap — it just isn't participating in the CAS. Today that's indistinguishable from "T0 missing, T1 existing".Scope
FilesystemExecutor, which captures identity viacaptureIdentityAtLockAcquisitionbefore the lock wait (filesystem-executor.ts:270for write/edit/format_json,:284for apply_patch) and forwards it.FilesystemWorkerClientconsumers are affected. The known one is the Windows sandbox e2e verifier; worth grepping for others before fixing.Suggested direction
Distinguish "caller supplied no identity" from "T0 saw nothing". Either make
expectedIdentityrequired at the type level so every direct caller has to decide explicitly, or add an opt-out the verifier can pass to say it isn't participating in the CAS. The first is stricter and would have caught this at compile time.Whoever picks this up:
scripts/verify-windows-sandbox-e2e.mjsreproduces it directly, so the fix is verifiable without a full Windows packaging run.Filed after this failure blocked #3472's Release Windows check. Reported by an AI-assisted review process; the reproduction and cause were verified against the source at
ec1579bc.