Merge branch 'worktree-agent-ad3aff0dffd17a6eb'
Some checks failed
CI / Node 22 (push) Successful in 12m19s
CI / Node 24 (push) Has been cancelled
CI / Integration + conformance (Node 22) (push) Has been cancelled
CI / Bun (latest) (push) Has been cancelled

This commit is contained in:
David Snelling 2026-08-25 11:47:25 -07:00
commit f14da34b27
12 changed files with 726 additions and 80 deletions

View file

@ -239,38 +239,54 @@ export class FileSystemStorage extends BaseStorage {
// Finish any restore interrupted by a crash (resume the staged swap, or
// discard an uncommitted staging area) BEFORE counts/derived state load,
// so the rest of startup sees the completed store.
// so the rest of startup sees the completed store. ORDER-DEPENDENT:
// `swapStagedRestoreIn()` reads `fs.readdir(rootDir)` and then
// removes/renames rootDir's own TOP-LEVEL entries to place the staged
// copy — racing that against the directory-creation batch below (which
// also touches rootDir's children) could see a half-created directory
// mid-swap or a mkdir racing a concurrent rm/rename on the same path.
// Stays strictly sequential, never folded into the OPEN-PATH batch.
await this.completeInterruptedRestore()
// Create the nouns directory if it doesn't exist
await this.ensureDirectoryExists(this.nounsDir)
// OPEN-PATH FIX: the remaining bootstrap directories are mutually
// independent — each is its own subtree under rootDir, and
// `fs.mkdir(dir, { recursive: true })` creates every intermediate
// segment of ITS OWN path in one call, so it never depends on any
// sibling here existing first. Nothing between here and
// `initializeCounts()` reads any of them, so batching collapses what
// was up to 8 sequential mkdir round-trips (each a real syscall+await)
// into one wave — this is what serialized an N-writer restart storm on
// filesystem I/O it never structurally needed. `initializeCounts()`
// right after DOES depend on `systemDir` (which the batch creates), so
// it stays outside, awaited only once every directory has landed.
await Promise.all([
// Create the nouns directory if it doesn't exist
this.ensureDirectoryExists(this.nounsDir),
// Create the verbs directory if it doesn't exist
this.ensureDirectoryExists(this.verbsDir),
// Create the metadata directory if it doesn't exist
this.ensureDirectoryExists(this.metadataDir),
// Create the noun metadata directory if it doesn't exist
this.ensureDirectoryExists(this.nounMetadataDir),
// Create the verb metadata directory if it doesn't exist
this.ensureDirectoryExists(this.verbMetadataDir),
// Create both directories for backward compatibility
this.ensureDirectoryExists(this.systemDir),
// Only create legacy directory if it exists (don't create new legacy
// dirs) — a read-then-maybe-write, but on its own subtree, so it's
// still independent of every other entry in this batch.
(async () => {
if (await this.directoryExists(this.indexDir)) {
await this.ensureDirectoryExists(this.indexDir)
}
})(),
// Create the locks directory if it doesn't exist
this.ensureDirectoryExists(this.lockDir),
// Create the binary blobs directory if it doesn't exist
this.ensureDirectoryExists(this.blobsDir)
])
// Create the verbs directory if it doesn't exist
await this.ensureDirectoryExists(this.verbsDir)
// Create the metadata directory if it doesn't exist
await this.ensureDirectoryExists(this.metadataDir)
// Create the noun metadata directory if it doesn't exist
await this.ensureDirectoryExists(this.nounMetadataDir)
// Create the verb metadata directory if it doesn't exist
await this.ensureDirectoryExists(this.verbMetadataDir)
// Create both directories for backward compatibility
await this.ensureDirectoryExists(this.systemDir)
// Only create legacy directory if it exists (don't create new legacy dirs)
if (await this.directoryExists(this.indexDir)) {
await this.ensureDirectoryExists(this.indexDir)
}
// Create the locks directory if it doesn't exist
await this.ensureDirectoryExists(this.lockDir)
// Create the binary blobs directory if it doesn't exist
await this.ensureDirectoryExists(this.blobsDir)
// Initialize count management
// Initialize count management — depends on systemDir, created above.
this.countsFilePath = path.join(this.systemDir, 'counts.json')
await this.initializeCounts()