Closed
Labels
area:apiThe API a customer can call, and integrations — REST, connectors, webhooks, jobsThe API a customer can call, and integrations — REST, connectors, webhooks, jobsbugSomething isn't workingSomething isn't workingdomain:serverDomain lane — seat card indexed by label:pm:seatDomain lane — seat card indexed by label:pm:seatfindingRecorded observation — held, not dispatchable until the findings triage round grades itRecorded observation — held, not dispatchable until the findings triage round grades itpriority:p3
Description
domain:server#1 · os-project-manager · session_01R1q2xdtYgU6FKrZ7LgjPCC
Filing gate: ① two should-fix findings from the CONTRACT_REVIEW_TIER contract review of PR #28724 (#28564), comment 6560407046. Filed by domain:server seat 1. ⛔ Not a claim.
Reach: once #28564 lands, the __c reservation (ADR-0146 D10) is judged at the save, restore, publish, validate, marketplace-install and duplicate doors, but not at the adopt door.
What the review found
- The adopt door rebinds without the judge.
reassignOrphanedMetadata(packages/metadata-protocol/src/protocol.tsaround L26610-26720, RESTPOST /api/v1/packages/:id/adopt-orphans) moves active package-unbound rows into a plain package by a storage update plusapplyRegistryWriteThrough, and no judge runs. A fieldx__cthe save door admits into no package (pinned atprotocol.extension-field-suffix.test.tsaround L414-425) then lands as a plain package's own field: case (a), silently admitted. The namespace-prefix and owner judges are skipped on this door too, an older gap of the same class; ADR-0146 D11 shrinks its input. - Promotion ordering (consistency). The promotion's suffix block (
protocol.tsaround L23734-23739) is not gated!this.isSealedManagedItem(...)the way the two blocks above it are (around L23660 and L23696). A sealed draft carryingx__cwould answer "rename the field" before the store'sNOT_OVERRIDABLE. It is hard to reach, because the save door'srefusePackagedBaseOverrideruns first.
Wanted
- The adopt door asks the one exported judge (
judgeExtensionFieldSuffix/objectBodyExtensionFieldSuffixRefusal) for each object row it would rebind, judged against the TARGET package id, and refuses the adopt whole, with the save door'sVALIDATION_ERRORenvelope, when any row breaks case (a). Say whether the namespace-prefix and owner judges should ride the same call, or leave them to their own card. - Gate the promotion's suffix block like its neighbours.
- Tests: an adopt of a package-less
x__cfield into a plain package is refused and moves nothing, and an adopt into the base's.custompair is admitted. Ablation per step.
Declarations expected: Clause-②: no (narrowing), so a contract review is owed.
Blocked-by: #28564 (PR #28724).
Dedupe: searched adopt-orphans __c, reassignOrphanedMetadata judge; only #28564.
Links back: #28564, PR #28724, ADR-0146 D10 / D11
Generated by Claude Code
Metadata
Metadata
Assignees
Labels
area:apiThe API a customer can call, and integrations — REST, connectors, webhooks, jobsThe API a customer can call, and integrations — REST, connectors, webhooks, jobsbugSomething isn't workingSomething isn't workingdomain:serverDomain lane — seat card indexed by label:pm:seatDomain lane — seat card indexed by label:pm:seatfindingRecorded observation — held, not dispatchable until the findings triage round grades itRecorded observation — held, not dispatchable until the findings triage round grades itpriority:p3
Activity
objectstack-fleet commentedon Nov 1, 2026
domain:server#1 · os-project-manager · session_01R1q2xdtYgU6FKrZ7LgjPCC
Claim: PM loop round 1 · card thread · 2026-11-01T16:39Z
Session:
session_01R1q2xdtYgU6FKrZ7LgjPCC(the seat); the card thread issession_01FEkQ5mTYb1BTJNRqf3bdJ8Account:
os-project-manager(the seat's linked user; the card's assignee)Branch:
claude/project-thread-xqgh8b(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#1Ruling-ref: 「定级你可以自己决定」 (Leehom, 2026-11-01T12:11Z); seat 1 filed the card as p3 from PR #28724's contract review
6560407046(two should-fix findings).File surface:
packages/metadata-protocol/src/protocol.ts(reassignOrphanedMetadataand the promotion's suffix block inpromoteDraftForPublishonly), their tests, and a@objectstack/metadata-protocolchangeset. ⛔ Nopackages/spec/**; ⛔ notprotocol-packaged-base-refusal.ts,stored-row-binding.ts,withRowBindingor the automation boot pull.Container & model:
S,mode:thread, model and effort as the thread was openedClause-②: no (narrowing) (expected: an adopt that was admitted is now refused, on a count the dev measures, expected 0; the seat arranges a contract review at
CONTRACT_REVIEW_TIER)Metadata-protocol: measured by the dev
Responsibility: the code is the adopt-orphans door of
@objectstack/metadata-protocol· the platform path is a package-less object row carrying anx__cfield rebound into a plain package · reached byPOST /api/v1/packages/:id/adopt-orphansThread-read: none
Premise: holds; #28564 (PR #28724) is on
mainas447a2a6f69Serial constraints cleared: seat 1's #28412 (PR #28693) and #28717 edit other regions of
protocol.ts, and PR #28734 (#28673) edits its read sites. Mergeorigin/mainonce if one lands first, and keep both.Resume-from: none — fresh from
origin/mainState:
pm:queue→pm:dispatched, assigned toos-project-manager, recorded by this claim.Generated by Claude Code
objectstack-fleet commentedon Nov 1, 2026
domain:server#1 · os-project-manager · session_01FEkQ5mTYb1BTJNRqf3bdJ8
os-dev-report
{
"issue": 28735,
"status": "done",
"branch": "claude/project-thread-xqgh8b",
"pr": "#28768",
"session": "session_01FEkQ5mTYb1BTJNRqf3bdJ8",
"premise_still_valid": true,
"summary": "reassignOrphanedMetadata (POST /api/v1/packages/:id/adopt-orphans) now judges every active object row it would rebind against the TARGET package id with judgeExtensionFieldSuffix, before the first storage update, and refuses the adoption whole with extensionFieldSuffixRefusalError (VALIDATION_ERROR / 400, issues[] at objects.OBJECT.fields.FIELD): no row moves, no registry write-through. An adoption into the base's .custom pair is admitted. The promotion's suffix block in promoteDraftForPublish is gated
singularType === 'object' && !this.isSealedManagedItem(...)like the D9.9 and namespace blocks, so a sealed draft gets the store's NOT_OVERRIDABLE / 403. #28734 landed on protocol.ts mid-run: merged origin/main once (clean, both kept), head 8bdde03. Changeset: metadata-protocol minor, BREAKING banner, Clause-②: no (narrowing), Metadata-protocol: narrowed, ADR-0087 not-required (no-migration-prescription). Narrowing count: 0 fields spelled [a-z0-9]__c in examples/** and packages/*/src outside tests (10 textual hits, all doc comments). The card's assignee was not touched; the PR carries no assignees and no labels (the order names none; skip-changeset does not apply).","tests": "New in packages/metadata-protocol/src/protocol.extension-field-suffix.test.ts: §4b adopt (x__c into plain package refused, case (a) envelope + sentence, engine.update and applyRegistryWriteThrough never called, row still package-less; mixed clean + x__c refused whole, clean row unmoved; CONTROL x__c into .custom admitted and moved, clean row into plain moved) and §3b seal pin (shipped object draft with x__c staged as a pre-gate row, publishMetaItem answers NOT_OVERRIDABLE / 403, draft kept; reachable only through a pre-gate stored row, the save door refuses that draft first). Base (protocol.ts from db357f6): Tests 3 failed | 21 passed (24) (seal pin got VALIDATION_ERROR; both adopt refusals got no error); .custom CONTROL green on base and head; restore git diff HEAD empty. Head: 24/24. Ablations via scripts/ablation-replace.mjs from committed HEAD (suite reads src, no build leg): adopt throw removed, anchor 1->0, blob 065792a3d3d0->618118ae6818, 2 failed (the two adopt refusals), restored blob == HEAD, diff empty; seal gate removed, anchor 1->0, blob 065792a3d3d0->7a4b2a52267f, 1 failed (seal pin), restored blob == HEAD, diff empty. At 8bdde03: pnpm --filter @objectstack/metadata-protocol test: Test Files 294 passed | 4 skipped (298), Tests 22012 passed | 26 skipped; typecheck exit 0 (tests type-checked). REST door: runtime packages-adopt-orphans-readonly + package-duplicate-adopt-org-scope 13/13; objectql adopt/lifecycle/packageless-migration/sentinel-owner 44/44 (dist rebuilt, pre-merge head 6f6d2f3). eslint --no-inline-config on the two touched files: exit 0.",
"gates": "At 8bdde03 after a full turbo build (118/118): node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 61; all 61 exit 0 (record /mnt/project-files/pm-orders/server/28735-gates-ran.txt); --ran: 61 derived, 61 run, 0 NOT-MEASURED, 0 UNRUN. check:type-check-debt under the lock: OK 126/129, --re-measure OK. First pass at 6f6d2f3 had 3 PREREQUISITE exits (dts-closure, dual-build-cjs-loads: no full build; plugin-teardown-shape --self-test: shallow clone) and 1 queue-timeout (type-check-debt behind dogfood); none measured, all green on the 8bdde03 pass. CI families: check-runs on 8bdde03 were in_progress (18) / skipped (3) at report time; the seat reads their conclusions.",
"dogfood_battery": "8bdde034 · os-verify-lock VERDICT command-exit 0 · Test Files 374 passed | 3 skipped (377) · Tests 3034 passed | 28 skipped (3062) · no failures (also green at pre-merge 6f6d2f3, same counts)",
"mcp_calls": "0 — no MCP GitHub calls (card, claim and PR read through public REST GETs)",
"api_writes": "2 — fleet-write relay dispatches (POST /repos/objectstack-ai/objectstack/dispatches): pr_create #28768 (run 41336638207, read-back identical, assignees none) and this os-dev-report comment. Plus 1 git push. Write budget spent: 3 of 3.",
"open_questions": [
{
"question": "Should the namespace-prefix judge (objectNamespacePrefixViolation) and the owner judge (permissionSetOwnerBindingRefusal) ride the same pre-loop call in reassignOrphanedMetadata?",
"options": [
"A: own card. The owner judge is cheap here (same loop, permission rows, VALIDATION_ERROR envelope, almost certainly 0 rows hit). The namespace judge is not: its envelope is NAMESPACE_PREFIX / 422, not the suffix judge's VALIDATION_ERROR, so one adopt could fail two ways and needs an ordering ruling; and package-less objects are usually unprefixed, so adopting into a namespaced package would be refused for most real environments, which turns the ADR-0070 D5 convergence door into a dead end unless the adopt also renames (a design question). Needs a reach measurement on the CRM/showcase examples first. ADR-0146 D11 closes new package-less writes, so the population is legacy rows only and shrinks.",
"B: ride now. About 15 lines in the same block plus two pins; cost is the namespace blast radius above, unmeasured, and a narrowing that may refuse most adoptions into a namespaced base."
],
"recommendation": "A, because the namespace judge changes the door's purpose for namespaced targets and needs a reach count and a ruling on refuse-vs-rename, while the owner judge alone is too small to justify its own narrowing record; file one card for both with the measurement as its first step."
}
],
"out_of_scope_findings": [
"noted, not filed: protocol-extension-field-suffix.ts's header lists the doors that call the judge and does not name the adopt door; that file is outside the claim's file surface. carrier: none (PR Acceptance notes). A later edit of that file can add one line.",
"The namespace-prefix and owner judges skipped at the adopt door: same class, carried by open_questions above, not filed (ruling 2)."
]
}
Generated by Claude Code
objectstack-fleet commentedon Nov 1, 2026
domain:server#1 · os-project-manager · session_01R1q2xdtYgU6FKrZ7LgjPCC
Landing record · #28735 · PR #28768
PR #28768 merged by the merge queue as
c363084b53onmain(Fixes #28735; contract review PASS6562423822; ACCEPT6562435013). The card closed on the merge.git ls-remote origin 'refs/heads/gh-readonly-queue/main/pr-28768-*'origin/mainby contentgit diff --quiet c363084b53 origin/main --the PR's 3 filesgit merge-base --is-ancestor c363084b53 origin/mainreassignOrphanedMetadata) asks the one__cjudge for every activeobjectorphan, against the target package, before anything moves. A refusal answersVALIDATION_ERROR/ 400 and adopts nothing, and an adoption into the base's.custompair is admitted. The promotion's suffix block is seal-gated like its neighbours.@objectstack/metadata-protocolminor, BREAKING banner,Clause-②: no (narrowing), measured count 0.pm:blockedtopm:queuenow.Release: branch
claude/project-thread-xqgh8bmerged; nothing unpushed.pm:dispatchedremoved and the card unassigned.Generated by Claude Code