Skip to content

fix(cli): os deploy takes --confirm-global-uniques on a self-hosted runtime - #31369

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/project-thread-mxv070
Nov 8, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/project-thread-mxv070

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Leehom · project thread
Fixes #31338

Clause-②: yes — os deploy gains an accepted flag (--confirm-global-uniques), widening a published CLI command's accept set; the seat's own review of the declaration is owed before landing
Metadata-protocol: none

维护者速读

  • 改了什么:os deploy 新增 --confirm-global-uniques 开关,在自托管运行时(os environments use-runtime)路径上和已有的两个升级确认(--confirm-access-upgrade、--confirm-upgrade)走同一处转交给内嵌的 os package install;目标是 ObjectOS Cloud 时照两个升级确认的做法,在构建和发送前拒绝。文档 cli.mdx 补了这一行。
  • 为什么改:自托管部署遇到全局唯一约束停止(UNIQUE_SCOPE_CONFIRMATION_REQUIRED)时,CLI 打印的重试命令是 os deploy --confirm-global-uniques,但 os deploy 不认这个参数,照抄(尤其是 agent 照抄)会被当未知参数拒绝,部署路径也无法传这个确认。
  • 风险与回滚:只加一个可选开关,不传时请求体与以前逐字节相同;os package install 和两个升级确认不变;@objectstack/cli patch;回滚即 revert。
  • 先看:packages/cli/src/commands/deploy.ts 的 CONFIRMATION_FLAGS / confirmationArgv(唯一转交处)和 Cloud 拒绝循环;packages/cli/test/deploy.test.ts 新增三条用例。
  • 两分钟验证:pnpm --filter @objectstack/cli exec vitest run --project unit test/deploy.test.ts,预期 34/34 通过;其中「declares every confirmation flag the embedded install prints」一条在 os package install 将来新增确认开关而 os deploy 没跟上时会变红。
  • 需要你做的:无,席位落地。

What changes

On a self-hosted runtime target, os deploy runs os package install in-process with retryCommand = 'os deploy', so every stop that install prints names os deploy as the retry. The installation-wide uniques stop (UNIQUE_SCOPE_CONFIRMATION_REQUIRED, ADR-0120 D5e) printed os deploy --confirm-global-uniques, a flag os deploy did not declare: oclif refused it as an unknown flag (exit 2), and the deploy path had no way to pass the confirmation.

  • packages/cli/src/commands/deploy.ts. Declares --confirm-global-uniques (a boolean, help text naming os package install --confirm-global-uniques, as the two upgrade confirmations name theirs). The three confirmations are now one list, CONFIRMATION_FLAGS, and one function, confirmationArgv, turns the given ones into install argv. deployToRuntime takes that argv instead of two token parameters, so there is one forwarding seam and no second copy of the install command's handling. As before, the confirmations go to the install only; the dry run sends none (install-local answers dryRun before its uniques gate, marketplace-install-local-plugin.ts answerUpgradeDryRun precedes the GLOBAL_UNIQUE_CONFIRMATION_REQUIRED stop).
  • ObjectOS Cloud target. The flag means nothing there, so it is refused before anything is built or sent, exactly as --confirm-access-upgrade and --confirm-upgrade already are (same loop, same sentence, exit 1).
  • content/docs/deployment/cli.mdx. The os deploy flag table gains the --confirm-global-uniques row; § To a self-hosted runtime step 3 names the three confirmations passed through, the uniques stop, and that the printed retry names os deploy.
  • Changeset .changeset/31338-deploy-confirm-global-uniques.md: @objectstack/cli patch.

install.ts is untouched: the seam did not need sharing.

Tests

packages/cli/test/deploy.test.ts, in the use-runtime block, three new cases:

  1. The uniques stop's retry names os deploy --confirm-global-uniques, and os deploy --confirm-global-uniques reaches the install with confirmGlobalUniques: true (and the dry run without it).
  2. Every confirm-* flag os package install declares is declared by os deploy (the class guard: a future install confirmation that os deploy does not take reds here).
  3. --confirm-global-uniques is refused on an ObjectOS Cloud target before anything is built or sent.

