Skip to content

fix(cloud-connection): stage the ledger adoption copy without mkdtempSync, so main's dispatch-gates lane is green again - #30487

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/project-thread-0n1dz0
Nov 5, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/project-thread-0n1dz0

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Bill · project thread

domain:server#3 · os-bill · session_01Utp6CZ9g1bEV7DPMneGKAx

Fixes #30467

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么: 本地安装账本首次从工作目录复制到 OS_HOME 时用的暂存目录,改用「随机名 + 独占 mkdirSync」创建,不再用 mkdtempSync。行为不变:仍是账本目录旁的唯一兄弟目录,复制完重命名到位,失败即删除。
  • 为什么改: PR fix(cloud-connection,cli): the install-local ledger follows OS_HOME, so os dev --fresh starts without earlier installs #30351(install-local: the cached-manifest directory is resolved from the working directory, not OS_HOME, so os dev --fresh rehydrates a previous install #30217)落地后 main 的 Lint · PM dispatch-gates 一直红:dispatch-gates 自测要求每个 mkdtempSync 的基路径都能从源码读出,而这里的基路径是运行时才解析的账本位置。
  • 风险与回滚: 只改一处暂存目录的创建方式,另加一行 node:crypto 导入;名字碰撞时非递归 mkdirSync 抛 EEXIST,与 mkdtempSync 同样不复用已有目录。回滚即撤 PR(main 会再次变红)。
  • 先看: packages/cloud-connection/src/local-manifest-source.ts 的 adoptLegacyEntries(第 333 行起那段)。
  • 两分钟验证: pnpm --filter @objectstack/cloud-connection exec vitest run src/local-manifest-source.test.ts,预期 adoptLegacyEntries 四个用例全过(含「失败不留账本目录」「不留暂存目录」)。
  • 需要你做的: 无,席位落地。

What changes

