Skip to content

Package upgrade plan: one framework comparison of installed vs new version (fields, types, renames, seeds) as a dry run; declared renamedFrom is planned, an undeclared rename or type change is refused unless confirmed (epic cloud#3652 Q3) #30884

Closed

Description

@objectstack-fleet

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)

  1. 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 through loadConfig (: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.
  2. os migrate plan compares 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)
  3. Schema sync on upgrade is additive. "The driver's initObjects sync 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 categories safe | needs_confirm | destructive exist (:135) and are what this card reuses for type changes. (measured)
  4. The finding-set-token pattern already exists on install-local. A managed re-install with access findings answers 409 ACCESS_UPGRADE_CONFIRMATION_REQUIRED with details.findings and a token, before any write. The same request with confirmAccessUpgrade: <token> installs, and a changed set is refused again (packages/cloud-connection/src/marketplace-install-local-plugin.ts:85-97, code at :454, :3456; ledger packages/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.
  5. 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)
  6. Seeds: the overwrite is now limited to upsert datasets. (measured)
  7. No rename declaration exists yet. renamedFrom → 0 hits in packages/spec/src/data/**; FieldSchema is strict (packages/spec/src/data/field.zod.ts:1243). Card 07 adds renamedFrom: string[] on a field. (measured)

Expected

  1. 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.ts already assigns;
    • declared renames: a field of the new version whose renamedFrom names a field of the installed version that the new version no longer has. This is a planned rename, listed as old → new. A declared rename whose old and new types the drift categories call destructive is a type-change finding too;
    • suspected renames: within one object, a field removed and a field added in the same upgrade with no renamedFrom linking them;
    • seed datasets added, removed and changed, each with its mode.
    • os diff is moved onto the same function, so the CLI and the runtime cannot disagree.
  2. 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 upsert dataset 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.
  3. Dry run on install-local. POST /api/v1/marketplace/install-local with dryRun: true returns the plan and writes nothing. os package install … --dry-run prints it.
  4. 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: 409 with a new code (working name SCHEMA_UPGRADE_CONFIRMATION_REQUIRED; the lane names it in the ledger), details.findings and a finding-set token. The same request with confirmSchemaUpgrade: <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 renamedFrom in the package and rebuild, or confirm.
    • The CLI gains --confirm-upgrade <token> on os package install. ⛔ There is no request-side rename input.
  5. 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 upsert dataset change and an ignore dataset change are both listed with their modes;
    • os diff output is unchanged for its existing fixtures.
  • Install-local integration tests:
    • dryRun: true writes 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 upsert dataset, one row edited, reports that row as one that would be overwritten.
  • The new error code is in error-code-ledger.zod.ts and 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=…:

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

  1. added
    enhancementNew feature or request
    repo:cloudCard routed whole to the cloud PM: needs cloud code, a producer or a fact (cloud owns it to closure)
    pm:blockedBlocked by another issue/PR — body carries Blocked-by: #N
    domain:serverDomain lane — seat card indexed by label:pm:seat
    on Nov 6, 2026
  2. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: audit · grade kept · domain:server · pm:blocked · 2026-11-06T09:58Z

    ⛔ Not a claim.

    • Epic objectstack-ai/cloud#3652 (the managed-package dev loop) on the maintainer's word. The framework half is objectstack code, so domain:server, and repo:cloud#3 follows it. The grade the filer set is kept. Blocked-by holds.

    Generated by Claude Code

  3. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    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 destructive declared rename to a type-change finding. A needs_confirm type change on a field that also declares renamedFrom would 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, whatever renamedFrom says.

  4. added
    pm:queueReady for the PM dispatch loop
    and removed
    pm:blockedBlocked by another issue/PR — body carries Blocked-by: #N
    on Nov 6, 2026
  5. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: unblock · pm:blocked → pm:queue · 2026-11-06T13:31Z

    ⛔ Not a claim.


    Generated by Claude Code

  6. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Nov 6, 2026
  7. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#1 · os-elon-musk · session_016WV4cytLmJxfAmgPVjUbbW

    Claim: PM loop patrol · 2026-11-06T13:45Z
    Session: session_016WV4cytLmJxfAmgPVjUbbW
    Account: os-elon-musk (the seat's linked user as GET /user answers 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-30884
    Domain: domain:server
    Seat: domain:server#1
    Plan: two PRs on one branch, in order. PR 1 widens only (Expected 1, 2, 3: the comparison, the environment half, dryRun, os diff on the same function). PR 2 carries the Q3 refusal (Expected 4 and the error code), the narrowing half, and Fixes #30884.
    File surface (card Expected 1–5 and its Acceptance, nothing else):

    • The comparison: ONE new module (plus its test) in one framework package, beside the upgrade-impact family (packages/plugins/plugin-security/src/upgrade-impact-report.ts) unless the dev names a better home in the report before the first push.
    • The environment half: one kernel service method (row counts in touched columns; for each changed upsert dataset, 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) and packages/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: true writes nothing; PR 2: the 409 with details.findings and a finding-set token, and confirmSchemaUpgrade), and the install-local request schema wherever it is declared.
    • PR 2 only: packages/spec/src/api/error-code-ledger.zod.ts (the new code, working name SCHEMA_UPGRADE_CONFIRMATION_REQUIRED) and its gate.
    • Tests named in the card's Acceptance; .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 declared renamedFrom never 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 report
    Container & 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: confirmSchemaUpgrade and 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 diff
    Metadata-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 diff compares 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). renamedFrom on FieldSchema landed with #30880 (PR #30934, f108e6d2). The dev re-reads each cited line on current main first
    Owed before landing: a CONTRACT_REVIEW_TIER review on each PR (served install door; PR 2 also the ledger); domain:spec consent 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#3652
    Serial constraints cleared: open PR #30933 (#30871, os deploy) touches packages/cli/src/help.ts but none of the files above; no open PR edits install-local, install.ts, diff.ts or schema-drift.ts. #30885 (01b, pm:blocked, unclaimed) follows this card's install-local edits

  8. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    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 thread session_016WV4cyPqWLpc3AxaZrs98n
    Account: os-elon-musk (the seat's linked user as GET /user answers 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-30884
    Domain: domain:server
    Seat: domain:server#1
    Plan: two PRs on one branch, in order. PR 1 widens only (Expected 1, 2, 3: the comparison, the environment half, dryRun, os diff on the same function). PR 2 carries the Q3 refusal (Expected 4 and the error code), the narrowing half, and Fixes #30884.
    File surface (card Expected 1–5 and its Acceptance, nothing else):

    • The comparison: ONE new pure module packages/metadata-core/src/package-upgrade-plan.ts (plus its test, and one export line in packages/metadata-core/src/index.ts). Home chosen by the dev: install-local (cloud-connection) calls it directly for dryRun and PR 2's Q3 verdict, and cloud-connection deliberately does not depend on plugin-security, so a home there would let a deployment without that plugin pass an undeclared rename silently. metadata-core is already a dependency of the CLI, objectql and cloud-connection: no new dependency edge.
    • The drift vocabulary safe | needs_confirm | destructive is declared in that module as the same three-word union; a type-level parity pin in a CLI test (the CLI depends on both driver-sql and metadata-core) keeps it equal to packages/drivers/driver-sql/src/schema-drift.ts, which is read and not changed.
    • The environment half: a planUpgrade method on the manifest service ObjectQLPlugin registers (packages/objectql/src/plugin.ts, one wiring line), implemented in a new packages/objectql/src/package-upgrade-environment.ts (plus its test). Install-local already registers through that service; a host (cloud's runtime) composes objectql and calls the same method.
    • packages/cli/src/commands/diff.ts (moved onto the comparison, existing fixtures' output unchanged) and packages/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: true writes nothing; PR 2: the 409 with details.findings and a finding-set token, and confirmSchemaUpgrade), and the install-local request schema wherever it is declared.
    • PR 2 only: packages/spec/src/api/error-code-ledger.zod.ts (the new code, working name SCHEMA_UPGRADE_CONFIRMATION_REQUIRED) and its gate.
    • Tests named in the card's Acceptance; .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 declared renamedFrom never 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 report
    Container & 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: confirmSchemaUpgrade and 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 diff
    Metadata-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 diff compares 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). renamedFrom on FieldSchema landed with #30880 (PR #30934, f108e6d2). The dev re-reads each cited line on current main first
    Owed before landing: a CONTRACT_REVIEW_TIER review on each PR (served install door; PR 2 also the ledger); domain:spec consent 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#3652
    Serial constraints cleared: open PR #30933 (#30871, os deploy) touches packages/cli/src/help.ts but none of the files above; no open PR edits install-local, install.ts, diff.ts or schema-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 edits

  9. 3 remaining items

  10. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    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 on domain: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:queue with no assignee; the next claimant resumes from that handoff.

  11. added
    pm:queueReady for the PM dispatch loop
    and removed
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    on Nov 6, 2026
  12. removed
    repo:cloudCard routed whole to the cloud PM: needs cloud code, a producer or a fact (cloud owns it to closure)
    on Nov 7, 2026
  13. objectstack-fleet commented on Nov 7, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · marchtian · session_017ihL2fBJ6xsLWJsnNJGMMx

    Ruling recorded · #30884 PR 2 · 2026-11-07T15:32Z · the maintainer chose A on a decision card (Fable decision thread, tapped 2026-11-07T15:30:20Z).

  14. objectstack-fleet commented on Nov 7, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · marchtian · session_017ihL2fBJ6xsLWJsnNJGMMx

    Correction · #30884 · 2026-11-07T15:33Z · to the ruling record above: #31134 (#29594 PR 3) does not touch marketplace-install-local-plugin.ts or objectql/src/plugin.ts (its file list was read at head 775c97b55e), so it is no serial constraint. PR 2 waits only for a free slot in this seat (cap 5); the seat dispatches it when one frees. ⛔ Not a claim.

  15. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Nov 7, 2026
  16. objectstack-fleet commented on Nov 7, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · marchtian · session_017ihL2fBJ6xsLWJsnNJGMMx

    Claim: PM loop round 1 · #30884 PR 2 of 2 (the Q3 refusal on a managed re-install) · 2026-11-07T17:48Z · resumes handoff 6688284693 (domain:server#1, PR 1 #30983 landed as 8847e09d)
    Session: session_017ihL2fBJ6xsLWJsnNJGMMx
    Account: marchtian (the card's assignee)
    Branch: claude/project-thread-r2ii08 (the card thread's harness-pinned branch, cut from main d57be872; platform exception to claude/issue-<n>-<slug>)
    Worktree: objectstack-issue-30884
    Domain: domain:server
    Seat: domain:server#2
    File surface: packages/cloud-connection/src/marketplace-install-local-plugin.ts (gate step 2c′, confirmSchemaUpgrade, the ledger entry's schemaUpgradeConfirmation) and the install-local request schema wherever it is declared; packages/cli/src/commands/package/install.ts (--confirm-upgrade TOKEN) and its help entry; packages/metadata-core/src/package-upgrade-plan.ts only if the verdict needs a helper there; packages/spec/src/api/error-code-ledger.zod.ts (one code, under @objectstack/cloud-connection) and its regenerated artifacts; tests named in the card's Acceptance; .changeset/ files. ⛔ Rename execution or column carry (01b, #30885), dropping a removed column, request-side rename input, refusing removed fields or seed changes, ui/**, docs/adr/**, skills/**, .claude/**. Stop on breach; explain in the report.
    Container & model: L, mode:thread, model: TIER_DEFAULT (the dev; the seat holds the CONTRACT_REVIEW_TIER review), effort: default (the project's default effort)
    Clause-②: yes (narrowing) — a managed re-install that succeeds today is refused 409 SCHEMA_UPGRADE_CONFIRMATION_REQUIRED on an undeclared suspected rename or a needs_confirm / destructive type change, and a declared rename is refused 501 NOT_IMPLEMENTED until #30885; the widening half is the new ledger code and the confirmSchemaUpgrade body key / --confirm-upgrade flag. Ruling record 6709061297.
    Metadata-protocol: none
    Responsibility: what a package upgrade changes, refused before it installs unless confirmed | install-local body (confirmSchemaUpgrade), the error-code ledger | os package install, install-local, and cloud's hosted install door (sibling card 01c, repo:cloud)
    Thread-read: 6709069778
    Premise: holds. On origin/main d57be87276, comparePackageVersions and install-local's dryRun (PR 1) are present, ACCESS_UPGRADE_CONFIRMATION_REQUIRED is in the ledger and SCHEMA_UPGRADE_CONFIRMATION_REQUIRED is not, and install-local has no confirmSchemaUpgrade.
    Serial constraints cleared: #31182 (package-unmanaged-install.ts) landed; #31205 is ADR-only. No open PR of this seat edits install-local or install.ts. #30885 (01b) follows this card.

    The maintainer's tap 「A 全部同意(推荐)」 (record 6709061297) is the spec consent for the one ledger hunk and the narrowing consent. Owed before landing: a full dogfood run, the BREAKING banner, migration lines, exactly one ADR-0087 marker, and the seat's CONTRACT_REVIEW_TIER (Fable) review of the ledger hunk and the code that implements the refusal. Order: project file server-seat-2/orders/30884-pr2.md.

  17. objectstack-fleet commented on Nov 7, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · marchtian · session_017ihL2dvnT51fbSebUyE3zW
    os-dev-report

    {
      "issue": 30884,
      "status": "done",
      "branch": "claude/project-thread-r2ii08",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/31243",
      "session": "session_017ihL2dvnT51fbSebUyE3zW",
      "premise_still_valid": true,
      "summary": "PR 2 of 2 (Fixes #30884), under ruling record 6709061297 (C1-C4 all A). install-local step 2c' (after the access gate, managed re-install only, before any write) reads comparePackageVersions on the ledger body vs the incoming one: a declared rename answers 501 NOT_IMPLEMENTED naming each (no token answers it); an undeclared suspected rename or a needs_confirm/destructive type change (on a declared rename too, scope note 6685166393) answers 409 SCHEMA_UPGRADE_CONFIRMATION_REQUIRED with details.findings, details.findingSet (schema-upgrade: + 32 hex SHA-256 over the sorted finding identities) and confirmWith; the matching confirmSchemaUpgrade installs and the ledger entry records schemaUpgradeConfirmation. safe retypes, removed fields/objects and seed changes are refused by nothing. Row counts attach per finding only when manifest.planUpgrade answers. One ledger code in packages/spec (no other spec hunk). CLI: os package install --confirm-upgrade and its 409 rendering; os deploy --confirm-upgrade pass-through (file-surface extension, see deviations).",
      "tests": "cloud-connection marketplace-install-local-upgrade-plan.test.ts 16/16 (10 new step-2c' cases; PR 1's control re-judged to a no-finding upgrade). cloud-connection pnpm test 73 files / 949 tests VERDICT command-exit 0, typecheck exit 0 (tree b767211b). spec pnpm test 821 files / 25964 passed + 1 todo VERDICT 0 (b767211b). cli --project unit 398 / 5626 (b767211b) and 398 / 5627 on head db650901, VERDICT 0; cli typecheck (8 GB heap) VERDICT 0. Ablations via scripts/ablation-replace.mjs on marketplace-install-local-plugin.ts (tests import src/, no build leg), each anchor hit once and restored with git diff HEAD empty: A1 suspected renames dropped -> 4 failed/12 passed; A2 category filter removed -> 1 failed (safe retype case); A3 any string token accepted -> 1 failed (different finding set); A4 501 branch disabled -> 2 failed (both declared-rename cases); A5 type change on a declared rename skipped -> 1 failed (scope-note case); A6 schemaUpgradeConfirmation not written -> 1 failed (token installs case).",
      "gates": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack on db650901 -> 113 families, each exit captured before any pipe; --ran: '113 derived famil(ies) accounted for -- 113 run, 0 NOT-MEASURED'. All exit 0; check-plugin-teardown-shape --self-test exit 3 on the shallow clone first, exit 0 after fetching 621a487607881c66b2899b7e3477115229a156b4. pnpm lint exit 0. spec check:generated: gen:docs regenerated 3 reference pages (committed); check:test-typecheck failed without diagnostics at the default heap, exit 0 at --max-old-space-size=8192 (ledger unchanged). Full build 119/119 VERDICT 0 on db650901. PR check-runs: not concluded at report time.",
      "dogfood_battery": "db650901 · os-verify-lock VERDICT command-exit 0 · Test Files 435 passed, 3 skipped (438) · Tests 3609 passed, 41 skipped (3650) · no failure",
      "ablations": "A1-A6 above, all red then restored",
      "spec_hunk": "packages/spec/src/api/error-code-ledger.zod.ts: +13 lines, one code SCHEMA_UPGRADE_CONFIRMATION_REQUIRED in the '@objectstack/cloud-connection' array (alphabetical, after RESEED_SKIPPED) with its comment; plus the 3 regenerated content/docs/references/api pages. No other packages/spec/src hunk.",
      "mcp_calls": "5 -- mcp__github__issue_read x2 (card body, comments), ToolSearch loads only; no write tool",
      "api_writes": "2 relay writes -- fleet-write pr_create (#31243, read back 13128/13128 bytes identical, assignees none) and this os-dev-report via post-stamped; plus git pushes (not REST)",
      "open_questions": [],
      "security_findings": "none",
      "out_of_scope_findings": [
        "carrier: seat -- docs follow-up owed (Skills-docs line): content/docs/deployment/cli.mdx has no --confirm-upgrade paragraph; content/docs/data-modeling/fields.mdx renamedFrom row does not say a declaring upgrade is now refused 501. Dedupe words: confirm-upgrade docs, renamedFrom docs 501, schema upgrade confirmation docs",
        "carrier: none · noted, not filed -- the dry run answer carries no schema token; an installer reads it off the 409"
      ],
      "files_changed": [
        ".changeset/30884-schema-upgrade-confirmation.md",
        "content/docs/references/api/automation-api.mdx",
        "content/docs/references/api/contract.mdx",
        "content/docs/references/api/error-code-ledger.mdx",
        "packages/cli/src/commands/deploy.ts",
        "packages/cli/src/commands/package/install.ts",
        "packages/cli/test/deploy.test.ts",
        "packages/cli/test/package-install-dry-run.test.ts",
        "packages/cloud-connection/src/local-manifest-source.ts",
        "packages/cloud-connection/src/marketplace-install-local-plugin.ts",
        "packages/cloud-connection/src/marketplace-install-local-upgrade-plan.test.ts",
        "packages/spec/src/api/error-code-ledger.zod.ts"
      ],
      "deviations": [
        "File surface extension for the seat to ratify: packages/cli/src/commands/deploy.ts (+ deploy.test.ts). os deploy runs os package install in-process with retryCommand 'os deploy', so the new 409 would print 'os deploy --confirm-upgrade TOKEN', a flag os deploy would reject; mirrored --confirm-access-upgrade exactly. Also local-manifest-source.ts for the InstalledEntry.schemaUpgradeConfirmation type.",
        "A ledger entry that will not parse: no installed body to compare, so the upgrade proceeds as before with one warn (refusing would block the repairing re-install). Stated in Acceptance notes for the contract review.",
        "pr_create carried no labels and no assignees, per the dispatch order ('Leave the PR draft, no reviewers, no assignees'); the coordinator's spawn note had asked for pm:awaiting-merge + assignee marchtian -- the seat sets them at hand-over.",
        "PR body attribution link points to this card thread (the harness-required block), not the seat thread the order quoted.",
        "The ADR-0087 marker is in the changeset (the gate's input); the PR body states the disposition in prose (no HTML comment in dev-written GitHub text).",
        "Branch fast-forwarded to origin/main 30002df5 before the first commit (nothing pushed yet), then merged origin/main once (db650901) before opening the PR; no rebase, no force push."
      ]
    }
  18. objectstack-fleet commented on Nov 8, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · marchtian · session_017ihL2dvnT51fbSebUyE3zW
    os-dev-report

    {
      "issue": 30884,
      "status": "done",
      "branch": "claude/project-thread-r2ii08",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/31243",
      "session": "session_017ihL2dvnT51fbSebUyE3zW",
      "premise_still_valid": true,
      "round": "rework after seat contract review r1 (6712105906), seat follow-up on the orphan regression, and Fable incremental r2 (6714005316) should-fix + nits; head 39a525c236",
      "summary": "Step 2c' now fails closed and keeps C2 on every installed side. (1) Installed side: the ledger entry's body; for an orphaned install (no ledger file, findConflict step 3 finds the registry row) the body the registry serves. (2) No installed body (ledger file will not parse, the entry carries no body, or the orphan's registry row carries none): 409 SCHEMA_UPGRADE_CONFIRMATION_REQUIRED with one finding, installed_record_unreadable {file, cause via describeLedgerCause} or installed_record_missing; its token installs and records schemaUpgradeConfirmation. (3) On that path the renames the incoming version declares are read from it alone (declaredRenamesOfIncoming: first renamedFrom key the object no longer declares, toType, no fromType) and still answer 501 NOT_IMPLEMENTED with the record finding listed, with or without the token (Fable r2 should-fix). Round-1 should-fixes and nits: destructive pin, real tokens in the 501 tests, true-token pin, unused logger dropped, 01b #30885 cited in the step comment and JSDoc, manifestId folded into the digest. Docs in this PR: cli.mdx --confirm-upgrade paragraph, fields.mdx renamedFrom 501 line. JSDoc states the one-row limit of the orphan comparison for a multi-package artifact. check:slot-lookup: the registry read is typed (ctx.getService of OverrideGateEngine), no new any.",
      "tests": "cloud-connection pnpm test on 39a525c236: 73 files / 969 tests, os-verify-lock VERDICT command-exit 0; typecheck exit 0. marketplace-install-local-upgrade-plan + corrupt-ledger + conflict: 37/37. New pins: orphan retype vs registry body 409 + clean orphan 200 control; orphan row without body 409 installed_record_missing and its token installs; corrupt entry 409 installed_record_unreadable (file, cause /JSON/) and its token installs; corrupt entry + name2 renamedFrom ['name'] 501 without token and with the record's token, nothing written; destructive text-to-formula 409; 501 with tokenFor([]) and with a real token; true confirms nothing. marketplace-install-local-conflict.test.ts orphan upgrade unchanged (200, assertion not touched). cli unit and typecheck last run on a274e36e (399 files / 5637, VERDICT 0); no cli file changed since.",
      "gates": "node scripts/pm/dispatch-gates.mjs --commands on 39a525c236 (after the merge of origin/main incl. #31284 59d9815a03) gives 116 families, each exit captured before any pipe; --ran: '116 derived famil(ies) accounted for -- 116 run, 0 NOT-MEASURED (a DERIVED zero)'. All exit 0. pnpm lint exit 0. Full turbo build exit 0. On a274e36e check:slot-lookup was red (14 gives 15 erasures, an any-typed ctx.getService in registeredPackageBody); fixed in 5c6cd1cfd7, holds since.",
      "dogfood_battery": "39a525c236 · os-verify-lock VERDICT command-exit 0 (27m32s) · Test Files 435 passed, 3 skipped (438) · Tests 3610 passed, 41 skipped (3651) · no failure",
      "ablations": "scripts/ablation-replace.mjs on marketplace-install-local-plugin.ts, each restored with git diff HEAD empty: A7 unavailable branch returns no finding gives 2 failed; A8 declaredRenames.length===0 guard removed gives 2 failed (both declared-rename cases); A9 destructive dropped from the confirmed set gives 1 failed; A10 manifestId dropped from the digest gives 2 failed; A11 registeredPackageBody returns undefined gives 2 failed (conflict orphan upgrade + orphan compare); A12 declaredRenamesOfIncoming replaced by [] gives 1 failed (corrupt entry + rename 501). A1-A6 from round 1 unchanged.",
      "ci_reds": "Test Core 2/8 and 6/8 on 02481a8990 (app-todo loop-body-iteration, scenarios external-driver): unrelated, each rerun twice locally 5/5 and 8/8; external-driver timeout fixed on main by #31284, merged in.",
      "spec_hunk": "unchanged from round 1 except the ledger comment clause '-- or has no installed record it can read to compare against --'. One code SCHEMA_UPGRADE_CONFIRMATION_REQUIRED; no other packages/spec/src hunk.",
      "contract_surface": "gate code changed (step 2c' installed side, unavailable verdict, declared renames on that path): seat-internal incremental review owed per the seat.",
      "mcp_calls": "read only -- issue_read (PR comments), actions_list + get_job_logs (CI reds); no write tool",
      "api_writes": "1 relay write -- this os-dev-report via post-stamped; plus git pushes (not REST). PR body not patched by dev.",
      "pr_body_edits_for_seat": [
        "Skills-docs line: 'updated here -- content/docs/deployment/cli.mdx, content/docs/data-modeling/fields.mdx' (no follow-up owed)",
        "Tests / Verification: refresh to this report (head 39a525c236, 116 gates, dogfood 3610, ablations A1-A12)",
        "Acceptance notes: replace the fail-open note with: no installed body asks (409 installed_record_unreadable / installed_record_missing, token installs); an orphan is compared against the registry body; a declared rename is 501 on every path. Drop the label note."
      ],
      "open_questions": [],
      "security_findings": "none",
      "out_of_scope_findings": [
        "carrier: none · noted, not filed -- the dry run answer carries no schema token; an installer reads it off the 409"
      ],
      "files_changed": [
        ".changeset/30884-schema-upgrade-confirmation.md",
        "content/docs/data-modeling/fields.mdx",
        "content/docs/deployment/cli.mdx",
        "content/docs/references/api/automation-api.mdx",
        "content/docs/references/api/contract.mdx",
        "content/docs/references/api/error-code-ledger.mdx",
        "packages/cli/src/commands/deploy.ts",
        "packages/cli/src/commands/package/install.ts",
        "packages/cli/test/deploy.test.ts",
        "packages/cli/test/package-install-dry-run.test.ts",
        "packages/cloud-connection/src/local-manifest-source.ts",
        "packages/cloud-connection/src/marketplace-install-local-corrupt-ledger.test.ts",
        "packages/cloud-connection/src/marketplace-install-local-plugin.ts",
        "packages/cloud-connection/src/marketplace-install-local-upgrade-plan.test.ts",
        "packages/spec/src/api/error-code-ledger.zod.ts"
      ],
      "deviations": [
        "deploy.ts passthrough and InstalledEntry type: accepted by the seat in r1.",
        "Merges of origin/main at 02481a8990, d04e0e2c88 and 39a525c236; no rebase, no force push."
      ]
    }
  19. objectstack-fleet commented on Nov 8, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · marchtian · session_017ihL2fBJ6xsLWJsnNJGMMx

    Landed — PR 2, card complete · 2026-11-08T01:27Z

    PR #31243 merged 2026-11-08T01:26Z through the merge queue as 40eaae7ff0. A managed re-install now refuses 409 SCHEMA_UPGRADE_CONFIRMATION_REQUIRED on an undeclared suspected rename or on a needs_confirm / destructive retype until the call carries the finding set's token (--confirm-upgrade, confirmSchemaUpgrade). With no readable installed record it asks rather than passing. A declared renamedFrom answers 501 NOT_IMPLEMENTED on every path.

    With PR 1 (#30983) and PR 2 on main, this card is closed (completed). The 501 lifts with #30885 (a renamedFrom carries the old column's data), which is still open and unclaimed by server seat 2. The cloud-hosted install door reading the same 409 is sibling card 01c.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

domain:serverDomain lane — seat card indexed by label:pm:seatenhancementNew feature or requestpm:blockingDerived cache from the Blocked-by reverse index: open card with open dependents — never hand-setpm:dispatchedDispatched to a dev agent by /pm-dispatchpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions