From c4fb1fe5f1d46dd4ce1ec78a04463700a9117ebb Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 5 Nov 2026 13:00:42 +0000 Subject: [PATCH] fix(cloud-connection): stage the ledger adoption copy without mkdtempSync, so main's scratch scan reads green again MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The #30217 adoption step created its staging directory with mkdtempSync under dirname(this.dir), a base resolved at run time. The dispatch-gates self-test holds every mkdtempSync site to a base its source scan can read, so the `Lint · PM dispatch-gates` lane has been red on main since #30351. The staging directory is now an exclusive, non-recursive mkdirSync of a randomly named sibling: still unique, still renamed into place, still removed on failure. Claude-Session: https://claude.ai/code/session_01Utp6CZ9g1bEV7DPMneGKAx Co-authored-by: Claude --- .changeset/30467-ledger-adoption-staging-dir.md | 8 ++++++++ .../cloud-connection/src/local-manifest-source.ts | 13 ++++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) create mode 100644 .changeset/30467-ledger-adoption-staging-dir.md diff --git a/.changeset/30467-ledger-adoption-staging-dir.md b/.changeset/30467-ledger-adoption-staging-dir.md new file mode 100644 index 00000000000..c0ab0f30047 --- /dev/null +++ b/.changeset/30467-ledger-adoption-staging-dir.md @@ -0,0 +1,8 @@ +--- +'@objectstack/cloud-connection': patch +--- + +Clause-②: no +Metadata-protocol: none + +The one-time copy of the working-directory install ledger into its `OS_HOME` location (#30217) now creates its staging directory with an exclusive `mkdirSync` of a randomly named sibling instead of `mkdtempSync`. Behaviour is unchanged: the copy still lands in a unique sibling of the ledger directory, is renamed into place, and is removed if the copy fails. This repairs the red `main` build (#30467), where the repository's in-tree scratch scan could not read the `mkdtempSync` base. diff --git a/packages/cloud-connection/src/local-manifest-source.ts b/packages/cloud-connection/src/local-manifest-source.ts index 6cd1bd09566..bf4f8b466a5 100644 --- a/packages/cloud-connection/src/local-manifest-source.ts +++ b/packages/cloud-connection/src/local-manifest-source.ts @@ -30,9 +30,9 @@ * the same ledger without going through HTTP. */ +import { randomBytes } from 'node:crypto'; import { - copyFileSync, existsSync, mkdirSync, mkdtempSync, readFileSync, readdirSync, renameSync, rmSync, unlinkSync, - writeFileSync, + copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, unlinkSync, writeFileSync, } from 'node:fs'; import { homedir } from 'node:os'; import { basename, dirname, join, resolve } from 'node:path'; @@ -330,7 +330,14 @@ export class LocalManifestSource { const files = readdirSync(from).filter((name) => name.endsWith('.json')); if (files.length === 0) return null; mkdirSync(dirname(this.dir), { recursive: true }); - const staging = mkdtempSync(join(dirname(this.dir), `.${basename(this.dir)}.adopting-`)); + // A unique sibling, created exclusively: a non-recursive mkdirSync + // throws EEXIST rather than reuse a directory that is already there, + // which is the guarantee mkdtempSync gave. mkdtempSync is not used + // because the repo's in-tree scratch scan holds every mkdtempSync to a + // base it can read from source, and this base is the ledger location + // resolved at run time. + const staging = join(dirname(this.dir), `.${basename(this.dir)}.adopting-${randomBytes(6).toString('hex')}`); + mkdirSync(staging); try { for (const name of files) copyFileSync(join(from, name), join(staging, name)); renameSync(staging, this.dir);