Skip to content

fix(cli): os diff says a replay inserts an insert seed dataset again, as it does for replace - #31192

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/project-thread-64ua3t
Nov 7, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/project-thread-64ua3t

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by 田騏 · project thread

Fixes #31139

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么: 只加一句提示。os diff 的升级计划对 mode: 'insert' 的种子数据集,现在和 replace 一样说明重放会怎样:"a replay inserts these again, and a row the database refuses is that row's error"。
  • 为什么改: 加载器里 insert 和 replace 走的是同一条路:不查匹配,每次重放都把记录再插一遍。上一个 PR 只给 replace 加了提示,insert 数据集在计划里看起来像无害,同一种行为被说成两样。
  • 风险与回滚: 无行为变化,只动一条 CLI 提示字符串、它的测试和一个 changeset。回滚即 revert。
  • 先看: packages/cli/src/commands/diff.ts 的 seedModeNote(新增的 insert 一行);packages/cli/test/diff-upgrade-plan.test.ts 新增的用例。
  • 两分钟验证: pnpm --filter @objectstack/cli exec vitest run --project unit test/diff-upgrade-plan.test.ts,预期 8 个测试全过,新用例里 insert 数据集新增和修改两行都带这句提示,ignore 那行不带。
  • 需要你做的: 无,席位落地。

What changes

Text only; no behaviour change.

  • packages/cli/src/commands/diff.ts seedModeNote: a dataset in mode insert now reads "; a replay inserts these again, and a row the database refuses is that row's error". The replace and upsert/update lines are unchanged.
  • Worded from the loader on main, not from the card: SeedLoaderService.writeRecord case 'insert' calls engine.insert without looking at the existing-row match, and decideWriteAction answers insert for insert, replace and the default alike (packages/metadata-protocol/src/seed-loader.ts). A refused row goes to the load's errors while the rest land, the same fact the SeedMode comment in seed.zod.ts states since docs(spec,cli): seed replace deletes nothing, and the AppPlugin loads skip a landed row a person deleted #31135.
  • packages/cli/test/diff-upgrade-plan.test.ts: one new case. An insert dataset added and an insert dataset changed both carry the note; an ignore dataset changed in the same plan stays silent (the control; an absent mode defaults to ignore, which the existing case already pins silent).
  • .changeset/cli-diff-insert-seed-note.md: @objectstack/cli patch.

Tests

  • pnpm --filter @objectstack/cli exec vitest run --project unit test/diff-upgrade-plan.test.ts: 1 file, 8 tests passed.
  • Ablation (the source is imported from src, so no build leg): scripts/ablation-replace.mjs deleted the new insert line (anchor 1 → 0, blob b02482c0 → 5a8ab9b9); the new case went red ("Tests 1 failed | 7 passed (8)"), the other seven stayed green. Restored: blob == HEAD and git diff HEAD empty.
  • packages/cli unit layer, whole (vitest run --project unit): 396 files passed; the 2 other files (published-subpath-console.pin, published-subpath-hook-body.pin) refused because packages/cli/dist was not built yet. After pnpm --filter @objectstack/cli build both passed (2 files, 29 tests). The integration layer is declared to CI: the diff touches no integration file or spawn entry.
  • pnpm --filter @objectstack/cli typecheck: exit 0.

Acceptance notes

  • No Fable review: the diff touches no spec, skills or ADR (rule of 2026-11-07 16:07Z).
  • Merged origin/main once before opening (1f5316d); the incoming commits touch no packages/cli file.

Verification

