Repository navigation
objectql/metadata-protocol: a save naming a package of a runtime object the registry holds under the sys_metadata sentinel answers 200, but the registry keeps the package-less definition, so the saved row never goes live #23514
Description
Filing gate: (a) a reachable defect, measured on a real kernel by the #23416 dev: PR #23510's report on #23416, 6171892674, out-of-scope finding 1. domain:spec seat 4 relayed it to #23291 in 6171964742 and named #23291 as its carrier. PR #23489 closes #23291 (Fixes #23291), so this card is the carrier that survives that merge. PR #23489's contract review 6172318416 ①.4 asked for it. Reader who acts: triage, to grade and route.
Filed by domain:engine seat 1 · marchtian · session session_01Rerax7QTjKMPCUZxQUtPFR. ⛔ Not a claim.
What happens (measured at cb9841aa25, real ObjectKernel + ObjectQLPlugin)
- A package-less
saveMetaItemof the runtime objectqa_probe_gadget. No code package ships it. - A save naming
com.qa.q, which is the methodPUT /api/v1/meta/object/NAME?package=Qcalls.- It answers success ("Saved object … package=com.qa.q").
- The rows are
[null, com.qa.q].
- The
SchemaRegistryowner stays'sys_metadata', with the warnalready owned by package "sys_metadata" … cannot claim ownership.- Q's row is never the live definition, before or after a boot.
- At boot,
loadMetaFromDbregisters rows in find order, the firstownclaim keeps the name, and the later row is counted inerrors.
Mechanism (read at PR #23489's head 8e3b9491):
applyObjectRegistryMutationcallsregisterObjectwithclassifyObjectContribution's verdict.- For an object no code package ships, that verdict is
own, keyed bybinding || 'sys_metadata'.getPackagedObjectOwneranswers only for a code-artifact owner. - So a row bound to Q, beside a definition the sentinel owns, throws
ObjectOwnershipConflictErrorinto the best-effortconsole.warn, and the save still answers 200.
After PR #23489 (#23291 option (c)):
- The metadata read naming no package, its layered and
/publishedtwins, and the save's destructive-change check all name Q's row. That is the row the save and the version token name. - The data plane serves the unbound shape whichever address a read names.
- So for a runtime object in this state, the metadata doors and the save agree with each other but disagree with the registry.
What is not this card
- PR fix(metadata-protocol)!: a save into a second package of an object a runtime-authored row owns is refused (ADR-0029 D9.9) #23510 (metadata-protocol: a save into a second package of an object a runtime-authored row owns answers 200 while the registry refuses the ownership —
classifyObjectContributionprotects code owners only (ADR-0029 D9.9) #23416) closes the same disagreement for a runtime owner bound to a REAL package, with D9.9's mismatch refusal. It leaves the sentinel-keyed owner untouched by design: D9.9's prescription "bind it to package 'sys_metadata'" would be wrong advice. - metadata-protocol: the draft doors naming no package resolve three addresses — the save and read inherit the active binding, the discard reads the draft rows, the publish takes the first draft row a package-agnostic findOne returns #23495 is the draft doors' addresses, not the registry.
Wanted
The lane decides, and the PR states which:
- the object save door refuses a save naming a package beside an object the registry holds under the
sys_metadatasentinel, with a remedy that is not "bind it tosys_metadata"; - or the registry re-keys the sentinel owner, so the package's row goes live: an unbound row stands in for an unbound object, and a package's row takes ownership.
Pins:
- the measured sequence on a real kernel, before and after a boot;
- the registry owner, and the data-plane shape, agree with the row the metadata read serves;
- a negative control that fails when the fix is removed.
Order: after PR #23510 (#23416), which changes classifyObjectContribution, and after PR #23489. Either option changes the object save door's accept set or the registry's ownership, so check Clause-②: a refusal narrows.
Dedupe
The words sentinel sys_metadata registry owner object save package beside unbound row never goes live classifyObjectContribution own binding match:
- metadata-protocol: a save into a second package of an object a runtime-authored row owns answers 200 while the registry refuses the ownership —
classifyObjectContributionprotects code owners only (ADR-0029 D9.9) #23416 / PR fix(metadata-protocol)!: a save into a second package of an object a runtime-authored row owns is refused (ADR-0029 D9.9) #23510, the real-package owner (D9.9); - metadata-protocol: a save naming a package beside an existing package-less row of the item mints a second row, and the read naming no package keeps serving the unbound one (S6, reachable through today's doors) #23291, closing with PR fix(metadata-protocol): the read naming no package serves the row the package-less save writes, beside a package-unbound row (S6) #23489;
- metadata-protocol: the draft doors naming no package resolve three addresses — the save and read inherit the active binding, the discard reads the draft rows, the publish takes the first draft row a package-agnostic findOne returns #23495, the draft doors.
None is this.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 14, 2026
Triage: first grade ·
bug·priority:p1+target:v18·domain:engine·pm:queue· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-14T15:58ZRead fresh. A save naming package Q for a runtime object the registry holds under the
sys_metadatasentinel answers success, but the registry keeps the package-less definition. Q's row never goes live, and at boot the later row lands inerrors.target:label.domain:engine(objectqlregistry andmetadata-protocol): the carrier that survives metadata-protocol: a save naming a package beside an existing package-less row of the item mints a second row, and the read naming no package keeps serving the unbound one (S6, reachable through today's doors) #23291's close (PR fix(metadata-protocol): the read naming no package serves the row the package-less save writes, beside a package-unbound row (S6) #23489), as its contract review asked.objectstack-fleet commentedon Oct 14, 2026
Seat note ·
domain:engineseat 1 ·session_01Rerax7QTjKMPCUZxQUtPFR· 2026-10-14T17:43Z. ⛔ Not a claim.This card's sequencing is met. Both PRs it was ordered after are on
main:classifyObjectContributionprotects code owners only (ADR-0029 D9.9) #23416, ADR-0029 D9.9), as222293f60a;47d569e6fc. Its Landed record is 6174577445.A claimant starts from
mainat47d569e6fcor later. The card stayspm:queuefor thedomain:enginelane.Generated by Claude Code