docs(adr-0029): amend D3 — a tenant object overlay is its own contributor layer (#6853) - #7087
docs(adr-0029): amend D3 — a tenant object overlay is its own contributor layer (#6853)#7087os-zhuang wants to merge 1 commit into
Conversation
…utor layer (#6853) The 2026-08-09 maintainer ruling on #6853 approved direction B in principle and made this card's deliverable the ADR-0029 amendment that designs it. This is that design; nothing is implemented. Today an `object` overlay reaches `SchemaRegistry.registerObject` with the default `own` kind, takes the re-registration branch written for HMR replays, and SPLICES the packaged contributor out — the packaged definition is destroyed at write time, re-destroyed on every boot by `loadMetaFromDb`, and `isArtifactBacked` (which reads the merged body's `_provenance`) then answers `false` for a name a code package still ships, disarming `saveMetaItem`'s overlay gate and the delete heal's "never retire a code-shipped object" guard. D9 makes the overlay a third, non-owning contributor kind: base selection becomes `overlay ?? own`, extenders fold on top unchanged (so the resolved schema does not move), artifact identity is read from the OWNER contributor, and "restoration" degenerates to removing the layer. D3's single-owner invariant is unchanged. #6995 is answered by the model: the row's `package_id` is provenance on the layer, never an ownership claim. Also anchors `packages/objectql/src/registry.ts` to ADR-0029 — it implements D3 and was unanchored, which is how the splice could read as a local convenience. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W6bLax4KMrSfnE1ydFU8Dw
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
|
ACCEPT — PM step-7 review ( Verified against GitHub:
Why this design answers the question that was actually askedThe ruling sent this card to design because B's semantics could not be guessed: an overlay replaces the whole body while D9 resolves that by refusing both horns: a third, non-owning The clause that would have been guessed wrong is called out rather than buried: #6995 is answered by the model rather than left dangling, as the ruling required: the row's Also good: one anchor added for Both open questions resolved here, no escalation
@maintainer: this one needs your review to move — and by the ruling's own logic, reviewing it is the decision on B's semantics. Generated by Claude Code |
Fixes #6853
Design document only. No implementation, and none is intended in this PR.
The 2026-08-09 maintainer ruling on #6853 approved direction B in principle, rejected A, and shipped C separately as #7012 — and it made this card's deliverable the ADR-0029 amendment that designs B, because B's semantics were explicitly to be designed in the amendment, not guessed.
docs/adr/**is maintainer-merged (#6741), so merging this PR is what ratifies the design.What is added
docs/adr/0029-…md— a Status-line entry plus an end-of-document## Amendment (2026-08-09, #6853)section declaring D9. D1-D8 and D3's text are left standing (Prime Directive [WIP] Add Chinese version of the documentation #13); the amendment holds the present tense.scripts/adr-anchors.json— one entry:packages/objectql/src/registry.tsagainst ADR-0029.The design question the ruling posed, and the answer
The tension it named: an overlay replaces the whole body today, while
mergeObjectDefinitionsmerges fields additively — so modelling the overlay as an extender would silently stop an overlay removing a packaged field, and modelling it as a secondownbreaksassertSingleOwnerPerObject.Chosen: a replace-semantics, NON-owning contributor kind (
overlay). Not a new ownership kind.base = overlay ?? own, then extenders fold on top exactly as today. That is bit-for-bit what the current splice already produces — today the overlay is the owner and extenders fold over it. The resolved schema,_provenance: 'org'included, does not move; only what the registry remembers does.assertSingleOwnerPerObjectis unchanged, literally: overlays are not owners, so D3's sentence keeps holding with no exemption clause. That matters because ADR-0028 D5/D6 depend on D3 being unconditional. One violation class is added — an orphan overlay with no owner.computeFQNis untouched (it is identity today); the layer shares the owner's key and no namespace dimension is introduced. The measured namespace loss on the owner contributor is repaired by subtraction, because the owner is no longer removed._provenance: 'org', soisArtifactBackedonly stops lying ifgetArtifactItem's object branch reads the owner's definition.An ownership kind that also owns is rejected in the record: it grants authority the overlay measurably never uses (it registers no namespace, does not decide package membership, owns no table), and it would make D3 conditional at every
getObjectOwnercall site.#6995, per the ruling
Answered by the model rather than left open. Because the overlay makes no ownership claim,
registerObject's "already owned by package X" throw is unreachable from the overlay seams, so the silent-discard-with-success-receipt cannot recur. The amendment rules all three bindings: same package (normal), package-less sentinel (accepted — today's throw was an artefact of the borrowed slot, not a decision), different package (refused at the producer, loudly). The last one carries its own reason: the overlay-uniqueness index can legitimately hold two rows for one(type, name)bound to two packages, which forobjectthe registry can never represent, becausecomputeFQNis identity and there is exactly one entry per object name.Honestly stated cost
With the predicate honest,
saveMetaItemrefuses an overlay write to a packaged object withNOT_OVERRIDABLEevery time, not only the first —objectdeclaresallowOrgOverride: false. Today the first write is refused and, by destroying the evidence, admits every later one through theallowRuntimeCreatetier. This is the declared contract enforced consistently, not a new restriction, but a deployment living in the post-first-write state will feel it. Recorded in D9.6 with the migration note.The amendment also carries a measured blast-radius table (every
.ownershipread site on the current tree and what D9 does to it) — including one silent regression a naive implementation would ship:provisionPrimary/provisionSearchCompanionare gated onown, and that gate has to become "is this a BASE layer", ornameFieldchanges on every overlaid object.Verification
Prose has no test coverage, and no reverse verification is reported, because none exists for a design document — inventing one would be fabricated evidence. What was done instead: every file:line claim was re-read on
2f3e79351rather than carried over from the 08:12Z measurement report (which cites51f2bb8c3), and the docs gates were run locally:pnpm installwas deliberately skipped: the diff is one Markdown file and one JSON file, and every gate above is a zero-dependencynodescript — which is exactly howcheck-links.ymlandadr-merge-approval.ymlrun them in CI.Changeset
None, deliberately. This releases no package, and
pr-automation.yml's changeset gate prescribes theskip-changesetlabel rather than an empty changeset file (an empty file is a real input tochangesets/action; the label is a gate-level exemption). The label is applied on this PR.Landing
Do not auto-merge.
ADR maintainer approval(#6741) will sit unsatisfied until the maintainer's own approval, and per the ruling that is the point: it puts the design question on the maintainer's desk by construction.Generated by Claude Code