Derived gates (dispatch-gates --commands) — 62 families, all exit 0 on 1f5316d
  • node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran ran.txt :: exit 0 — "62 derived famil(ies) accounted for — 62 run, 0 NOT-MEASURED".
  • First pass: 3 families exited 3 (PREREQUISITE NOT MET): check-plugin-teardown-shape --self-test (shallow clone), check:dual-build-cjs-loads and check:i18n-coverage (workspace packages not built). After git fetch --unshallow origin main and a full turbo run build, all 3 reran with exit 0; the other 59 exited 0 on the first pass.
  • eslint on diff.ts and diff-upgrade-plan.test.ts :: exit 0 (repo-wide pnpm lint is CI's).

🤖 Generated with Claude Code

https://claude.ai/code/session_017ihL2bzGzPEiCJX3i1fYRj

… on replay

seedModeNote explained the replace arm and the upsert/update arm but
said nothing for insert, whose loader arm (SeedLoaderService.writeRecord
and decideWriteAction) inserts every record with no match, exactly like
replace. The plan now says so, and the diff test pins the note on an
insert dataset added and changed, with an ignore dataset as the silent
control.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ihL2bzGzPEiCJX3i1fYRj
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation tests tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface labels Nov 7, 2026
@github-actions

github-actions Bot commented Nov 7, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/cli, touching 2 documentable anchor(s).

3 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/kernel/services-checklist.mdx (via os diff (command, read off packages/cli/src/commands/diff.ts))
  • content/docs/protocol/backward-compatibility.mdx (via os diff (command, read off packages/cli/src/commands/diff.ts))
  • content/docs/protocol/objectql/schema.mdx (via os diff (command, read off packages/cli/src/commands/diff.ts))
What this run could not see
  • the SDK route bridge reached 63 of 226 client-bound route-ledger rows — the other 163 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 163: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 59 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 104 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 — 35 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 8acbfab9b4d9c0f6df84d4f350b364fe5eb8d4fc → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 28ae5a96cbb1fefd42a760f108c42ec390f61946 — the merge of head 1f5316d18a534870666a2709c58ee5d7e6876cd2 into base 8acbfab9b4d9c0f6df84d4f350b364fe5eb8d4fc, 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 28ae5a96cbb1fefd42a760f108c42ec390f61946 && git checkout 28ae5a96cbb1fefd42a760f108c42ec390f61946
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 8acbfab9b4d9c0f6df84d4f350b364fe5eb8d4fc 1f5316d18a534870666a2709c58ee5d7e6876cd2 && git checkout -B drift-repro 8acbfab9b4d9c0f6df84d4f350b364fe5eb8d4fc && git merge --no-ff 1f5316d18a534870666a2709c58ee5d7e6876cd2

node scripts/docs-audit/affected-docs.mjs --json 8acbfab9b4d9c0f6df84d4f350b364fe5eb8d4fc

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

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 8acbfab9b4d9c0f6df84d4f350b364fe5eb8d4fc → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#4 · marchtian · session_017ihL2dLQEXWGsE2SZ3Qn5F

席位核对 — ACCEPT · domain:server#4 · Head-sha: 1f5316d18a534870666a2709c58ee5d7e6876cd2

  • Shape: draft → main, attribution lines first, Fixes #31139 once, Clause-②: no and Metadata-protocol: none line-initial in body and changeset (@objectstack/cli patch). 3 files, +33, packages/cli/** + one .changeset; not governed, under 3000 lines; local git merge-tree against current origin/main clean.
  • Diff read by the seat: seedModeNote gains one insert arm after the replace arm; the overwrite (upsert/update) and replace lines are byte-unchanged. The wording matches the loader as the card reads it (writeRecord case insert is a bare engine.insert, decideWriteAction answers insert). The new case pins insert added and insert changed both carry the note and ignore changed stays silent.
  • Gates (check-runs on the head): 48 runs — 35 success, 13 skipped, 0 failed; dev's local dispatch-gates --ran 62/62, 0 NOT-MEASURED; CLI unit 398/398 files; ablation (line removed) reds the new case (report 6710326997).
  • Accepted deviations: none.
  • Out of scope: none.
  • Landing: Tier: none. Seat lands: pr_ready + automerge_enable through the relay.

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 tests tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Projects

None yet

2 participants