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
install-local: DELETE /api/v1/marketplace/install-local/:manifestId removes the ledger entry before asking the uninstall refusal, so a refused base (its <base>.custom installed, or an ADR-0029 extender) answers 200 and is left half-removed #28511
Filing gate: ① product defect with a named location and reach · child of epic #28236 (ADR-0146 D3) · found while measuring #28408's Wanted 3 (os-dev-report 6553094234) and judged a separate card by the contract review on PR #28506 (CONTRACT_REVIEW_TIER, PASS). ⛔ Not a claim.
Part of #28236 · lane: packages/cloud-connection (the marketplace install-local plugin) · size: S.
Business reason. An administrator uninstalls a marketplace-installed HotCRM while hotcrm.custom holds their customizations. The platform must refuse and name the pair (ADR-0146 D3: a refused uninstall leaves no half-removed base). On this door, today, the platform says 200. The base stays registered until restart, and after the restart the base is gone and the pair is stranded. The customer loses the base under their customizations, with no refusal sentence telling them why.
Reach (measured).DELETE /api/v1/marketplace/install-local/:manifestId on a marketplace-installed base whose BASE.custom is installed returned 200 success. The test was a scratch test over the real MarketplaceInstallLocalPlugin and SchemaRegistry at d79d71aa7, recorded under PR #28506 Verification and Wanted 3. The response:
removes the ledger entry;
reports a failed registry.uninstallPackage cleanup inside the 200, without naming the pair;
The epic's two-minute check 「卸载 HotCRM 被拒并点名 hotcrm.custom」 is recorded as met on a marketplace-installed HotCRM only after this lands.
Readers who act. A domain:server lane dev, after #28506 merges (the predicate's pair cause lives there).
Dedupe. Searched issues: install-local uninstall ledger (66 hits; #21490 and #27395 closed, about leftovers after a successful uninstall, not this ordering), handleUninstall assertPackageUninstallable (1 hit, #28408), marketplace uninstall half-removed (6 hits, none on this). No existing card covers it.
Claim: PM loop round 4 · card thread · 2026-11-01T04:58Z
Session: session_0186vfjHHMsRXC9EUT9hTFMo (the seat); the card thread is session_01FEkQ5pQqiDztRTRRHCtcay
Account: os-project-manager (the seat's linked user; the card's assignee)
Branch: claude/project-thread-q0reft (the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)
Worktree: the card thread's own container checkout
Domain: domain:server
Seat: domain:server#2
Ruling-ref: the maintainer, 2026-11-01T03:46Z: 「server 车道现在就是你负责,任务有一大堆啊,都可以派,包括 p3」, with 2026-10-26T10:29Z 「p1 p2 处理完了,就可以处理 p3」. Card #28511 Wanted 1–2: handleUninstall asks registry.assertPackageUninstallable(id) before ledger.remove; on refusal 409 RESOURCE_CONFLICT with its sentence and nothing moves; a non-refused uninstall keeps #21576's ledger-first order. ⛔ No reversal of the ledger-then-withdraw order, ⛔ no second predicate.
File surface: packages/cloud-connection/src/marketplace-install-local-plugin.ts (handleUninstall), packages/cloud-connection/src/marketplace-install-local-uninstall-withdrawal.test.ts (the re-pin), tests, and a changeset.
Container & model: M, mode:thread, model: opus (--tier names no path mandate: PM's call, default), effort: high
Clause-②: no (expected: narrowing — an uninstall answered 200 today is refused 409; a contract review at CONTRACT_REVIEW_TIER is owed before the queue)
Metadata-protocol: none (expected; the dev measures and declares)
Responsibility: the code is the marketplace install-local plugin's uninstall door · the platform path is the package ledger and the running registry it withdraws from · reached by an administrator uninstalling a marketplace-installed base whose .custom pair or extender is installed
Thread-read: none
Premise: holds (read 2026-11-01T04:58Z at origin/main: handleUninstall calls ledger.remove before any uninstall predicate, and #28506 put the pair cause in assertPackageUninstallable); the dev re-measures first
Serial constraints cleared: PR #28506 merged and #28408 closed (the card's Blocked-by); no open PR touches packages/cloud-connection/src/marketplace-install-local* at this read
Resume-from: none — fresh from origin/main
State: → pm:dispatched, assigned to os-project-manager, recorded by this claim.
domain:server#2 · os-project-manager · session_01FEkQ5pQqiDztRTRRHCtcay
os-dev-report
{
"issue": 28511,
"status": "done",
"branch": "claude/project-thread-q0reft",
"pr": "#28571",
"session": "session_01FEkQ5pQqiDztRTRRHCtcay",
"premise_still_valid": true,
"summary": "handleUninstall now asks registry.assertPackageUninstallable(manifestId) (the one predicate uninstallPackage asks; it carries the ADR-0029 extender cause and, since #28506, the .custom pair cause) after the capability, 404 and walled gates and before ledger.remove. On the registry's coded refusal (RESOURCE_CONFLICT / 409) the door answers 409 RESOURCE_CONFLICT with the refusal's own sentence in the file's existing { success:false, error:{ code, message } } envelope, and nothing moves: ledger entry, registration, pair and object contributions stay, no withdrawal, cleanup or re-sync runs. A non-refused uninstall keeps #21576's ledger-first order; a host with no objectql registry or a registry without the method is not asked (behaviour as before); a non-coded throw is left to the withdrawal. Measured ids: the withdrawal uninstalls exactly one id, the manifest id, so that one id is asked. Premise held at 35b178b (red-first reproduced the card's 200). Card assignee untouched; claim 6553661530 names this branch.",
"tests": "Red first: the new #28511 cases run with the plugin source restored to base 35b178b -> (a) pair and (b) extender red, each "expected 200 to be 409" with the card's 200 body (failed registry.uninstallPackage outcome in cleanups); 10 others green. Green at HEAD: pnpm --filter @objectstack/cloud-connection exec vitest run --maxWorkers=2 src/marketplace-install-local-uninstall-withdrawal.test.ts -> Tests 12 passed (12). Ablation: node scripts/ablation-replace.mjs, anchor "registry.assertPackageUninstallable(manifestId);" -> "(globalThis as any).ABL28511 = manifestId;": anchor 1 -> 0, blob d582fa927604 -> a2d2d49255f6, exactly (a) and (b) red, 10 controls green (incl. ledger-first order on the real registry and the unknown-id 404), restore blob == HEAD d582fa927604, git diff HEAD empty; the suite imports the plugin from src, no build between mutation and run. Package at head 83e5965: pnpm --filter @objectstack/cloud-connection test -> Test Files 63 passed (63), Tests 772 passed (772); typecheck (tsc --noEmit && tsc --noEmit -p tsconfig.test.json) exit 0 at 52e5af7 (the later commit only adds an env key to vitest.config.ts, covered by the head test run).",
"gates": "Local subset at head 83e5965: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack -> 60 families, each exit captured before any pipe, all exit 0; --ran: "✓ dispatch-gates --ran: 60 derived famil(ies) accounted for — 60 run, 0 NOT-MEASURED". First pass (on 52e5af7) had three non-zero, all met before the union: check-plugin-teardown-shape --self-test exit 3 (shallow; git fetch --depth=1 origin 621a487 -> 48 cases pass), check:dual-build-cjs-loads exit 3 (no full build; green after the packages build check:type-check-debt runs), check:registry-log-declared exit 1 (the new real-SchemaRegistry test selects the package via S1; fixed with the gate's own prescription env: { OS_REGISTRY_LOG: 'warn' } in packages/cloud-connection/vitest.config.ts, commit 83e5965). Hand gates: check-commit-card-trailers --range origin/main..HEAD exit 0; eslint --no-inline-config packages/cloud-connection exit 0 (no package lint script). CI on 83e5965 at report time: 3 completed, 18 in_progress, 0 failed (honest in_progress; the seat reads convergence).",
"dogfood_battery": "83e5965d · os-verify-lock: VERDICT command-exit 0 (held 22m52s) · Test Files 363 passed | 3 skipped (366) · Tests 2932 passed | 28 skipped (2960) · no failure (after pnpm --filter '@objectstack/dogfood^...' build, VERDICT command-exit 0)",
"mcp_calls": "0 — no GitHub MCP tool called",
"api_writes": "2 REST writes via the fleet-write relay: pr_create (#28571, draft, no labels, no assignees; read-back "10406 byte(s) sent, 10406 stored — identical", assignees none) and this os-dev-report issue comment on #28511 (post-stamped). Plus 2 git pushes (not REST). Write budget spent: 2 pushes + pr_create + report comment.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: none · noted, not filed — content/docs/deployment/marketplace-handbook.mdx step 9 (Uninstalling) stays true for a successful uninstall but does not mention the new 409 refusal (pair installed / extended object); a one-sentence addition is the seat's call, outside the claim's file surface.",
"carrier: none · noted, not filed — file surface beyond the claim: packages/cloud-connection/vitest.config.ts (OS_REGISTRY_LOG declaration required by check:registry-log-declared); the seat may amend the claim.",
"carrier: PR #28554 · noted — open PR #28554 (#28410, ADR-0146 D2 override write gate, opened after this order) also edits marketplace-install-local-plugin.ts in other hunks (imports, ~L511, ~L1924, ~L2102, ~L3020); not the same work, whichever lands second merges."
]
}
The amendment: the file surface also covers packages/cloud-connection/vitest.config.ts. It declares OS_REGISTRY_LOG: 'warn', which check:registry-log-declared requires once the suite builds a real SchemaRegistry for the red-first and control cases. This is test configuration only, and no governed path is involved.
Landed (Fixes): merged as bca27ad3. DELETE /api/v1/marketplace/install-local/:manifestId now asks the registry's one uninstall predicate before it touches the ledger. An uninstall refused because the base's .custom pair is installed, or because another package extends one of its objects (ADR-0029), answers 409 RESOURCE_CONFLICT with the registry's sentence, and nothing moves. @objectstack/cloud-connection patch.
Activity
objectstack-fleet commentedon Nov 1, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: 错的必须被响亮拒绝并给处方 | none | P②
Triage: p1 ·
domain:server·area:studio·pm:queue· 2026-11-01T04:56Z⛔ Not a claim.
pm:queuewithoutpm:epic.Blocked-by: #28408is closed, and PR fix(objectql)!: uninstalling a base package is refused while its paired .custom package is installed, naming the pair (409) #28506, which carries the pair cause inassertPackageUninstallable, merged at 2026-11-01T04:34Z. So the card goes straight topm:queue.packages/cloud-connectionis a framework package in this repo. No change in the cloud repo is needed, so the lane isdomain:server.Generated by Claude Code
objectstack-fleet commentedon Nov 1, 2026
domain:server#2 · os-project-manager · session_0186vfjHHMsRXC9EUT9hTFMo
Claim: PM loop round 4 · card thread · 2026-11-01T04:58Z
Session:
session_0186vfjHHMsRXC9EUT9hTFMo(the seat); the card thread issession_01FEkQ5pQqiDztRTRRHCtcayAccount:
os-project-manager(the seat's linked user; the card's assignee)Branch:
claude/project-thread-q0reft(the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)Worktree: the card thread's own container checkout
Domain:
domain:serverSeat:
domain:server#2Ruling-ref: the maintainer, 2026-11-01T03:46Z: 「server 车道现在就是你负责,任务有一大堆啊,都可以派,包括 p3」, with 2026-10-26T10:29Z 「p1 p2 处理完了,就可以处理 p3」. Card #28511 Wanted 1–2:
handleUninstallasksregistry.assertPackageUninstallable(id)beforeledger.remove; on refusal 409RESOURCE_CONFLICTwith its sentence and nothing moves; a non-refused uninstall keeps #21576's ledger-first order. ⛔ No reversal of the ledger-then-withdraw order, ⛔ no second predicate.File surface:
packages/cloud-connection/src/marketplace-install-local-plugin.ts(handleUninstall),packages/cloud-connection/src/marketplace-install-local-uninstall-withdrawal.test.ts(the re-pin), tests, and a changeset.Container & model:
M,mode:thread,model: opus(--tiernames no path mandate: PM's call, default),effort: highClause-②: no (expected: narrowing — an uninstall answered 200 today is refused 409; a contract review at
CONTRACT_REVIEW_TIERis owed before the queue)Metadata-protocol: none (expected; the dev measures and declares)
Responsibility: the code is the marketplace install-local plugin's uninstall door · the platform path is the package ledger and the running registry it withdraws from · reached by an administrator uninstalling a marketplace-installed base whose
.custompair or extender is installedThread-read: none
Premise: holds (read 2026-11-01T04:58Z at
origin/main:handleUninstallcallsledger.removebefore any uninstall predicate, and #28506 put the pair cause inassertPackageUninstallable); the dev re-measures firstSerial constraints cleared: PR #28506 merged and #28408 closed (the card's Blocked-by); no open PR touches
packages/cloud-connection/src/marketplace-install-local*at this readResume-from: none — fresh from
origin/mainState: →
pm:dispatched, assigned toos-project-manager, recorded by this claim.Generated by Claude Code
objectstack-fleet commentedon Nov 1, 2026
domain:server#2 · os-project-manager · session_01FEkQ5pQqiDztRTRRHCtcay
os-dev-report
{
"issue": 28511,
"status": "done",
"branch": "claude/project-thread-q0reft",
"pr": "#28571",
"session": "session_01FEkQ5pQqiDztRTRRHCtcay",
"premise_still_valid": true,
"summary": "handleUninstall now asks registry.assertPackageUninstallable(manifestId) (the one predicate uninstallPackage asks; it carries the ADR-0029 extender cause and, since #28506, the .custom pair cause) after the capability, 404 and walled gates and before ledger.remove. On the registry's coded refusal (RESOURCE_CONFLICT / 409) the door answers 409 RESOURCE_CONFLICT with the refusal's own sentence in the file's existing { success:false, error:{ code, message } } envelope, and nothing moves: ledger entry, registration, pair and object contributions stay, no withdrawal, cleanup or re-sync runs. A non-refused uninstall keeps #21576's ledger-first order; a host with no objectql registry or a registry without the method is not asked (behaviour as before); a non-coded throw is left to the withdrawal. Measured ids: the withdrawal uninstalls exactly one id, the manifest id, so that one id is asked. Premise held at 35b178b (red-first reproduced the card's 200). Card assignee untouched; claim 6553661530 names this branch.",
"tests": "Red first: the new #28511 cases run with the plugin source restored to base 35b178b -> (a) pair and (b) extender red, each "expected 200 to be 409" with the card's 200 body (failed registry.uninstallPackage outcome in cleanups); 10 others green. Green at HEAD: pnpm --filter @objectstack/cloud-connection exec vitest run --maxWorkers=2 src/marketplace-install-local-uninstall-withdrawal.test.ts -> Tests 12 passed (12). Ablation: node scripts/ablation-replace.mjs, anchor "registry.assertPackageUninstallable(manifestId);" -> "(globalThis as any).ABL28511 = manifestId;": anchor 1 -> 0, blob d582fa927604 -> a2d2d49255f6, exactly (a) and (b) red, 10 controls green (incl. ledger-first order on the real registry and the unknown-id 404), restore blob == HEAD d582fa927604, git diff HEAD empty; the suite imports the plugin from src, no build between mutation and run. Package at head 83e5965: pnpm --filter @objectstack/cloud-connection test -> Test Files 63 passed (63), Tests 772 passed (772); typecheck (tsc --noEmit && tsc --noEmit -p tsconfig.test.json) exit 0 at 52e5af7 (the later commit only adds an env key to vitest.config.ts, covered by the head test run).",
"gates": "Local subset at head 83e5965: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack -> 60 families, each exit captured before any pipe, all exit 0; --ran: "✓ dispatch-gates --ran: 60 derived famil(ies) accounted for — 60 run, 0 NOT-MEASURED". First pass (on 52e5af7) had three non-zero, all met before the union: check-plugin-teardown-shape --self-test exit 3 (shallow; git fetch --depth=1 origin 621a487 -> 48 cases pass), check:dual-build-cjs-loads exit 3 (no full build; green after the packages build check:type-check-debt runs), check:registry-log-declared exit 1 (the new real-SchemaRegistry test selects the package via S1; fixed with the gate's own prescription env: { OS_REGISTRY_LOG: 'warn' } in packages/cloud-connection/vitest.config.ts, commit 83e5965). Hand gates: check-commit-card-trailers --range origin/main..HEAD exit 0; eslint --no-inline-config packages/cloud-connection exit 0 (no package lint script). CI on 83e5965 at report time: 3 completed, 18 in_progress, 0 failed (honest in_progress; the seat reads convergence).",
"dogfood_battery": "83e5965d · os-verify-lock: VERDICT command-exit 0 (held 22m52s) · Test Files 363 passed | 3 skipped (366) · Tests 2932 passed | 28 skipped (2960) · no failure (after pnpm --filter '@objectstack/dogfood^...' build, VERDICT command-exit 0)",
"mcp_calls": "0 — no GitHub MCP tool called",
"api_writes": "2 REST writes via the fleet-write relay: pr_create (#28571, draft, no labels, no assignees; read-back "10406 byte(s) sent, 10406 stored — identical", assignees none) and this os-dev-report issue comment on #28511 (post-stamped). Plus 2 git pushes (not REST). Write budget spent: 2 pushes + pr_create + report comment.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: none · noted, not filed — content/docs/deployment/marketplace-handbook.mdx step 9 (Uninstalling) stays true for a successful uninstall but does not mention the new 409 refusal (pair installed / extended object); a one-sentence addition is the seat's call, outside the claim's file surface.",
"carrier: none · noted, not filed — file surface beyond the claim: packages/cloud-connection/vitest.config.ts (OS_REGISTRY_LOG declaration required by check:registry-log-declared); the seat may amend the claim.",
"carrier: PR #28554 · noted — open PR #28554 (#28410, ADR-0146 D2 override write gate, opened after this order) also edits marketplace-install-local-plugin.ts in other hunks (imports, ~L511, ~L1924, ~L2102, ~L3020); not the same work, whichever lands second merges."
]
}
Generated by Claude Code
objectstack-fleet commentedon Nov 1, 2026
domain:server#2 · os-project-manager · session_0186vfjHHMsRXC9EUT9hTFMo
2026-11-01T06:03Z · claim
6553661530amended (file surface)packages/cloud-connection/vitest.config.ts. It declaresOS_REGISTRY_LOG: 'warn', whichcheck:registry-log-declaredrequires once the suite builds a realSchemaRegistryfor the red-first and control cases. This is test configuration only, and no governed path is involved..custommay declare; undeclared same names and a dependent'soverridesare refused at publish,os validateand install #28410) editsmarketplace-install-local-plugin.tsin other hunks. Whichever lands second mergesmainin.Generated by Claude Code
objectstack-fleet commentedon Nov 1, 2026
domain:server#2 · os-project-manager · session_0186vfjHHMsRXC9EUT9hTFMo
Landing record · #28511 · PR #28571
domain:serverseat 2 ·session_0186vfjHHMsRXC9EUT9hTFMo· 2026-11-01T06:38Zgit ls-remote origin 'refs/heads/gh-readonly-queue/main/pr-28571-*'git merge-base --is-ancestor bca27ad3 origin/maingit grep -c assertPackageUninstallable origin/main -- packages/cloud-connection/src/marketplace-install-local-plugin.tsbca27ad3.DELETE /api/v1/marketplace/install-local/:manifestIdnow asks the registry's one uninstall predicate before it touches the ledger. An uninstall refused because the base's.custompair is installed, or because another package extends one of its objects (ADR-0029), answers 409RESOURCE_CONFLICTwith the registry's sentence, and nothing moves.@objectstack/cloud-connectionpatch.SchemaRegistry. A live run on a marketplace-installed HotCRM was not measured by this seat.mainin.Release: claim
6553661530(amended6554221492;session_0186vfjHHMsRXC9EUT9hTFMo, branchclaude/project-thread-q0reft) · landed.pm:dispatchedremoved; assignee cleared; the card closed on merge.Generated by Claude Code