Repository navigation
Description
repo:cloud#3 · hotlong · session_01Q4EjjRhUZjdy8VXs6Kkur2
Blocked-by: #30880
Filing gate: ③ a task on the maintainer's word. Epic objectstack-ai/cloud#3652, coordinator session 2026-11-06, verbatim: 「我觉得先把受管软件包的开发循环走顺了」, then 「同意,然后第一步先解决我本地开发的软件包怎么能够方便地发布到云端」. The epic's Phase 2 lists "Change preview before install". Rulings on the epic (record on cloud#3652, 2026-11-06), verbatim:
- Q1: 「a 本地 os dev(推荐)」. Iterate locally, deploy at milestones. The preview guards each milestone deploy into an environment that is production (ADR-0024).
- Q3: 「默认拒绝,需显式映射(推荐)」. An upgrade that renames a field or changes its type is refused by default, unless an explicit old→new mapping or a confirmation is supplied. The ruling places Q3 in this card.
- D3, on where the mapping lives: 「写进清单 renamedFrom」. The package author declares it on the field in the manifest (card 07), so every installer's upgrade applies it. It is not an install-request input. This supersedes item 5 of objectstack#24443's ruling record
6259332195("No rename spelling is added to the spec").
Carrier: objectstack. Comparing two versions of a package is platform capability (maintainer, 2026-11-06: 「平台的问题应该在平台处理」, #30765). Cloud only calls it from its hosted install door (sibling card 01c, repo:cloud).
维护者速读
现在升级一个软件包时,平台不会先告诉你会变什么:字段改名会变成一个新的空列,旧数据还在旧列里但看不到;改字段类型也是静默的。这张卡在平台里做一个「升级预览」:比较已安装版本和新版本,列出增删字段、类型变化、改名和种子数据变化,可以只看不装。包作者在新版本里用 renamedFrom 声明过的改名,算作计划内的改名,照常升级,数据由 01b 搬过去。没有声明却像改名的、以及改字段类型的,按 Q3 默认拒绝,要带着预览给出的确认令牌再装一次。云端和私有部署都调用这同一个比较。
Today (objectstack origin/main 8b9cc712; every file cited below is unchanged since c50e0dfa)
- No comparison of two package versions exists as a library. (measured)
os diff(packages/cli/src/commands/diff.ts:162) compares two config files loaded throughloadConfig(:223-224). It lives in the CLI command, not in a package a runtime or cloud can call.- It reports added and removed objects and fields, required-field additions and type changes (
:23,:55,:138), and name-level changes for views, flows, agents, apps, dashboards, actions, workflows, apis and positions (:239-247). - It does not compare seeds (
data) or permission sets, and it does not detect renames.
os migrate plancompares metadata with a physical database, not two versions. "dry-run diff of metadata vs the physical database" (commands/migrate/plan.ts:62). It needs--database-url/$OS_DATABASE_URL(:137-139), so it cannot run against a cloud environment. (measured)- Schema sync on upgrade is additive. "The driver's
initObjectssync is additive-only" and a "drop or rename a field" silently diverges (packages/drivers/driver-sql/src/schema-drift.ts:6-9). A renamed field becomes a new empty column and the old column stays. The drift categoriessafe | needs_confirm | destructiveexist (:135) and are what this card reuses for type changes. (measured) - The finding-set-token pattern already exists on install-local. A managed re-install with access findings answers
409 ACCESS_UPGRADE_CONFIRMATION_REQUIREDwithdetails.findingsand a token, before any write. The same request withconfirmAccessUpgrade: <token>installs, and a changed set is refused again (packages/cloud-connection/src/marketplace-install-local-plugin.ts:85-97, code at:454,:3456; ledgerpackages/spec/src/api/error-code-ledger.zod.ts:1319; CLI--confirm-access-upgrade,packages/cli/src/commands/package/install.ts:174,:307). (measured) This card's confirmation copies that shape. - The boot-time upgrade-impact report covers customer state against the registry: orphans, re-enablements, overlay orphans, field collisions and setting orphans (
packages/plugins/plugin-security/src/upgrade-impact-report.ts:4-17). It does not look at a package's own data schema or seeds. (measured) - Seeds: the overwrite is now limited to
upsertdatasets. (measured)- Since feat(spec)!: a seed is create-only by default — an omitted
modeisignore#30152 an omitted seedmodeisignore(packages/spec/src/data/seed.zod.ts:193;packages/spec/src/migrations/entries/semantic/18.seed-mode-default-create-only.ts). Cloud's pin2ffdb649contains it. - A dataset that declares
mode: 'upsert', or an artifact built before feat(spec)!: a seed is create-only by default — an omittedmodeisignore#30152 (it carries a writtenupsert), still writes its declared columns back over a customer's edit on every replay.
- Since feat(spec)!: a seed is create-only by default — an omitted
- No rename declaration exists yet.
renamedFrom→ 0 hits inpackages/spec/src/data/**;FieldSchemais strict (packages/spec/src/data/field.zod.ts:1243). Card 07 addsrenamedFrom: string[]on a field. (measured)
Expected
- One comparison, as a library. A pure function in a framework package (the lane picks where; beside the upgrade-impact family is the natural home) takes the installed artifact and the new artifact and returns findings:
- objects and fields added and removed;
- field type changes, each with the drift category
schema-drift.tsalready assigns; - declared renames: a field of the new version whose
renamedFromnames a field of the installed version that the new version no longer has. This is a planned rename, listed asold → new. A declared rename whose old and new types the drift categories calldestructiveis a type-change finding too; - suspected renames: within one object, a field removed and a field added in the same upgrade with no
renamedFromlinking them; - seed datasets added, removed and changed, each with its mode.
os diffis moved onto the same function, so the CLI and the runtime cannot disagree.
- An environment-aware half, as a kernel service method. Given the running environment, it adds what only the environment knows: row counts in columns a removal, rename or type change touches, and for each changed
upsertdataset the number of rows whose current value differs from the version being replaced (rows a customer edited that the replay would overwrite). It is a service method, so install-local and a host (cloud's runtime) call the same code. - Dry run on install-local.
POST /api/v1/marketplace/install-localwithdryRun: truereturns the plan and writes nothing.os package install … --dry-runprints it. - Q3 verdict on a managed re-install, before any write:
- A declared rename is not refused. The install proceeds and 01b carries the column. Until 01b lands, a declared rename is refused with a sentence that names it and says rename execution is not available yet, so a declared rename never installs as a silent empty column.
- An undeclared suspected rename or a type change is refused by default:
409with a new code (working nameSCHEMA_UPGRADE_CONFIRMATION_REQUIRED; the lane names it in the ledger),details.findingsand a finding-set token. The same request withconfirmSchemaUpgrade: <token>installs, accepting the findings as shown (today's additive behaviour). A changed finding set is refused again. - The refusal says both ways forward: declare
renamedFromin the package and rebuild, or confirm. - The CLI gains
--confirm-upgrade <token>onos package install. ⛔ There is no request-side rename input.
- Removed fields and seed changes are reported, not refused (no ruling covers them; the removed column is not dropped today).
Acceptance
- Unit tests on the comparison:
- a field removed + one added of the same type in one object, no
renamedFrom→ one suspected rename; - the same pair with
renamedFrom: ['old']on the new field → one declared rename and no suspected rename; - a type change carries its drift category;
- an
upsertdataset change and anignoredataset change are both listed with their modes; os diffoutput is unchanged for its existing fixtures.
- a field removed + one added of the same type in one object, no
- Install-local integration tests:
dryRun: truewrites nothing (ledger file and registry unchanged);- a re-install with an undeclared suspected rename answers 409 with the token; the same call with the token installs; with a different finding set the old token is refused;
- a declared rename is refused by name until 01b lands (this test is replaced by 01b's).
- Environment half: a test with a seeded
upsertdataset, one row edited, reports that row as one that would be overwritten. - The new error code is in
error-code-ledger.zod.tsand its gate passes.
Serial: after 07, so the new manifest can carry renamedFrom. Everything but the declared-rename finding can be built in parallel and merged after 07. No Phase 1 dependency: #30871's file surface (publish, login, environments, dev, deploy command) does not include install.ts or install-local.
Dedupe (2026-11-06), each run as gh api -X GET search/issues -f q=…:
repo:objectstack-ai/objectstack package upgrade plan dry run install-local is:issue→ 17 hits, none about this (test budgets, seat posts).repo:objectstack-ai/objectstack "upgrade-impact" is:issue→ 8 hits. [Decision] A vendor package upgrade checks nothing: a rename silently re-enables what the customer switched off, and orphans, collisions and broken references go unreported, for every metadata type #24443 (decision, ruled A), upgrade-impact comparison, access class (v18): a vendor upgrade that would orphan, re-enable or collide with a customer's access choice is refused until an administrator confirms, and raised at boot #24446 and upgrade-impact comparison after v18: report the non-access classes to the administrator, and retire the zero-consumer package-upgrade.zod.ts #24447 (closed) compare customer state with the registry at boot. Related, not duplicates: none compares two versions' data schema or seeds, and none answers Q3.repo:objectstack-ai/objectstack upgrade impact schema rename field type change package is:issue→ 12 hits, the same family.repo:objectstack-ai/objectstack rename field upgrade new empty column is:issue→ 143 hits; the open ones are QA runs, seat posts, [epic] ADR-0131 — total organization ownership: no NULL organization_id (v18 line) #15194, objectql: a plainlookupwrite accepts a target the writer cannot read — the reference check probes it elevated, and onlymaster_detailasks the caller's read door #29029. None.repo:objectstack-ai/objectstack package upgrade preview diff is:issue is:open→ 12 hits, none.repo:objectstack-ai/objectstack "os diff" is:issue→ 500 hits (broad); the one on the command, [finding]os diff's missing-paths usage error prints 141 bytes of prose to STDOUT then exits 1 — it sits ABOVE the--jsonguard, so it fires in both faces #15697, is closed (stderr fix).repo:objectstack-ai/objectstack renamedFrom is:issue→ 1 hit, [Decision] A vendor package upgrade checks nothing: a rename silently re-enables what the customer switched off, and orphans, collisions and broken references go unreported, for every metadata type #24443 (see the gate).
Suggested-grade: p2 · Phase 2; a silent data-visibility loss on every renamed field in an environment that is production (ADR-0024).
Clause-②: yes (widening) · new install-local body keys (dryRun, confirmSchemaUpgrade) and a new error code in the ledger. It also narrows: a re-install that succeeds today is refused when it carries an undeclared suspected rename or a type change. The contract review covers both directions.
🤖 Generated with Claude Code
Activity
objectstack-fleet commentedon Nov 6, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: audit · grade kept ·
domain:server·pm:blocked· 2026-11-06T09:58Z⛔ Not a claim.
domain:server, andrepo:cloud#3follows it. The grade the filer set is kept.Blocked-byholds.Generated by Claude Code
objectstack-fleet commentedon Nov 6, 2026
repo:cloud#3 · hotlong · session_01Q4EjjRhUZjdy8VXs6Kkur2
Scope note (not a claim), escalated from contract review 6685133297 on PR #30934: today this card's Expected 1 promotes only a
destructivedeclared rename to a type-change finding. Aneeds_confirmtype change on a field that also declaresrenamedFromwould therefore slip Q3's confirmation (epic objectstack-ai/cloud#3652: a type change is refused unless confirmed). A declared rename must never stand in for a type-change confirmation: judge the type change on its own, whateverrenamedFromsays.objectstack-fleet commentedon Nov 6, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: unblock ·
pm:blocked→pm:queue· 2026-11-06T13:31Z⛔ Not a claim.
Blocked-by:is spec: a field declares the name(s) it was renamed from —renamedFromon FieldSchema, so a package upgrade carries the column instead of adding an empty one (epic cloud#3652 Q3; supersedes #24443 ruling item 5) #30880. Triage re-read it: closed as completed at 13:22Z, when PR feat(spec): a field declares the key(s) it was renamed from —renamedFromon FieldSchema #30934 merged asf108e6d2. Asked bydomain:server#1(6685980343).domain:serverkept. Therepo:cloudfollower is unchanged.repo:cloud#3(above) is part of the card: a declared rename never stands in for a type-change confirmation.Generated by Claude Code
objectstack-fleet commentedon Nov 6, 2026
domain:server#1 · os-elon-musk · session_016WV4cytLmJxfAmgPVjUbbW
Claim: PM loop patrol · 2026-11-06T13:45Z
Session:
session_016WV4cytLmJxfAmgPVjUbbWAccount:
os-elon-musk(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-30884-package-upgrade-plan(placeholder; the card thread's harness-pinned branch replaces it by a full re-post of this claim before any push)Worktree:
objectstack-issue-30884Domain:
domain:serverSeat:
domain:server#1Plan: two PRs on one branch, in order. PR 1 widens only (Expected 1, 2, 3: the comparison, the environment half,
dryRun,os diffon the same function). PR 2 carries the Q3 refusal (Expected 4 and the error code), the narrowing half, andFixes #30884.File surface (card Expected 1–5 and its Acceptance, nothing else):
packages/plugins/plugin-security/src/upgrade-impact-report.ts) unless the dev names a better home in the report before the first push.upsertdataset, rows whose current value differs from the version being replaced), in the package whose service the install doors already reach; named in the report.packages/drivers/driver-sql/src/schema-drift.ts: read the drift categories (safe | needs_confirm | destructive); an export only if the comparison cannot reach them otherwise.packages/cli/src/commands/diff.ts(moved onto the comparison, existing fixtures' output unchanged) andpackages/cli/src/commands/package/install.ts(--dry-run; PR 2 adds--confirm-upgrade <token>), and the CLI help entry for those flags.packages/cloud-connection/src/marketplace-install-local-plugin.ts(dryRun: truewrites nothing; PR 2: the 409 withdetails.findingsand a finding-set token, andconfirmSchemaUpgrade), and the install-local request schema wherever it is declared.packages/spec/src/api/error-code-ledger.zod.ts(the new code, working nameSCHEMA_UPGRADE_CONFIRMATION_REQUIRED) and its gate..changeset/files at the levels the diffs earn.⛔ Executing a rename or carrying column data (that is 01b, #30885), dropping a removed column, any request-side rename input, refusing removed fields or seed changes (Expected 5: report only),
ui/**,docs/adr/**,skills/**,.claude/**, objectstack-ai/cloud. ⛔ A declaredrenamedFromnever stands in for a type-change confirmation (repo:cloud#3's scope note 6685166393, adopted by triage 6686006987): the type change is judged on its own. Stop on breach and explain in the reportContainer & model:
L,mode:card-thread,model: default,effort: xhigh(a served install door, a new error code, a narrowing of re-install)Clause-②: yes, expected. PR 1 widens ( new install-local body key
dryRun, a new service method); PR 2 expected to carry both directions (widening:confirmSchemaUpgradeand the new code; narrowing: a re-install that succeeds today is refused on an undeclared suspected rename or a type change). The dev re-derives each from its diffMetadata-protocol: none expected; the dev re-derives it
Responsibility: what a package upgrade will change, before it installs | install-local body (
dryRun,confirmSchemaUpgrade), the error-code ledger |os diff,os package install, install-local, and cloud's hosted install door (sibling card 01c, repo:cloud)Thread-read: 6686006987
Premise: as the filer measured on
8b9cc712: no library compares two package versions (os diffcompares two config files inside the CLI,commands/diff.ts:162); schema sync on upgrade is additive, so a renamed field becomes a new empty column (schema-drift.ts:6-9); install-local already answers access findings with a 409 and a finding-set token (marketplace-install-local-plugin.ts:85-97).renamedFromonFieldSchemalanded with #30880 (PR #30934,f108e6d2). The dev re-reads each cited line on currentmainfirstOwed before landing: a
CONTRACT_REVIEW_TIERreview on each PR (served install door; PR 2 also the ledger);domain:specconsent for PR 2's ledger hunk; PR 2's narrowing goes to the maintainer for the per-PR word, citing the Q3 ruling on objectstack-ai/cloud#3652Serial constraints cleared: open PR #30933 (#30871,
os deploy) touchespackages/cli/src/help.tsbut none of the files above; no open PR edits install-local,install.ts,diff.tsorschema-drift.ts. #30885 (01b,pm:blocked, unclaimed) follows this card's install-local editsobjectstack-fleet commentedon Nov 6, 2026
domain:server#1 · os-elon-musk · session_016WV4cytLmJxfAmgPVjUbbW
Claim: PM loop patrol · 2026-11-06T13:55Z (full re-post of claim 6686288036: the card thread's branch, and the two module homes the dev named before the first push)
Session:
session_016WV4cytLmJxfAmgPVjUbbW(the seat); dev = card threadsession_016WV4cyPqWLpc3AxaZrs98nAccount:
os-elon-musk(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/project-thread-89x8tb(the card thread's harness-pinned branch; it names no issue, so this claim is the identity record; PR 2 stacks on PR 1 on this branch)Worktree:
objectstack-issue-30884Domain:
domain:serverSeat:
domain:server#1Plan: two PRs on one branch, in order. PR 1 widens only (Expected 1, 2, 3: the comparison, the environment half,
dryRun,os diffon the same function). PR 2 carries the Q3 refusal (Expected 4 and the error code), the narrowing half, andFixes #30884.File surface (card Expected 1–5 and its Acceptance, nothing else):
packages/metadata-core/src/package-upgrade-plan.ts(plus its test, and one export line inpackages/metadata-core/src/index.ts). Home chosen by the dev: install-local (cloud-connection) calls it directly fordryRunand PR 2's Q3 verdict, andcloud-connectiondeliberately does not depend onplugin-security, so a home there would let a deployment without that plugin pass an undeclared rename silently.metadata-coreis already a dependency of the CLI,objectqlandcloud-connection: no new dependency edge.safe | needs_confirm | destructiveis declared in that module as the same three-word union; a type-level parity pin in a CLI test (the CLI depends on bothdriver-sqlandmetadata-core) keeps it equal topackages/drivers/driver-sql/src/schema-drift.ts, which is read and not changed.planUpgrademethod on the manifest serviceObjectQLPluginregisters (packages/objectql/src/plugin.ts, one wiring line), implemented in a newpackages/objectql/src/package-upgrade-environment.ts(plus its test). Install-local already registers through that service; a host (cloud's runtime) composesobjectqland calls the same method.packages/cli/src/commands/diff.ts(moved onto the comparison, existing fixtures' output unchanged) andpackages/cli/src/commands/package/install.ts(--dry-run; PR 2 adds--confirm-upgrade <token>), and the CLI help entry for those flags.packages/cloud-connection/src/marketplace-install-local-plugin.ts(dryRun: truewrites nothing; PR 2: the 409 withdetails.findingsand a finding-set token, andconfirmSchemaUpgrade), and the install-local request schema wherever it is declared.packages/spec/src/api/error-code-ledger.zod.ts(the new code, working nameSCHEMA_UPGRADE_CONFIRMATION_REQUIRED) and its gate..changeset/files at the levels the diffs earn.⛔ Executing a rename or carrying column data (that is 01b, #30885), dropping a removed column, any request-side rename input, refusing removed fields or seed changes (Expected 5: report only),
ui/**,docs/adr/**,skills/**,.claude/**, objectstack-ai/cloud. ⛔ A declaredrenamedFromnever stands in for a type-change confirmation (repo:cloud#3's scope note 6685166393, adopted by triage 6686006987): the type change is judged on its own. Stop on breach and explain in the reportContainer & model:
L,mode:card-thread,model: default,effort: xhigh(a served install door, a new error code, a narrowing of re-install)Clause-②: yes, expected. PR 1 widens ( new install-local body key
dryRun, a new service method); PR 2 expected to carry both directions (widening:confirmSchemaUpgradeand the new code; narrowing: a re-install that succeeds today is refused on an undeclared suspected rename or a type change). The dev re-derives each from its diffMetadata-protocol: none expected; the dev re-derives it
Responsibility: what a package upgrade will change, before it installs | install-local body (
dryRun,confirmSchemaUpgrade), the error-code ledger |os diff,os package install, install-local, and cloud's hosted install door (sibling card 01c, repo:cloud)Thread-read: 6686288036
Premise: as the filer measured on
8b9cc712: no library compares two package versions (os diffcompares two config files inside the CLI,commands/diff.ts:162); schema sync on upgrade is additive, so a renamed field becomes a new empty column (schema-drift.ts:6-9); install-local already answers access findings with a 409 and a finding-set token (marketplace-install-local-plugin.ts:85-97).renamedFromonFieldSchemalanded with #30880 (PR #30934,f108e6d2). The dev re-reads each cited line on currentmainfirstOwed before landing: a
CONTRACT_REVIEW_TIERreview on each PR (served install door; PR 2 also the ledger);domain:specconsent for PR 2's ledger hunk; PR 2's narrowing goes to the maintainer for the per-PR word, citing the Q3 ruling on objectstack-ai/cloud#3652Serial constraints cleared: open PR #30933 (#30871,
os deploy) touchespackages/cli/src/help.tsbut none of the files above; no open PR edits install-local,install.ts,diff.tsorschema-drift.ts. The two one-line wiring files (metadata-core/src/index.ts,objectql/src/plugin.ts) were not searched; the dev rebases on a collision. #30885 (01b,pm:blocked, unclaimed) follows this card's install-local editsobjectstack-fleet commentedon Nov 6, 2026
domain:server#1 · os-elon-musk · session_016WV4cyPqWLpc3AxaZrs98n
os-dev-report
{ "issue": 30884, "status": "done", "branch": "claude/project-thread-89x8tb", "pr": "https://github.com/objectstack-ai/objectstack/pull/30983", "session": "session_016WV4cyPqWLpc3AxaZrs98n", "premise_still_valid": true, "summary": "PR 1 of the claim's two (widening only, Refs #30884, no Fixes). @objectstack/metadata-core gains comparePackageVersions: a pure comparison of two package versions listing objects and fields added/removed, type changes with the driver's drift word (safe / needs_confirm / destructive), renames declared by renamedFrom, suspected renames (a stored field removed and stored fields added in one object with nothing linking them), and seed datasets added/removed/changed with their mode; a declared rename whose type changes is also a type change (scope note 6685166393). os diff now reads its object, field and seed entries from it (every earlier output shape pinned before the move and unchanged). objectql's manifest service gains planUpgrade(installed, incoming): the plan plus rows holding a value in each removed / renamed / retyped column and in each removed object, and for each upsert or update dataset the matched rows and the customer-edited rows a replay would overwrite (a failed reading is null and logged, never 0). install-local accepts dryRun: true and answers the plan of the ledger entry against the incoming body at step 2a', before any ceremony or write; os package install --dry-run sends it and prints the plan. PR 2 (the Q3 refusal, the ledger code, confirmSchemaUpgrade, --confirm-upgrade, Fixes #30884) follows after this lands.", "tests": "New: 42 tests (metadata-core package-upgrade-plan.test.ts 17, objectql package-upgrade-environment.test.ts 7 on the real ObjectQL engine, cloud-connection marketplace-install-local-upgrade-plan.test.ts 6, cli diff-upgrade-plan.test.ts 7 + package-install-dry-run.test.ts 5). Suites under os-verify-lock, each VERDICT command-exit 0: metadata-core 24 files / 928 tests; objectql --project local 560 / 11207 (tree of 3dba4119; head 3100b841 changes only package-upgrade-environment.test.ts, 7/7 on head); cloud-connection 73 / 923; cli --project unit 391 / 5494; cli --project integration test/package-install-local- 8 / 83 and test/package-owned-command-readers.test.ts 1 / 8. Typecheck exit 0: metadata-core, cloud-connection, cli (NODE_OPTIONS=--max-old-space-size=8192; OOM at the default heap), objectql (on head); test-layer debt ledgers unchanged. Ablations on head (tree of 3100b841) via scripts/ablation-replace.mjs, anchor x1, blob changed, restored to the HEAD blob with git diff HEAD empty: suspected-rename detection (storedAdded forced to []) -> 3 failed / 14 passed; type judgment on a declared rename (limited to target === key) -> 1 failed / 16 passed ('a declared rename whose type changes is ALSO a type change, judged on its own'); dryRun write guard (step 2a' removed) -> 3 failed / 3 passed (re-install, not-installed, 503 cases).", "gates": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack -> 66 families; each run with its exit code recorded before any pipe; --ran reconciliation: '66 derived famil(ies) accounted for -- 66 run, 0 NOT-MEASURED (a DERIVED zero)'. First pass 62 exit 0; reruns all exit 0: check:objectql-double-limit (exit 1 -> fixed in 3100b841: the failed-reading test stubs engine.find instead of a driver-double switch the probe drives; 'none new'), check-plugin-teardown-shape --self-test (exit 3 on a shallow clone -> fetched 621a4876), check:dual-build-cjs-loads and check:i18n-coverage (exit 3, no dist -> full build pnpm turbo run build --filter=!@objectstack/docs exit 0). Plus pnpm lint exit 0. git push passed the pre-push hooks (after the commit-message rewording in deviations). PR check-runs: not concluded at report time; the seat reads them.", "dogfood_battery": "n/a -- no ! in the PR title and no (narrowing) arm", "mcp_calls": "2 -- mcp__github__issue_read x2 (card body and comments); no write tool", "api_writes": "2 relay writes -- fleet-write pr_create (#30983, read back identical 16041/16041 bytes, assignees os-elon-musk) and this os-dev-report comment via post-stamped; plus one git push (not REST)", "open_questions": [ { "question": "PR 2: the name of the new ledger code for the Q3 refusal (409, under '@objectstack/cloud-connection' beside ACCESS_UPGRADE_CONFIRMATION_REQUIRED); the hunk owes domain:spec consent.", "options": [ "A SCHEMA_UPGRADE_CONFIRMATION_REQUIRED", "B another name the spec owner prefers" ], "recommendation": "A, because it reads beside ACCESS_UPGRADE_CONFIRMATION_REQUIRED, which has the same token-confirm shape." }, { "question": "PR 2: how a declared rename is refused until 01b (#30885) can carry the column.", "options": [ "A 501 NOT_IMPLEMENTED (a standard catalog code; no ledger churn; 01b replaces the test)", "B its own 409 ledger code", "C the new 409 code with no token" ], "recommendation": "A, because the refusal is temporary and names the missing capability; B adds a ledger code 01b retires, and C gives one code two meanings (one confirmable, one not)." }, { "question": "PR 2: what the Q3 verdict reads.", "options": [ "A the pure comparison in cloud-connection (already a metadata-core dependent), with the environment readings attached to details.findings when manifest.planUpgrade exists", "B require planUpgrade and refuse 503 without it" ], "recommendation": "A, because whether an install is refused must not hinge on a reading, and existing re-install tests mount manifest services without planUpgrade." }, { "question": "PR 2: where the gate sits and what the ledger keeps.", "options": [ "A after the access gate (a step 2c'), recording schemaUpgradeConfirmation on the ledger entry", "B before the access gate" ], "recommendation": "A, because the access gate already answers its own token first, and the ledger then shows which finding set an installer confirmed." } ], "security_findings": "none", "out_of_scope_findings": [ "carrier: none · noted, not filed -- content/docs/deployment/cli.mdx has no row for os package install --dry-run; the page is outside this claim's file surface (docs, not class a/b/c)", "carrier: none · noted, not filed -- objectql's edited-row comparison restates metadata-protocol's private seedValueEquals (same core temporal / blank functions); one shared home would be a follow-up", "carrier: none · noted, not filed -- pnpm --filter @objectstack/cli typecheck runs out of memory at the default node heap; it passes at --max-old-space-size=8192" ], "line_budget": "n/a -- no skills/** in the diff", "files_changed": [ ".changeset/30884-package-upgrade-plan.md", "packages/cli/src/commands/diff.ts", "packages/cli/src/commands/package/install.ts", "packages/cli/test/diff-upgrade-plan.test.ts", "packages/cli/test/package-install-dry-run.test.ts", "packages/cloud-connection/src/marketplace-install-local-plugin.ts", "packages/cloud-connection/src/marketplace-install-local-upgrade-plan.test.ts", "packages/metadata-core/src/index.ts", "packages/metadata-core/src/package-upgrade-plan.test.ts", "packages/metadata-core/src/package-upgrade-plan.ts", "packages/objectql/src/package-upgrade-environment.test.ts", "packages/objectql/src/package-upgrade-environment.ts", "packages/objectql/src/plugin.ts" ], "deviations": [ "Six of the seven local commits first carried a 'Refs #30884' line; the pre-push check:commit-card-trailers refused the push, so the messages were reworded before the first push (git filter-branch --msg-filter, messages only; trees verified identical: 3100b841 = dfe095d9, 3dba4119 = 8fe46b01). No pushed history rewritten. Evidence measured on dfe095d9 / 8fe46b01 applies to the same trees.", "The objectql full suite was measured on the tree of 3dba4119; head 3100b841 changes only package-upgrade-environment.test.ts, which passes 7/7 on head.", "Declared narrowing: of the CLI integration tier (205 files) only the 8 install-local integration files and package-owned-command-readers.test.ts ran locally; CI runs the full tier.", "pr_create carried assignees ['os-elon-musk'] (the claim's Account), per os-dev's current 'PR 派认领 Account:(pr_create 带)'.", "Two readings go beyond the card's letter: the edited-row count covers every overwriting dataset both versions declare, changed or not, and the update mode as well as upsert. Stated in the PR's Acceptance notes.", "The dry run answers before the D5e and access ceremonies, so it does not say whether those would stop the real install. A runtime predating dryRun ignores it and installs; the CLI detects this and exits 1 after the fact. Both stated in the PR.", "Not counted by the environment half: replace datasets, and a dataset's locale / env filters (over-reports, never under-reports).", "Branch base 616311c1 is 17 commits behind main at push; none of those commits touches the 13 changed files (git diff --name-only checked).", "PR 1 opens with 'Refs #30884' (not Fixes) as the dispatch orders; Fixes goes with PR 2." ] }Generated by Claude Code
objectstack-fleet commentedon Nov 6, 2026
domain:server#1 · os-elon-musk · session_016WV4cytLmJxfAmgPVjUbbW
Handoff: #30884 after PR 1 (written by the card thread
session_016WV4cyPqWLpc3AxaZrs98n, posted by the seat)The lane is winding down. PR 1 (#30983) lands through the seat; PR 2 was not started and goes back to the queue. This is what the next claim needs.
What PR 1 delivers (#30983, draft, head
3100b841,Refs #30884, Clause-② yes (widening), report 6688229380)comparePackageVersions(installed, incoming)in@objectstack/metadata-core: pure, decides nothing. It lists objects and fields added and removed, type changes with the driver's drift word (safe/needs_confirm/destructive), renames declared byrenamedFrom, suspected renames (a stored field removed and stored fields added in one object with nothing linking them), and seed datasets added, removed and changed with their mode. A declared rename whose type changes is also a type change (scope note 6685166393).planUpgrade(installed, incoming)on objectql'smanifestservice: the plan plus readings. It counts the rows holding a value in each removed, renamed or retyped column and in each removed object. For eachupsertorupdatedataset it counts the matched rows and the customer-edited rows a replay would overwrite. A failed reading isnull, never0.dryRun: trueonPOST /api/v1/marketplace/install-local, answered at step 2a′ (answerUpgradeDryRun) before any ceremony or write.os package install --dry-runprints it, andos diffreads the same comparison.os diffunchanged,dryRunwrites nothing, and the environment half's edited-row case.What PR 2 must do (card, claim 6686492425)
renamedFromcarries the old column's data to the new name on package upgrade, instead of leaving it behind an empty new column (epic cloud#3652 Q3) #30885) lands. Open question:501 NOT_IMPLEMENTED(my recommendation; no ledger churn) or its own code.409with the new ledger code (working nameSCHEMA_UPGRADE_CONFIRMATION_REQUIRED),details.findingsand a token over the finding set. Open question: whether asaferetype (the installed field had no stored column) needs confirming.confirmSchemaUpgrade: TOKENinstalls. A changed finding set is refused again with a stale-token sentence. The pattern to copy isevaluateAccessUpgradeGateinmarketplace-install-local-plugin.ts.renamedFromand rebuild, or confirm with the token.--confirm-upgrade TOKEN.packages/spec/src/api/error-code-ledger.zod.tsunder'@objectstack/cloud-connection', besideACCESS_UPGRADE_CONFIRMATION_REQUIRED. It owesdomain:specconsent.Fixes #30884and states the narrowing: a re-install that succeeds today is refused.Module homes
packages/metadata-core/src/package-upgrade-plan.ts. cloud-connection already depends on metadata-core, and it cannot depend on plugin-security. The drift words are restated there asUpgradeDriftCategory, andpackages/cli/test/diff-upgrade-plan.test.tspins them type-equal to driver-sql'sDriftCategory.packages/objectql/src/package-upgrade-environment.ts, wired inpackages/objectql/src/plugin.ts.packages/cloud-connection/src/marketplace-install-local-plugin.ts. Suggested gate placement is after the access gate (2c). The ledger entry should recordschemaUpgradeConfirmation.packages/cli/src/commands/package/install.ts(renderUpgradePlanis exported) andpackages/cli/src/commands/diff.ts.Traps
planUpgradeexists. Existing install-local re-install tests mountmanifestservices withoutplanUpgrade, so requiring it breaks them.dryRunpre-loads no existing rows, so it cannot count edited rows. The environment half compares rows itself and restates metadata-protocol's privateseedValueEquals.check:objectql-double-limitprobes everyfinddouble in objectql tests, and a throwing switch in a driver double fails it. Stubengine.findwithvi.spyOninstead.check:dual-build-cjs-loadsandcheck:i18n-coverageexit 3 without a full build (pnpm turbo run build --filter=!@objectstack/docs).check-plugin-teardown-shape --self-testexits 3 on a shallow clone. Fetch621a4876first.check:commit-card-trailersrefuses aRefs #NorFixes #Nline in a commit message. The relation goes in the PR body only.pnpm --filter @objectstack/cli typecheckruns out of memory at the default heap. UseNODE_OPTIONS=--max-old-space-size=8192.renamedFrom(feat(spec): a field declares the key(s) it was renamed from —renamedFromon FieldSchema #30934) is not released yet; changeset30880-field-renamed-fromis pending.objectstack-fleet commentedon Nov 6, 2026
domain:server#2 · os-tesla · session_01L9qibyvxbrLP4jm4PargoH
Landing · 2026-11-06T17:32Z · PR #30983 (PR 1) MERGED 2026-11-06T17:11Z as
8847e09d, landed by this seat ondomain:server#1's hand-over (6690664327 on mailbox #30985).The card stays open for PR 2; its handoff is 6688284693 on this card. The card goes back to
pm:queuewith no assignee; the next claimant resumes from that handoff.