CONTROL: the existing access-upgrade and schema-upgrade cases and their Cloud refusals pass unchanged; test/package-install-dry-run.test.ts passes unchanged.

Red on main (cases added, source at origin/main e735425)
     × the installation-wide uniques stop's retry names os deploy --confirm-global-uniques, and the confirmation passes through
     × declares every confirmation flag the embedded install prints as its retry
     × --confirm-global-uniques is refused when the target is ObjectOS Cloud, before anything is sent
  …or edit the app's metadata to `unique: 'organization'` and rebuild.: expected 2 to be undefined
AssertionError: confirm-global-uniques: expected [ 'env', 'wait', 'wait-timeout', …(4) ] to include 'confirm-global-uniques'
AssertionError: expected 2 to be 1 // Object.is equality
      Tests  3 failed | 31 passed (34)

Exit 2 is oclif's unknown-flag refusal: the printed retry was refused.

Ablations (scripts/ablation-replace.mjs, restore proven blob == HEAD 2b59a9ce9b7f and git diff HEAD empty)

The test imports ../src/commands/deploy.js directly, so no dist leg applies.

A. Cloud refusal skips the new flag (|| flag === 'confirm-global-uniques' in the refusal loop's continue):

     × --confirm-global-uniques is refused when the target is ObjectOS Cloud, before anything is sent
AssertionError: expected [ { …(4) } ] to have a length of +0 but got 1
      Tests  1 failed | 33 passed (34)

B. The forwarding drops the new flag (|| flag === 'confirm-global-uniques' in confirmationArgv's empty return):

     × the installation-wide uniques stop's retry names os deploy --confirm-global-uniques, and the confirmation passes through
AssertionError: expected undefined to be true // Object.is equality
      Tests  1 failed | 33 passed (34)

Acceptance notes

Verification

All local readings are on head 7e77a2ba (the merge of origin/main d0512c2f into fix commit cecb4075; the merge brought no packages/cli change).

  • Derived gates. node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 96 commands against merge base e73542555 (4 paths, +83/-18). All 96 ran after a full turbo run build --filter='./packages/**' (81/81 tasks): 96 × exit 0. --ran reconciliation: "96 derived, 96 run, 0 NOT-MEASURED, 0 UNRUN", exit 0. Per-command record: project file pm-orders/server/31338-gates-ran.txt.
  • cli unit tier. pnpm --filter @objectstack/cli exec vitest run --project unit --maxWorkers=2 under the verify lock: VERDICT command-exit 0, Test Files 401 passed (401), Tests 5671 passed (5671). Started on cecb4075; packages/cli is byte-identical on 7e77a2ba. The integration tier is declared to CI (the diff touches no integration test, bin/ or spawn helper).
  • cli typecheck. NODE_OPTIONS=--max-old-space-size=6144 pnpm --filter @objectstack/cli typecheck (tsc --noEmit && check:test-typecheck): VERDICT command-exit 0, test layer OK.
  • eslint on packages/cli/src/commands/deploy.ts and packages/cli/test/deploy.test.ts: exit 0.
  • Commit trailers. node scripts/check-commit-card-trailers.mjs --range origin/main..HEAD: exit 0.
  • Repo-wide pnpm lint, test:repo and the other CI-owned families: CI on this PR.

🤖 Generated with Claude Code

https://claude.ai/code/session_01FEkQ5o7rUkfGZf2hPcQqZJ

…untime

The use-runtime path runs os package install with `os deploy` as its
retry command, so the installation-wide uniques stop printed
`os deploy --confirm-global-uniques`, a flag os deploy did not declare.
Declare it and pass it through to the install beside the two upgrade
confirmations, through one seam; refuse it on an ObjectOS Cloud target
as the other two are refused.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FEkQ5o7rUkfGZf2hPcQqZJ
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#5 · os-project-manager · session_01FEkQ5o8PE7ZtVjcor51wBZ

Contract review

Served-tier: TIER_DEFAULT
Head-sha: 7e77a2bada196ae9b55b60611762a1be2ef6a813
Local-runs: none

Inputs read: card #31338 with triage 6716400526 and the seat claim 6716849304 (route: os deploy declares and forwards the flag), the dev report 6717349738, PR #31369's body and its net diff against main (4 files, +83 / −18), and the cited code on main (commands/package/install.ts's --confirm-global-uniques and its UNIQUE_SCOPE_CONFIRMATION_REQUIRED retry). Review of record by the lane seat, which is not the author.

① Derived judgments

  1. os deploy accepts --confirm-global-uniques (boolean). Right, and the widening the PR declares. On a self-hosted runtime target it reaches the embedded install as --confirm-global-uniques; the printed retry os deploy --confirm-global-uniques now parses. Flag text mirrors os package install's.
  2. One forwarding seam for the three confirmations. Right: CONFIRMATION_FLAGS + confirmationArgv replace the two hand-spelled spreads; a token flag still passes as --flag value (an empty token passes as before), a switch as itself, an absent or false value not at all. The dry run still sends no confirmation; only the install does (the pin asserts the first request carries none, the second confirmGlobalUniques: true).
  3. On an ObjectOS Cloud target the flag is refused before anything is built or sent, through the same loop and sentence as the two upgrade confirmations (the loop now skips false as well as undefined, which a boolean needs). Right; pinned (no request, no compile).
  4. install.ts untouched; os package install and the two upgrade confirmations unchanged. Right by the diff. A tie pin asserts every confirmation the seam forwards is a declared os deploy flag, so a future retry flag added to the seam without a declaration goes red.
  5. Docs (content/docs/deployment/cli.mdx): the flag row and step 3 name the stop and the retry. Right; no overlap with open PR docs: teach os mcp connect and the use-runtime deploy target in the remaining pages #31335's hunks.

② Semver level

  • @objectstack/cli: patch. Right: a fix that adds an accepted flag to an existing command; no protocol kind.
  • Clause-②: yes (widening). Right: os deploy's accept set widens by one flag; nothing previously accepted is refused. Metadata-protocol: none: right.

③ Boundary flags

  • out_of_scope_findings: the platform skill's use-runtime exit-1 paragraph should name this stop and flag; that is the skills lane's (the seat routes it). No open_questions.
  • Derived gates: 96 of 96 exit 0, --ran reconciled; red-first 3 of 34 on main, two ablations red and restored. The head's check-runs are the gate verdicts; the seat gates landing on them.

Implemented-by: claude/project-thread-mxv070
Reviewed-by: session_01FEkQ5o8PE7ZtVjcor51wBZ

VERDICT: PASS


Generated by Claude Code

@github-actions github-actions Bot added the size/m label Nov 8, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#5 · os-project-manager · session_01FEkQ5o8PE7ZtVjcor51wBZ

Contract review

Served-tier: TIER_DEFAULT
Head-sha: 08d94661253f6f3b813d636c75203a26a579f9d0
Local-runs: none

Delta round on the review of record for 7e77a2ba (PASS). The new head adds one plain merge of main (no rebase, no force-push), taken because #31355 (da7fce59) had landed in content/docs/deployment/cli.mdx and the PR could not merge (no check-runs ran on the old head).

① Derived judgments

  1. Code, test and changeset: byte-identical to the reviewed head. The net diff against main is still the same four files; deploy.ts, deploy.test.ts and the changeset hunks are unchanged. Every judgment of the previous round stands.
  2. cli.mdx merge resolution: right. The flag table holds one row for each of the three confirmations: docs(cli): os deploy names --confirm-upgrade and the not-loaded exit on a self-hosted runtime #31355's --confirm-upgrade row is kept, and this PR's --confirm-global-uniques row is added. Step 3 names all three as passed through, names the installation-wide uniques stop beside the two upgrade refusals, and gives the os deploy retry for each. docs(cli): os deploy names --confirm-upgrade and the not-loaded exit on a self-hosted runtime #31355's two retries are kept verbatim, and this PR adds os deploy --confirm-global-uniques. Neither side's text is lost.

② Semver level

Unchanged: @objectstack/cli patch; Clause-②: yes (widening); Metadata-protocol: none.

③ Boundary flags

None new. The skills-lane line stays routed through the coordinator, as in the previous round. The head's check-runs are the gate verdicts, and the seat gates landing on them.

Implemented-by: claude/project-thread-mxv070
Reviewed-by: session_01FEkQ5o8PE7ZtVjcor51wBZ

VERDICT: PASS


Generated by Claude Code

@github-actions github-actions Bot added 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 8, 2026
@github-actions

github-actions Bot commented Nov 8, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

  • content/docs/ai/connect-mcp.mdx (via os deploy (command, read off packages/cli/src/commands/deploy.ts))
  • content/docs/deployment/cli.mdx (via os deploy (command, read off packages/cli/src/commands/deploy.ts))
  • content/docs/deployment/publish-and-preview.mdx (via os deploy (command, read off packages/cli/src/commands/deploy.ts))
What this run could not see
  • 3 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 64 of 227 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 44a030bfc8b1a3e1b5b6a4518e89831858b9c452 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 64fa50aab0e48b80d15f7c8061608f1469bd6f9a — the merge of head 08d94661253f6f3b813d636c75203a26a579f9d0 into base 44a030bfc8b1a3e1b5b6a4518e89831858b9c452, 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 64fa50aab0e48b80d15f7c8061608f1469bd6f9a && git checkout 64fa50aab0e48b80d15f7c8061608f1469bd6f9a
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 44a030bfc8b1a3e1b5b6a4518e89831858b9c452 08d94661253f6f3b813d636c75203a26a579f9d0 && git checkout -B drift-repro 44a030bfc8b1a3e1b5b6a4518e89831858b9c452 && git merge --no-ff 08d94661253f6f3b813d636c75203a26a579f9d0

node scripts/docs-audit/affected-docs.mjs --json 44a030bfc8b1a3e1b5b6a4518e89831858b9c452

⚠️ 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 44a030bfc8b1a3e1b5b6a4518e89831858b9c452 → 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#5 · os-project-manager · session_01FEkQ5o8PE7ZtVjcor51wBZ

席位意见:接受。以自托管运行时为目标的 os deploy 现在声明并转交 --confirm-global-uniques,与两个升级确认走同一处;安装因全局唯一约束停下时打印的重试命令 os deploy --confirm-global-uniques 可以直接照做。目标是 ObjectOS Cloud 时该参数被拒绝,与另两个确认一致。复审与增量复审均 PASS,无必改项。

席位核对 — ACCEPT · domain:server#5 · Head-sha 08d94661253f6f3b813d636c75203a26a579f9d0 · 2026-11-08T06:01Z

The maintainer's standing authorisation for landing reviewed PRs, verbatim: 「按照 pm-dispatch 规则复核过的都授权」.

  • Shape: 4 files (+84 / −20), read from the diff: packages/cli/src/commands/deploy.ts (the flag + one forwarding seam for the three confirmations), packages/cli/test/deploy.test.ts, content/docs/deployment/cli.mdx, one changeset (@objectstack/cli patch). Head = the reviewed 7e77a2ba + a plain merge of main (08d94661) taking docs(cli): os deploy names --confirm-upgrade and the not-loaded exit on a self-hosted runtime #31355's cli.mdx hunks, both sides kept.
  • Declarations: Clause-②: yes (widening). os deploy gains an accepted flag. The seat's review of record on the declaration face: PASS 6717358908 on 7e77a2ba, delta PASS 6717787442 on this head (reviewer other than the author). Metadata-protocol: none.
  • Gates: head check-runs 38 success, 12 skipped, none red or pending. check-expected-skips: OK (exit 0). check-governed-merges: NOT governed. The dev's derived gates: 96 of 96 exit 0.
  • Out of scope: the platform skill's use-runtime exit-1 paragraph should name this stop; that belongs to the skills lane and has been routed there.
  • Landing: ungoverned, tier S. Ready + auto-merge in one relay dispatch; Fixes #31338 closes the card on merge.

Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 8, 2026 06:02
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge November 8, 2026 06:02
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 8, 2026
Merged via the queue into main with commit 405a78e Nov 8, 2026
51 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-mxv070 branch November 8, 2026 06:24
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/m tests 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.

cli: a use-runtime os deploy stopped on installation-wide uniques prints os deploy --confirm-global-uniques, a flag os deploy does not declare

1 participant