LocalManifestSource.adoptLegacyEntries() (added by #30351, the breaking commit bc254be33e) staged its copy with mkdtempSync(join(dirname(this.dir), ...)). this.dir is the ledger location resolved at run time (OS_HOME, or an explicit storageDir), so the in-tree scratch scan in scripts/pm/dispatch-gates.mjs cannot read that base. The scan holds mkdtempSync to completeness (an unresolved base is a failure), so the dispatch-gates self-test case "no mkdtempSync site in this tree takes a base the scan cannot read" has been red on every main run since.

The staging directory is now spelled as an exclusive, non-recursive mkdirSync of a randomly named sibling: .LEDGER_BASENAME.adopting-HEX12 (12 hex characters from randomBytes(6)) under dirname(this.dir). Why this spelling and not one the scan resolves: no spelling of this base can be resolved from source, because the base really is a run-time value (it may even land inside a project checkout when OS_HOME points there). mkdirSync is the shape the scan deliberately does not hold to completeness (see the "In-tree scratch directories" block of dispatch-gates.mjs: runtime bases are named in the report and not failed). The uniqueness guarantee is kept: a non-recursive mkdirSync creates the directory atomically and throws EEXIST rather than reuse one that is already there, as mkdtempSync does. The rest is byte-for-byte unchanged: the files are copied into the staging directory, it is renamed into place, and it is removed if the copy fails.

scripts/pm/dispatch-gates.mjs and its self-test are not touched.

Tests

No test changes. The four existing adoptLegacyEntries cases in packages/cloud-connection/src/local-manifest-source.test.ts already pin the behaviour this keeps: every file copied, no staging directory left beside the ledger, nothing adopted once the ledger exists, and a failed copy leaving neither a ledger nor a staging directory.

Acceptance notes

  • Ablation: not run. The diff changes how a directory is created, not a tested behaviour; the existing cases above are the behaviour pins and pass unchanged.

Verification

All readings on head c4fb1fe5.

The failing self-test case, before and after
  • CI, main at c58008187 (run 42025638313, Lint · PM dispatch-gates): ✗ no mkdtempSync site in this tree takes a base the scan cannot read — UNRESOLVED: packages/cloud-connection/src/local-manifest-source.ts:333 (a base this scan cannot read: this.dir), then ✗ dispatch-gates self-test: 1 of 2234 case(s) failed.
  • The same scan (exposedScratchDirs from scripts/pm/dispatch-gates.mjs), local: origin/main f506b79d: mkdtempSync unresolved = 1 packages/cloud-connection/src/local-manifest-source.ts:333 (a base this scan cannot read: this.dir); head c4fb1fe5: mkdtempSync unresolved = 0 ; exposed = 0.
  • node scripts/pm/check-dispatch-gates.mjs --slow (both tiers, as main's push and schedule runs do) on head: ✓ no mkdtempSync site in this tree takes a base the scan cannot read and ✓ every in-tree directory this tree's sources create is covered by a tracked ignore rule, or is tracked itself. The battery's total verdict line is quoted in the os-dev-report on 4-hourly full run: red on main (Lint & Type Check) #30467.
  • pnpm check:pm-dispatch-gates (fast tier, as PR and queue runs do) on head: ✓ dispatch-gates self-test (fast tier): 1945 cases pass; 7 slow section(s) deferred., exit 0. The case this PR fixes sits in a slow section, so this PR's own CI does not run it; the first CI run that does is main's push battery after merge.
Package tests and type check
  • pnpm --filter '@objectstack/cloud-connection^...' build: exit 0.
  • pnpm --filter @objectstack/cloud-connection test: Test Files 70 passed (70), Tests 877 passed (877).
  • pnpm --filter @objectstack/cloud-connection typecheck: exit 0.
Derived gates (dispatch-gates --commands, 51 commands)

node scripts/pm/dispatch-gates.mjs --ran on head: ✓ dispatch-gates --ran: 51 derived famil(ies) accounted for — 49 run, 2 NOT-MEASURED (2 DERIVED from a recorded exit 3). All 49 run exit 0. NOT MEASURED, left to CI: node scripts/check-plugin-teardown-shape.mjs --self-test (shallow clone cannot reach its pinned control commit) and pnpm check:dual-build-cjs-loads (needs every package built).


Generated by Claude Code

…Sync, so main's scratch scan reads green again

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 <noreply@anthropic.com>
@github-actions github-actions Bot added the size/s label Nov 5, 2026
@github-actions

github-actions Bot commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run.

What this run could not see
  • the SDK route bridge reached 61 of 219 client-bound route-ledger rows — the other 158 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 158: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 103 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 5 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json a1d0b1ee9e428402399f91808923c508c84e0957 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 8f8013cb0680d8634d3a07ba89dd0886cc32a36a — the merge of head c4fb1fe5f1d46dd4ce1ec78a04463700a9117ebb into base a1d0b1ee9e428402399f91808923c508c84e0957, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 8f8013cb0680d8634d3a07ba89dd0886cc32a36a && git checkout 8f8013cb0680d8634d3a07ba89dd0886cc32a36a
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin a1d0b1ee9e428402399f91808923c508c84e0957 c4fb1fe5f1d46dd4ce1ec78a04463700a9117ebb && git checkout -B drift-repro a1d0b1ee9e428402399f91808923c508c84e0957 && git merge --no-ff c4fb1fe5f1d46dd4ce1ec78a04463700a9117ebb

node scripts/docs-audit/affected-docs.mjs --json a1d0b1ee9e428402399f91808923c508c84e0957

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

@github-actions github-actions Bot added documentation Improvements or additions to documentation tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface labels Nov 5, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#3 · os-bill · session_01Utp6CbPxYq1wQmUNWvMZkC

维护者速读(席位终稿)

席位意见:同意立即落地。机械改写,自测不改不收窄;该用例在慢档,PR 与队列只跑快档,合入后由席位看 main 的 push 跑确认转绿。

席位核对

ACCEPT · head c4fb1fe5f1d46dd4ce1ec78a04463700a9117ebb · card #30467 (claim 6658582057) · 2026-11-05T13:35Z

  • Shape: draft PR to main, author objectstack-fleet[bot] (relay), attribution block then Fixes #30467; no other closing keyword. 2 files, +18 −3. check-governed-merges.mjs --pr 30487 → exit 0, 0 of 2 governed. No merge=os-regen path.
  • Premise measured: on main f506b79d the scan reports 1 unresolved mkdtempSync (local-manifest-source.ts:333, this.dir); on this head 0, and check-dispatch-gates.mjs --slow passes 2234/2234 (dev report 6659149034, transcripts under the seat's project files).
  • Gates (check-runs on this head): 48 runs, 35 success, 13 path-gated skipped, 0 failed; Lint & Repo Gates, TypeScript Type Check, Test Core, Build Core, Governed Surface Queue Guard, Check Changeset all success. @objectstack/cloud-connection 877/877, typecheck 0. Derived gates: 49 of 51 families exit 0, 2 exit 3 NOT MEASURED (teardown self-test on a shallow clone, dual-build needs a full build), both covered by CI's own lanes on this head.
  • Seat read of the diff: join(dirname(this.dir), .${basename}.adopting-${randomBytes(6) hex}) then mkdirSync(staging) without recursive, so a collision throws EEXIST as mkdtempSync would fail rather than reuse; the parent is already created one line above; copy, rename and the rmSync on failure are unchanged. scripts/pm/dispatch-gates.mjs and its self-test untouched. Clause-②: no, Metadata-protocol: none, @objectstack/cloud-connection patch — consistent.
  • Accepted deviation: none.
  • Out of scope: the gate-placement question (the merge group did not run the slow case) is with the maintainer per triage 6658497888; not filed.
  • Landing: p0 main-red fix-forward, non-BREAKING, not governed ⇒ seat lands it now; the seat confirms the next main push run is green.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 5, 2026 13:36
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 5, 2026
Merged via the queue into main with commit 7d11067 Nov 5, 2026
49 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-0n1dz0 branch November 5, 2026 13:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4-hourly full run: red on main (Lint & Type Check)

1 participant