Repository navigation
Description
epic:#3600#1 · hotlong · session_01Q4EjjPXwX1YfHyKJUh4agu
Filing-gate category: ② maintainer-directed task. Maintainer, verbatim (Claude Code session, 2026-11-06): 「有没有可能在安装的时候加上前缀,避免重复?」, then, on the seat's assessment and its default-namespace proposal: 「同意,裸名的问题立决策卡」. Follow-up of the scenario-library epic objectstack-ai/cloud#3600 (gallery apps tried in one click from ObjectOS cloud).
Problem (customer-visible)
A customer who installs a starter template as their own copy (unmanaged) gets the template author's namespace forever: object names are identities and are never renamed after install (ADR-0129). A gallery app named democrm_account stays democrm_account in the customer's production data, and a second template sharing a namespace cannot be installed at all (422 namespace conflict).
Readings (origin/main 802c98c)
- The rename engine already exists.
duplicatePackage(packages/metadata-protocol/src/protocol-package-commits.ts:122onward) takestargetNamespaceand re-namespaces a stored package: object renames with whole-identifier matching on both sides, bare names rewritten only at object-reference properties (the derived indexpackages/metadata-protocol/src/reference-sites.ts), record-key renames (permission.objects), view item names<object>.<key>and whole-valuetargets, refused collisions (never merged, never an invented name), and a prefix swap for non-object items that carry the source prefix. It carries fixes [finding] the HTTP install door readsmanifest.idpositionally and never parses the body throughManifestSchema— POST /packages answers 201 to ids thatMANIFEST_ID_PATTERN(spec,defineStack,os build, the publish face) refuses #19417, [finding] duplicatePackage validates its DERIVED namespace but takes an explicit targetNamespace raw — and that value is spliced into every copied object name, which the object-name regex then refuses #19577, fix(metadata-protocol)!: a save into a second package of an object a runtime-authored row owns is refused (ADR-0029 D9.9) #23510, Active metadata save stores an object without its package namespace prefix that publish-drafts then refuses (NAMESPACE_PREFIX) #24547 and the objectqlprotocol-duplicate-package-*test battery. - The unmanaged import keeps the source namespace.
planUnmanagedImport(packages/metadata-protocol/src/package-unmanaged-install.ts:435-485) sets the base's namespace to the source's, per ruling Q3 = A on feat(spec,objectql,cli): the template install mode — a package copied once into the environment ledger, fully editable, refused on shared-database multi-tenant postures (ADR-0131 D6) #15213 ("source namespace kept"; its reason was that copying into an existing base breaks that base's namespace on export, not a ban on renaming). - ADR-0048 §3.5 records rename-on-install as deferred future work; this card is that work.
Ask
- One engine, two callers. Extract the duplicate's rename logic into a pure function over item bodies and use it from both
duplicatePackageand the unmanaged import. ⛔ No second copy of the logic. targetNamespaceon the unmanaged import and the install-local door (MarketplaceInstallLocalPlugin), optional; absent keeps today's behaviour (source namespace). Validated byManifestSchema.shape.namespace; reserved namespaces refused.- Apply the rename after view-container expansion; name copied translations
<targetNamespace>_<locale>and rename object keys inside them. - Refuse before any write when a renamed object or item name breaks the item-name pattern or the storage identifier limit, naming the item.
- Record the mapping (
sourceNamespace→targetNamespace) in the copy's provenance next toinstalled_from. - The existing same-name refusal (
unmanagedImportCollisions, 409) runs on the renamed names. - Q6 on feat(spec,objectql,cli): the template install mode — a package copied once into the environment ledger, fully editable, refused on shared-database multi-tenant postures (ADR-0131 D6) #15213 stands: a later managed install of the same source package stays refused (seat default; the maintainer may reopen).
- Amend ADR-0048 §3.5 (rename-on-install, unmanaged only; a managed install is never renamed, it keeps its upgrade channel) and note the Q3 extension on ADR-0131 D6.
- Tests: a parity test (the same package through
duplicatePackageand through the import with onetargetNamespaceyields identical items), the duplicate battery replayed through the import, and one test per refusal.
Out of scope: seed data (not copied by the import today); the hosted-cloud wiring and the default namespace (objectstack-ai/cloud#3624, cloud card filed with this one); renaming bare non-object names (the gallery rule makes gallery items carry the prefix; the platform-wide question is a separate decision card).
Reader: the domain:engine lane seat, from pm:queue.
Generated by Claude Code
Activity
objectstack-fleet commentedon Nov 6, 2026
epic:#3600#1 · hotlong · session_01Q4EjjPXwX1YfHyKJUh4agu
Routing audit (maintainer direct-dispatch channel):
domain:engine+area:devpath+priority:p1were set at filing by the epic PM of objectstack-ai/cloud#3600, not by triage. The landing paths arepackages/metadata-protocol(the unmanaged import and the duplicate-package engine) and@objectstack/cloud-connection(the install-local door). The ruling is recorded on objectstack-ai/cloud#3600, comment 6676044775. The card is inpm:queuefor thedomain:enginelane, not in the epic subtree.objectstack-fleet commentedon Nov 6, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: audit ·
domain:engine→domain:server· p1 kept · 2026-11-06T03:53Z⛔ Not a claim.
domain:engineis a retired alias ofdomain:server. Rename-on-install for unmanaged copies takes p1 as filed, with its ADR-0048 §3.5 amendment (Tier H).Generated by Claude Code
objectstack-fleet commentedon Nov 6, 2026
domain:server#1 · os-elon-musk · session_016WV4cytLmJxfAmgPVjUbbW
Claim: PM loop · 2026-11-06T04:05Z
Session:
session_016WV4cytLmJxfAmgPVjUbbWAccount:
os-elon-musk(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-30794-unmanaged-install-target-namespace(placeholder; the card thread's harness-pinned branch replaces it by amendment before any push)Worktree:
objectstack-issue-30794Domain:
domain:serverSeat:
domain:server#1File surface: PR A (code):
packages/metadata-protocol/**(the rename engine extracted fromduplicatePackageinto one pure function over item bodies,planUnmanagedImportand the unmanaged import, provenance, their tests and the objectqlprotocol-duplicate-package-*battery replayed through the import),packages/cloud-connection/**(theMarketplaceInstallLocalPlugindoor's optionaltargetNamespace),packages/cli/src/commands/package/install.tsonly if the door's flag surfaces there,content/docs/**for the new option, one root.changeset/file. PR B (Tier H, after PR A):docs/adr/0048-*.md§3.5 amendment and the ADR-0131 D6 note. ⛔packages/spec/**(stop and ask: if the import's request shape lives in spec, the seat asks the spec seat for consent first),ui/**,skills/**,.claude/**, the hosted-cloud wiring and default namespace (objectstack-ai/cloud#3624). Stop on breach and explain in the reportContainer & model:
L,mode:card-thread(p1),model: default,effort: xhigh; PR B's contract review atCONTRACT_REVIEW_TIERClause-②: yes (widening) (an optional
targetNamespaceon the import and the install-local door; absent keeps today's behaviour). The dev re-derives it from the diffMetadata-protocol: re-derived from the diff by the dev (expected
noneunless a spec schema moves)Responsibility: rename-on-install for unmanaged copies (
packages/metadata-protocol,packages/cloud-connection) | the rename engine exists forduplicatePackageonly | a customer installing a gallery template as their own copy, who today keeps the author's namespace forever and cannot install a second template that shares itThread-read: none
Premise: holds per the card's readings on
origin/main(duplicatePackageatprotocol-package-commits.ts,planUnmanagedImportatpackage-unmanaged-install.ts, ADR-0048 §3.5 deferred), the maintainer's direction quoted in the card, the epic ruling on objectstack-ai/cloud#3600 (comment 6676044775), and triage 6676414471 (domain:server, p1). Ask items 1 to 9 are the acceptance list; item 7 (Q6 on #15213 stands) is not reopened hereSerial constraints: open PR #30687 (#30518, the protocol
--memberscodemod) touchesprotocol-package-commits.ts(+18 −2). If it lands first, PR A rebases over it; if PR A is ready first, the seat sequences the two. No other open PR touches these pathsobjectstack-fleet commentedon Nov 6, 2026
domain:server#1 · os-elon-musk · session_016WV4cytLmJxfAmgPVjUbbW
Claim: PM loop · 2026-11-06T04:08Z (re-post of 6676579365 with the card thread's branch; it supersedes that comment)
Session:
session_016WV4cytLmJxfAmgPVjUbbW(seat); dev sessionsession_016WV4cxRibYnMTw289qPx3HAccount:
os-elon-musk(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/project-thread-j4jgmg(the card thread's harness-pinned branch, objectstack-ai/objectstack)Worktree:
objectstack-issue-30794Domain:
domain:serverSeat:
domain:server#1File surface: PR A (code):
packages/metadata-protocol/**(the rename engine extracted fromduplicatePackageinto one pure function over item bodies,planUnmanagedImportand the unmanaged import, provenance, their tests and the objectqlprotocol-duplicate-package-*battery replayed through the import),packages/cloud-connection/**(theMarketplaceInstallLocalPlugindoor's optionaltargetNamespace),packages/cli/src/commands/package/install.tsonly if the door's flag surfaces there,content/docs/**for the new option, one root.changeset/file. PR B (Tier H, after PR A):docs/adr/0048-*.md§3.5 amendment and the ADR-0131 D6 note. ⛔packages/spec/**(stop and ask: if the import's request shape lives in spec, the seat asks the spec seat for consent first),ui/**,skills/**,.claude/**, the hosted-cloud wiring and default namespace (objectstack-ai/cloud#3624). Stop on breach and explain in the reportContainer & model:
L,mode:card-thread(p1),model: default,effort: xhigh; PR B's contract review atCONTRACT_REVIEW_TIERClause-②: yes (widening) (an optional
targetNamespaceon the import and the install-local door; absent keeps today's behaviour). The dev re-derives it from the diffMetadata-protocol: re-derived from the diff by the dev (expected
noneunless a spec schema moves)Responsibility: rename-on-install for unmanaged copies (
packages/metadata-protocol,packages/cloud-connection) | the rename engine exists forduplicatePackageonly | a customer installing a gallery template as their own copy, who today keeps the author's namespace forever and cannot install a second template that shares itThread-read: none
Premise: holds per the card's readings on
origin/main(duplicatePackageatprotocol-package-commits.ts,planUnmanagedImportatpackage-unmanaged-install.ts, ADR-0048 §3.5 deferred), the maintainer's direction quoted in the card, the epic ruling on objectstack-ai/cloud#3600 (comment 6676044775), and triage 6676414471 (domain:server, p1). Ask items 1 to 9 are the acceptance list; item 7 (Q6 on #15213 stands) is not reopened hereSerial constraints: open PR #30687 (#30518, the protocol
--memberscodemod) touchesprotocol-package-commits.ts(+18 −2). If it lands first, PR A rebases over it; if PR A is ready first, the seat sequences the two. No other open PR touches these pathsobjectstack-fleet commentedon Nov 6, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
To: domain:server#1
Request: progress · #30794 · idle since 2026-11-06T04:08Z
Generated by Claude Code
4 remaining items
objectstack-fleet commentedon Nov 6, 2026
domain:server#2 · os-tesla · session_01L9qibwymhkbKfSLjxsStXu
os-dev-report
{ "issue": 30794, "status": "done", "branch": "claude/project-thread-j4jgmg", "pr": "https://github.com/objectstack-ai/objectstack/pull/30997", "session": "session_01L9qibwymhkbKfSLjxsStXu", "premise_still_valid": true, "summary": "Conflict refresh of PR A (#30997), resumed at a39d3108: merged origin/main 7d9f1e97 (no rebase, no force) into merge commit cacc28d4. One conflicted file, packages/cloud-connection/src/marketplace-install-local-plugin.ts: main's dryRun refusals (step 0', #30884: non-boolean dryRun 400, dryRun on an unmanaged install 400) and this branch's targetNamespace refusal (managed install + targetNamespace 400) were both added right after the install-mode parse. Both kept, main's block first, this branch's relabelled step 0'' (comment label only). No behaviour was chosen between the sides: the two refusals are disjoint (dryRun is managed-only, targetNamespace unmanaged-only), so any body carrying both is refused 400 INVALID_REQUEST either way, and putting main's first keeps every body main already refuses answering exactly as it does on main. The header docblock and protocol.ts auto-merged. The PR's delta vs main is byte-identical to its delta before the merge (same 14 files, +1968/-361) apart from that one comment label. No generated file conflicted; spec rebuilt and check:generated reports all 14 artifacts current, nothing regenerated.", "tests": "At cacc28d4 after pnpm install --frozen-lockfile and closure builds: pnpm --filter @objectstack/cloud-connection test → Test Files 73 passed (73), Tests 935 passed (935); typecheck (tsc --noEmit + tsconfig.test.json) exit 0. pnpm --filter @objectstack/metadata-protocol test → Test Files 321 passed, 4 skipped (325), Tests 22512 passed, 26 skipped; typecheck exit 0. eslint --no-inline-config over the 10 changed .ts files → exit 0. No ablation: the merge changes no tested behaviour (the PR's refusal pins and main's dryRun pins both pass unchanged).", "gates": "dispatch-gates --commands at cacc28d4 derived 108 families (14 paths, 2329 changed lines, under the 3000 human-merge threshold); every line run with stdin closed. First sweep: 104 exit 0, 4 local-prerequisite refusals, none a finding: check-plugin-teardown-shape --self-test exit 3 (shallow clone lacked fixture commit 621a4876 → fetched it, rerun exit 0); spec check:skill-examples exit 3 (client-react dist absent → built its closure, rerun exit 0, 283 examples type-check); check:dts-closure exit 1 (client-react dist cleaned by my own concurrent build of it → rerun exit 0, 42 packages / 172 declaration files); check:dual-build-cjs-loads exit 3 (no full build → pnpm build, rerun exit 0, 116 entries / 71 packages). Reconciliation dispatch-gates --ran: 108 derived, 108 run, 0 NOT-MEASURED, 0 UNRUN. Spec check:generated exit 0. List saved at server-seat-2/r2026-11-06/out/30997-gates-ran-cacc28d4.list in the project files. PR check-runs on cacc28d4 when read: 57 success, 8 skipped, 0 failing; mergeable true, mergeable_state clean.", "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm (Clause-②: yes (widening), unchanged)", "mcp_calls": "0", "api_writes": "1 — this os-dev-report comment via post-stamped.mjs; plus one git push of cacc28d4 (not a REST write)", "open_questions": [], "security_findings": "none", "out_of_scope_findings": [] }objectstack-fleet commentedon Nov 6, 2026
domain:server#2 · os-tesla · session_01L9qibyvxbrLP4jm4PargoH
Landing · 2026-11-06T20:08Z · PR #30997 MERGED 2026-11-06T19:55Z as
f4d9c094(Part of #30794, PR A): an unmanaged install can take atargetNamespace, renaming the copy under a namespace of its own. The card stays open for PR B (the ADR-0048 §3.5 amendment, the ADR-0131 D6 note and Q6), per the ACCEPT 6689928072; the seat movespm:dispatched→pm:queueso PR B is picked up from the queue.objectstack-fleet commentedon Nov 7, 2026
domain:server#2 · marchtian · session_017ihL2fBJ6xsLWJsnNJGMMx
Ruling recorded · #30794 Q6 · 2026-11-07T15:30Z · the maintainer chose B on a decision card (Fable decision thread, tapped 2026-11-07T15:29:58Z).
targetNamespaceother than the source namespace) does not hold the source namespace, so a later managed install of the same source package is not refused. For an unrenamed copy, Q6 stands exactly as written: step 1f (findEnvironmentBaseHoldingNamespace,marketplace-install-local-plugin.ts) is untouched.targetNamespaceanswer code (VALIDATION_ERRORvs the documentedINVALID_REQUEST).issue-30794/decision-analysis.md.docs/adr/**⇒ Tier H: full Fable review before ACCEPT, and the maintainer merges. The seat dispatches it next. ⛔ Not a claim.objectstack-fleet commentedon Nov 7, 2026
domain:server#2 · marchtian · session_017ihL2fBJ6xsLWJsnNJGMMx
Claim: PM loop round 1 · #30794 the remaining work: PR C (the refused-
targetNamespaceanswer code, the Q6 pin) then PR B (ADR-0048 §3.5 amendment + ADR-0131 D6 note with the Q6 answer) · 2026-11-07T15:37Z · resumes landing 6694353641 (domain:server#2, previous holder) on hand-over 6690317826Session:
session_017ihL2fBJ6xsLWJsnNJGMMxAccount:
marchtian(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/project-thread-p29x8v(the card thread's harness-pinned branch; platform exception toclaude/issue-<n>-<slug>; PR C first, then PR B from the same branch after PR C merges, by a merge oforigin/main, no force push)Worktree:
objectstack-issue-30794Domain:
domain:serverSeat:
domain:server#2File surface: PR C —
packages/metadata-protocol/src/package-unmanaged-install.ts(the twotargetNamespacerefusals becomerefusal('INVALID_REQUEST', 400, …)), the planner pins that read[400, 'VALIDATION_ERROR']for them, one new test pinning a managed install beside a renamed unmanaged copy as admitted (Q6 = B),content/docs/**only where a sentence becomes false, one changeset (patch). PR B —docs/adr/0048-cross-package-metadata-collision.mdanddocs/adr/0131-total-organization-ownership-no-null-organization-id.mdonly,skip-changeset. ⛔packages/spec/**,ui/**, step 1f ofmarketplace-install-local-plugin.ts(Q6 = B leaves it untouched), thetargetPackageIdrefusals (a separate call).Container & model:
M,mode:thread,model: TIER_DEFAULT,effort: default; PR B is Tier H (docs/adr/**): an independent full Fable review before ACCEPT, then the maintainer merges.Clause-②: no
Metadata-protocol: none
Responsibility: n/a — PR C carries contract review 6689730882's should-fix 1 (the answer code the docs promise); PR B is Ask item 8.
Thread-read: 6694353641
Premise: holds. PR A #30997 merged
f4d9c094; Q6 ruled B (record 6709037798), so PR B needs no code and step 1f stays as written; onorigin/mainthe twotargetNamespacerefusals still answer400 VALIDATION_ERRORwhileinstall-modes.mdxsaysINVALID_REQUEST.Serial constraints cleared: no open PR edits
package-unmanaged-install.tsor either ADR; #30884 PR 2 (unclaimed, waits for a slot) editsmarketplace-install-local-plugin.tsbut not the lines PR C touches.Clause-②: nofor PR C: the change moves a refusal'scodefromVALIDATION_ERRORtoINVALID_REQUEST, the code the published docs already state for it (install-modes.mdxrefusal row, PR A's changeset), so it negates no accepted request; both codes are already in the ledger. PR B is prose. Order: project fileserver-seat-2/orders/30794-prb.md.Refs #30794(PR C carriesPart of #30794, PR BFixes #30794).objectstack-fleet commentedon Nov 7, 2026
domain:server#2 · marchtian · session_017ihL2ewD2rAszkPvzKBT8T
os-dev-report
{ "issue": 30794, "status": "done", "branch": "claude/project-thread-p29x8v", "pr": "https://github.com/objectstack-ai/objectstack/pull/31182", "session": "session_017ihL2ewD2rAszkPvzKBT8T", "premise_still_valid": true, "summary": "PR C (Part of #30794), draft at head 9aa77b4f. The two targetNamespace refusals of planUnmanagedImport (namespace rule; shared base/system/sys) now throw refusal('INVALID_REQUEST', 400, ..., { targetNamespace }) instead of a code-less statusCode 400, so the boundary answers INVALID_REQUEST and the install-local door now passes the reason through (before: VALIDATION_ERROR and 'the reason is in the server log'). Planner pins moved to [400, 'INVALID_REQUEST'] plus details; two new door describes in the existing install-modes harness: the real ObjectStackProtocolImplementation importer answering Acme/sys with 400 INVALID_REQUEST, reason and details.targetNamespace; and the Q6 = B pair (managed install beside a copy renamed to acme proceeds to register; beside a copy that kept crm, 409 RESOURCE_CONFLICT before any write). targetPackageId refusals and step 1f untouched; docs unchanged (no sentence became false). One changeset, @objectstack/metadata-protocol patch. Per the narrowed review rule, PR C touches no spec/skills/ADR, so it is Opus self-reviewed; no Fable review was run on it.", "tests": "head 9aa77b4f. pnpm --filter @objectstack/metadata-protocol exec vitest run --maxWorkers=2 src/package-unmanaged-install.test.ts: Test Files 1 passed, Tests 82 passed. pnpm --filter @objectstack/cloud-connection exec vitest run --maxWorkers=2 src/marketplace-install-local-install-modes.test.ts: Test Files 1 passed, Tests 34 passed. Ablation via scripts/ablation-replace.mjs: the reserved-namespace refusal turned back into invalidRequest (anchor 1 to 0, blob eb17407 to 23f7fd3), metadata-protocol rebuilt, door pin red: Expected INVALID_REQUEST, Received VALIDATION_ERROR (1 failed, 1 passed); restored (blob == HEAD, git diff HEAD empty), rebuilt, 34/34 green.", "gates": "Local: dispatch-gates --commands derived 60 families for 4 paths (merge base 8ef4d3090); 59 exit 0; pnpm check:dual-build-cjs-loads exit 3 PREREQUISITE NOT MET (no whole-tree pnpm build; self-test 93 cases passed) = NOT MEASURED, CI Lint and Repo Gates runs it. --ran: 60 derived accounted for, 59 run, 1 NOT-MEASURED. pnpm lint exit 0. typecheck metadata-protocol + cloud-connection exit 0. CI check-runs on the PR: in_progress at report time.", "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm", "mcp_calls": "3 read-only: issue_read get #30794 (plus tool-schema loads); no MCP write tool", "api_writes": "3: git push (branch), fleet-write relay pr_create (#31182), post-stamped comment on #30794 (this report)", "open_questions": [], "security_findings": "none", "out_of_scope_findings": [ "carrier: seat · noted, not filed: the targetPackageId refusals (id rule; equal to the source id) still answer a code-less 400, i.e. VALIDATION_ERROR, and the install-local door withholds their reason the same way; aligning them is the separate call the hand-over 6690317826 names." ] }objectstack-fleet commentedon Nov 7, 2026
domain:server#2 · marchtian · session_017ihL2fBJ6xsLWJsnNJGMMx
Landed — PR C · 2026-11-07T17:13Z
PR #31182 merged 2026-11-07T17:13Z as
bd5a443c02. A refusedtargetNamespacenow answers400 INVALID_REQUEST, carrying the reason anddetails.targetNamespace. Q6 = B is pinned on both sides.Next is PR B on the same card thread, from a merge of main: ADR-0048 and ADR-0131 only,
Fixes #30794, skip-changeset. It is Tier H, so it gets a Fable review of the two ADRs and the code they describe, then a human merge.objectstack-fleet commentedon Nov 7, 2026
domain:server#2 · marchtian · session_017ihL2ewD2rAszkPvzKBT8T
os-dev-report
{ "issue": 30794, "status": "done", "branch": "claude/project-thread-p29x8v", "pr": "https://github.com/objectstack-ai/objectstack/pull/31205", "session": "session_017ihL2ewD2rAszkPvzKBT8T", "premise_still_valid": true, "summary": "PR B (Fixes #30794), draft at head 35c09fa7, after merging origin/main (PR C #31182 landed as bd5a443c; merge commit ba9f019f, no rebase or force push). Two files only: ADR-0048 gains a dated Amended note under section 3.5 (rename-on-install ships for unmanaged copies through planNamespaceRename, the duplicate's engine; a managed install is never renamed) plus a status-line Amended entry; ADR-0131 gains a dated Note after D6's Unmanaged bullet (Q3 on #15213, record 6108175106, extended; Q6 does not apply to a renamed copy per maintainer ruling B, record 6709037798, and stands as written for a copy that kept the source namespace) plus a status-line Amended entry. Original paragraphs kept verbatim. The PR needs the skip-changeset label (docs/adr only; the seat sets it).", "tests": "n/a: docs/adr-only diff, no tested behaviour changed, so no test run or ablation.", "gates": "Local, final head 35c09fa7: dispatch-gates --commands derived 21 families (2 paths, merge base bd5a443c0); all 21 exit 0 (check-adr-links + self-test, check:adr-anchors, check:adr-index, check:adr-symbol-anchors, check:doc-authoring, check:pm-governed-merges, check:pm-prior-rulings, check:nul-bytes and the rest); --ran: 21 run, 0 NOT-MEASURED. CI check-runs on the PR: in_progress at report time.", "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm", "mcp_calls": "0 this round (PR C round: 1 read-only issue_read); no MCP write tool", "api_writes": "3 this round: git push (branch, two pushes), fleet-write relay pr_create (#31205), post-stamped comment on #30794 (this report)", "open_questions": [], "security_findings": "none", "out_of_scope_findings": [ "Fable review (independent, model fable, run by this card thread under the narrowed rule): verdict PASS with nits, no must-fix or should-fix. Files read: docs/adr/0048 and 0131 (diffs and context); packages/metadata-protocol/src/package-unmanaged-install.ts (whole), package-namespace-rename.ts (whole), protocol-package-commits.ts (duplicate and import paths); packages/cloud-connection/src/marketplace-install-local-plugin.ts (managed targetNamespace refusal, step 1f findEnvironmentBaseHoldingNamespace, installedFrom); marketplace-install-local-install-modes.test.ts (Q6 = B pair); content/docs/deployment/install-modes.mdx; packages/cli/src/commands/package/install.ts; packages/spec/src/api/package-lifecycle.zod.ts; ADR-0070/0086 headings; records 6108175106 and 6709037798 confirmed. Two nits applied in 35c09fa7 (reference sentence narrowed; #15213 Q3/Q6 disambiguated from ADR-0131 section 6); two left (status-entry punctuation; cloud#3624 not verifiable here). Nits only, so no delta re-review owed. Not read by Fable: the host's unmanagedImportCollisions body in protocol.ts (pre-existing behaviour).", "carrier: seat · noted, not filed: objectstack-ai/cloud#3600 comment 6676044775 (the epic ruling) was not readable from this session (other repository); someone who can read it should confirm the section 3.5 note agrees with it." ] }