You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fix(cloud-connection): keep the binding credential under OS_HOME so a container recreate does not unbind the node #31604
epic:#3814#1 · os-zhuang · session_01QUs3BrVu5TqwtmuhsjdEvM
Filing gate: ③ maintainer-dispatched task (child of #3814). Maintainer, (2026-11-08, 16:25 UTC): 「继续,现在派第二批」 (gate 1 after the card 0 inventory, cloud#3868 comment 6723322368).
Single-node defect S1 (open half), found by card 0 of #3814 (cloud#3868 comment 6723322368, V2). Maintainer ruling D2 (2026-11-08, 16:25 UTC): 「现在修,按普通 bug 卡」. This is a single-node bug fix, so it is NOT bound by #3814's R3.
Problem (measured on the shipped EE image)
cloud-connection.json resolves to <WORKDIR>/.objectstack/ in the container layer, not OS_HOME=/data. It survives docker restart but is lost on recreate (compose recreate, upgrade.sh's image switch). After a recreate the exchange answers "not bound", so an online-licensed single node drops to Community at once until someone re-binds.
Do
Resolve the binding credential file under OS_HOME (keep an explicit override variable if one exists).
Migrate in place: on boot, if the old cwd-relative file exists and the new one does not, move it, with mode 0600 kept and one log line.
Claim: PM loop round 5 · 2026-11-08T21:31Z
Session: session_01ADHkmS9T6n1TQU1WRxmFdb (card thread, dev) · seat session_01ADHkmQKZzp4DLtqiCkZZRM
Account: os-steve
Branch: claude/project-thread-3o188q
Worktree: objectstack-issue-31604
Domain: domain:server
Seat: domain:server#2
File surface: packages/cloud-connection/src/connection-credential-store.ts (+ cloud-connection-plugin.ts path wiring only if needed), tests, content/docs/deployment/cloud-connection.mdx if it states the path, root .changeset/. ⛔ Not the multi-replica shared store (connection-credential-ledger.ts, connection-credential-preboot.ts, sys-cloud-binding-credential.object.ts); no packages/spec/**, ui/**, helm/
Container & model: M, mode:thread, model: default tier, effort: high
Clause-②: no (unless a new env variable ships: then yes (widening), declared first)
Metadata-protocol: none
Responsibility: epic #3814 card 0 single-node defect; maintainer D2 「现在修,按普通 bug 卡」, not bound by R3
Thread-read: 6724404663
Premise: holds as filed, read at claim on origin/main5d1382a5: DEFAULT_CONNECTION_CREDENTIAL_PATH resolves against process.cwd() (connection-credential-store.ts L60), not OS_HOME
Serial constraints cleared: no open PR touches connection-credential-store.ts; #31554's shared store (PR #31584) merged and stays untouched
Scope: Fixes #31604. Resolve the credential through the install-local ledger's existing OS_HOME reading (local-manifest-source.ts), one resolver; explicit override kept with its precedence; boot migration MOVES the old cwd file when the new one is absent (0600 kept, one log line), both present → new wins, old untouched; OS_HOME unset → path unchanged; os dev --fresh keeps it in the ephemeral home. Order: /mnt/project-files/server-seat2/standard-order.md + /mnt/project-files/server-seat2/31604-delta.md. Summons: the maintainer's 「并发都是5」 (project chat, 2026-11-08T16:13Z).
{
"issue": 31604,
"status": "done",
"branch": "claude/project-thread-3o188q",
"pr": "https://github.com/objectstack-ai/objectstack/pull/31708",
"session": "session_01ADHkmS9T6n1TQU1WRxmFdb",
"premise_still_valid": true,
"summary": "A single node's cloud binding credential now resolves to OS_HOME/cloud-connection.json when OS_HOME is set (unchanged CWD/.objectstack/cloud-connection.json when unset), through readOsHome(), the install ledger's existing OS_HOME reading extracted so both resolvers share it. CloudConnectionPlugin.init() moves an old working-directory file once before the ledger is chosen (staged 0600 + rename, old removed after; one info line naming both paths), both present keeps the OS_HOME file and leaves the old one untouched, an os dev --fresh home moves nothing, and an explicit credentialPath keeps its precedence. Shared multi-replica store files untouched; doc page and patch changeset updated.",
"tests": "Red on main: the 3 source files restored from 5d1382a5 with the new tests: Tests 4 failed | 17 passed (21) (control + explicit-path green on main, as expected). Green on d6663391: pnpm --filter @objectstack/cloud-connection typecheck && vitest run --maxWorkers=2 -> Test Files 80 passed (80), Tests 1114 passed (1114), lock VERDICT command-exit 0. Ablation (scripts/ablation-replace.mjs, src-imported tests, no dist leg), 6 legs each red then restored (blob == HEAD, git diff HEAD empty): A1 both-present guard, A2 fresh-home exclusion, A3 old-file removal, A4 0600 staging mode, A5 explicit-path precedence, A6 plugin boot move.",
"gates": "d6663391: dispatch-gates --commands derived 99; all 99 run, all exit 0; --ran: 99 derived, 99 run, 0 NOT-MEASURED, 0 UNRUN. Round 1 on dd1ef0a6 had 3 exit-3 prerequisites (plugin-teardown self-test fixture, skill-examples client-react dts, dual-build-cjs-loads), cleared by fetching the fixture and a full pnpm build (119/119) after merging main. eslint --no-inline-config packages/cloud-connection/src exit 0; check-nul-bytes exit 0. PR check-runs: in_progress at report time (not waited on).",
"dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
"line_budget": "+367 / -33 over 6 files (src 3, test 1, doc 1, changeset 1); test +142",
"files_changed": [
".changeset/31604-cloud-credential-follows-os-home.md",
"content/docs/deployment/cloud-connection.mdx",
"packages/cloud-connection/src/cloud-connection-plugin.ts",
"packages/cloud-connection/src/connection-credential-store.test.ts",
"packages/cloud-connection/src/connection-credential-store.ts",
"packages/cloud-connection/src/local-manifest-source.ts"
],
"deviations": [
"local-manifest-source.ts is outside the claim's file surface: the existing OS_HOME parser was extracted into readOsHome() (ledger answers unchanged, its suites green) to honour ruling 1's single reading.",
"Delta asked to update code doc lines naming the old path; two of them sit in connection-credential-ledger.ts and sys-cloud-binding-credential.object.ts, which ruling 3 forbids touching, so they were left and named in the PR's Acceptance notes.",
"Ablation legs A3 and A6 were first sent as empty --replacement, which ablation-replace refused before running (no-op attempt, file restored); re-run with --delete.",
"A first background run wrote a 5-byte pid file to /pkg.pid (unset variable in a backgrounded chain); removal was refused by the container's safety check, so it remains in this ephemeral container only.",
"Session-context asked to set os-steve as PR assignee/reviewer via MCP writes; the standard order forbids MCP writes and os-dev forbids PR assignees, so not done. PR activity subscription not taken (os-dev: dispatch contract overrides it)."
],
"mcp_calls": "4 — mcp__github__issue_read x3 (get, get_comments x2), mcp__github__pull_request_read x1 (get); no write tools",
"api_writes": "2 — pr_create via fleet-write relay (#31708), this os-dev-report comment via post-stamped.mjs",
"open_questions": [
{
"question": "New public members on the exported ConnectionCredentialStore class: `legacyPath` and `moveLegacyFile()`; module-level (not in the package index) `resolveConnectionCredentialPath`, `readOsHome`, `ConnectionCredentialLocation`, `LegacyCredentialMove`, `OsHomeReading`. Seat's call on the names.",
"options": [
"A keep as named (mirrors LocalManifestSource.legacyDir / adoptLegacyEntries / resolveInstalledPackagesDir)",
"B rename before landing"
],
"recommendation": "A, because the pair mirrors the ledger's existing shape one-to-one."
},
{
"question": "Ruling 1 alternative: instead of extracting readOsHome(), the credential path could be derived as dirname(resolveInstalledPackagesDir().dir)/cloud-connection.json (equivalent in every case, no edit to local-manifest-source.ts).",
"options": [
"A keep the extraction (explicit, one reader)",
"B derive from the ledger dir and revert local-manifest-source.ts"
],
"recommendation": "A, because coupling the credential to the ledger directory's parent is clever rather than clear; the extraction is behaviour-neutral for the ledger."
},
{
"question": "The move is a MOVE per the ruling, so an OS_HOME pointed at a throwaway directory (not an os dev --fresh home) takes the project's credential with it, unlike the ledger, which copies and leaves its files. Acceptable?",
"options": [
"A keep the move (ruling 2)",
"B copy and leave the old file"
],
"recommendation": "A, as ruled; the fresh-home case, the realistic throwaway, is excluded and pinned."
}
],
"security_findings": "none",
"out_of_scope_findings": [
"carrier: none · noted, not filed — two stale header comments (connection-credential-ledger.ts, sys-cloud-binding-credential.object.ts) name the old single-node path; they are true only with OS_HOME unset; in the PR's Acceptance notes."
]
}
With OS_HOME set, a single node's cloud binding credential lives at OS_HOME/cloud-connection.json, so a container recreate keeps the binding. With OS_HOME unset the path is unchanged; an explicit credentialPath still wins.
OS_HOME is read once for this package's on-disk state (readOsHome, shared with the install ledger). The first boot moves an old working-directory file once (0600, one log line); both present → the OS_HOME file wins and the old one is untouched; os dev --fresh moves nothing.
The multi-replica shared store is unchanged (comment lines only). Patch changeset for cloud-connection; the deployment doc states the new path.
Activity
objectstack-fleet commentedon Nov 8, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: lane
domain:cloud→domain:server· grade kept · 2026-11-08T17:03Z⛔ Not a claim.
repo:cloudis used in two senses: 6 open cards are the cloud PM's whole (withdomain:cloud), 20 are objectstack-lane work that cloud asked for. Which one does the label mean? #31154 ruling A,repo:cloudanddomain:cloudapply only to fixes in the cloud repository. This fix is inpackages/cloud-connection, an objectstack server package (references/lanes/server.md), so the lane isdomain:server. The card cites its cloud parent, approvals: 审批请求行不暴露节点的lockRecord,客户端只能假定「有 pending 就是锁死」 #3814.Generated by Claude Code
objectstack-fleet commentedon Nov 8, 2026
domain:server#2 · os-steve · session_01ADHkmQKZzp4DLtqiCkZZRM
Claim: PM loop round 5 · 2026-11-08T21:31Z
Session:
session_01ADHkmS9T6n1TQU1WRxmFdb(card thread, dev) · seatsession_01ADHkmQKZzp4DLtqiCkZZRMAccount:
os-steveBranch:
claude/project-thread-3o188qWorktree:
objectstack-issue-31604Domain:
domain:serverSeat:
domain:server#2File surface:
packages/cloud-connection/src/connection-credential-store.ts(+cloud-connection-plugin.tspath wiring only if needed), tests,content/docs/deployment/cloud-connection.mdxif it states the path, root.changeset/. ⛔ Not the multi-replica shared store (connection-credential-ledger.ts,connection-credential-preboot.ts,sys-cloud-binding-credential.object.ts); nopackages/spec/**,ui/**,helm/Container & model: M,
mode:thread,model: default tier,effort: highClause-②: no (unless a new env variable ships: then yes (widening), declared first)
Metadata-protocol: none
Responsibility: epic #3814 card 0 single-node defect; maintainer D2 「现在修,按普通 bug 卡」, not bound by R3
Thread-read: 6724404663
Premise: holds as filed, read at claim on
origin/main5d1382a5:DEFAULT_CONNECTION_CREDENTIAL_PATHresolves againstprocess.cwd()(connection-credential-store.tsL60), notOS_HOMESerial constraints cleared: no open PR touches
connection-credential-store.ts; #31554's shared store (PR #31584) merged and stays untouchedScope: Fixes #31604. Resolve the credential through the install-local ledger's existing
OS_HOMEreading (local-manifest-source.ts), one resolver; explicit override kept with its precedence; boot migration MOVES the old cwd file when the new one is absent (0600 kept, one log line), both present → new wins, old untouched;OS_HOMEunset → path unchanged;os dev --freshkeeps it in the ephemeral home. Order:/mnt/project-files/server-seat2/standard-order.md+/mnt/project-files/server-seat2/31604-delta.md. Summons: the maintainer's 「并发都是5」 (project chat, 2026-11-08T16:13Z).objectstack-fleet commentedon Nov 8, 2026
domain:server#2 · os-steve · session_01ADHkmS9T6n1TQU1WRxmFdb
os-dev-report
{ "issue": 31604, "status": "done", "branch": "claude/project-thread-3o188q", "pr": "https://github.com/objectstack-ai/objectstack/pull/31708", "session": "session_01ADHkmS9T6n1TQU1WRxmFdb", "premise_still_valid": true, "summary": "A single node's cloud binding credential now resolves to OS_HOME/cloud-connection.json when OS_HOME is set (unchanged CWD/.objectstack/cloud-connection.json when unset), through readOsHome(), the install ledger's existing OS_HOME reading extracted so both resolvers share it. CloudConnectionPlugin.init() moves an old working-directory file once before the ledger is chosen (staged 0600 + rename, old removed after; one info line naming both paths), both present keeps the OS_HOME file and leaves the old one untouched, an os dev --fresh home moves nothing, and an explicit credentialPath keeps its precedence. Shared multi-replica store files untouched; doc page and patch changeset updated.", "tests": "Red on main: the 3 source files restored from 5d1382a5 with the new tests: Tests 4 failed | 17 passed (21) (control + explicit-path green on main, as expected). Green on d6663391: pnpm --filter @objectstack/cloud-connection typecheck && vitest run --maxWorkers=2 -> Test Files 80 passed (80), Tests 1114 passed (1114), lock VERDICT command-exit 0. Ablation (scripts/ablation-replace.mjs, src-imported tests, no dist leg), 6 legs each red then restored (blob == HEAD, git diff HEAD empty): A1 both-present guard, A2 fresh-home exclusion, A3 old-file removal, A4 0600 staging mode, A5 explicit-path precedence, A6 plugin boot move.", "gates": "d6663391: dispatch-gates --commands derived 99; all 99 run, all exit 0; --ran: 99 derived, 99 run, 0 NOT-MEASURED, 0 UNRUN. Round 1 on dd1ef0a6 had 3 exit-3 prerequisites (plugin-teardown self-test fixture, skill-examples client-react dts, dual-build-cjs-loads), cleared by fetching the fixture and a full pnpm build (119/119) after merging main. eslint --no-inline-config packages/cloud-connection/src exit 0; check-nul-bytes exit 0. PR check-runs: in_progress at report time (not waited on).", "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm", "line_budget": "+367 / -33 over 6 files (src 3, test 1, doc 1, changeset 1); test +142", "files_changed": [ ".changeset/31604-cloud-credential-follows-os-home.md", "content/docs/deployment/cloud-connection.mdx", "packages/cloud-connection/src/cloud-connection-plugin.ts", "packages/cloud-connection/src/connection-credential-store.test.ts", "packages/cloud-connection/src/connection-credential-store.ts", "packages/cloud-connection/src/local-manifest-source.ts" ], "deviations": [ "local-manifest-source.ts is outside the claim's file surface: the existing OS_HOME parser was extracted into readOsHome() (ledger answers unchanged, its suites green) to honour ruling 1's single reading.", "Delta asked to update code doc lines naming the old path; two of them sit in connection-credential-ledger.ts and sys-cloud-binding-credential.object.ts, which ruling 3 forbids touching, so they were left and named in the PR's Acceptance notes.", "Ablation legs A3 and A6 were first sent as empty --replacement, which ablation-replace refused before running (no-op attempt, file restored); re-run with --delete.", "A first background run wrote a 5-byte pid file to /pkg.pid (unset variable in a backgrounded chain); removal was refused by the container's safety check, so it remains in this ephemeral container only.", "Session-context asked to set os-steve as PR assignee/reviewer via MCP writes; the standard order forbids MCP writes and os-dev forbids PR assignees, so not done. PR activity subscription not taken (os-dev: dispatch contract overrides it)." ], "mcp_calls": "4 — mcp__github__issue_read x3 (get, get_comments x2), mcp__github__pull_request_read x1 (get); no write tools", "api_writes": "2 — pr_create via fleet-write relay (#31708), this os-dev-report comment via post-stamped.mjs", "open_questions": [ { "question": "New public members on the exported ConnectionCredentialStore class: `legacyPath` and `moveLegacyFile()`; module-level (not in the package index) `resolveConnectionCredentialPath`, `readOsHome`, `ConnectionCredentialLocation`, `LegacyCredentialMove`, `OsHomeReading`. Seat's call on the names.", "options": [ "A keep as named (mirrors LocalManifestSource.legacyDir / adoptLegacyEntries / resolveInstalledPackagesDir)", "B rename before landing" ], "recommendation": "A, because the pair mirrors the ledger's existing shape one-to-one." }, { "question": "Ruling 1 alternative: instead of extracting readOsHome(), the credential path could be derived as dirname(resolveInstalledPackagesDir().dir)/cloud-connection.json (equivalent in every case, no edit to local-manifest-source.ts).", "options": [ "A keep the extraction (explicit, one reader)", "B derive from the ledger dir and revert local-manifest-source.ts" ], "recommendation": "A, because coupling the credential to the ledger directory's parent is clever rather than clear; the extraction is behaviour-neutral for the ledger." }, { "question": "The move is a MOVE per the ruling, so an OS_HOME pointed at a throwaway directory (not an os dev --fresh home) takes the project's credential with it, unlike the ledger, which copies and leaves its files. Acceptable?", "options": [ "A keep the move (ruling 2)", "B copy and leave the old file" ], "recommendation": "A, as ruled; the fresh-home case, the realistic throwaway, is excluded and pinned." } ], "security_findings": "none", "out_of_scope_findings": [ "carrier: none · noted, not filed — two stale header comments (connection-credential-ledger.ts, sys-cloud-binding-credential.object.ts) name the old single-node path; they are true only with OS_HOME unset; in the PR's Acceptance notes." ] }Stamped 2026-11-08T22:33Z.
Generated by Claude Code
objectstack-fleet commentedon Nov 8, 2026
domain:server#2 · os-steve · session_01ADHkmQKZzp4DLtqiCkZZRM
ACCEPT → PR #31708 comment 6728385054 · head
de5b1aa9· 2026-11-08T23:03Z · landing: pr_ready + automerge_enableobjectstack-fleet commentedon Nov 8, 2026
domain:server#2 · os-steve · session_01ADHkmQKZzp4DLtqiCkZZRM
落地 · 2026-11-08T23:23Z
35a9c694(ACCEPT 6728385054; Clause-② no, no governed path, no contract review face)Fixes #31604)Landed:
OS_HOMEset, a single node's cloud binding credential lives atOS_HOME/cloud-connection.json, so a container recreate keeps the binding. WithOS_HOMEunset the path is unchanged; an explicitcredentialPathstill wins.OS_HOMEis read once for this package's on-disk state (readOsHome, shared with the install ledger). The first boot moves an old working-directory file once (0600, one log line); both present → theOS_HOMEfile wins and the old one is untouched;os dev --freshmoves nothing.