docs(adr): ADR-0048 addendum — publish-time / marketplace namespace exclusivity contract - #6671
Merged
os-zhuang merged 1 commit intoAug 8, 2026
Conversation
…xclusivity contract Draft the publish-time half of ADR-0048 that was deferred when the install-time gate shipped (#1810): a global `namespace -> publisher` exclusive registration made at marketplace publish time, in front of the existing install gate. Additive only — §§1-6 stand verbatim, the install gate's semantics are unchanged, and §3.5 (namespace rename-on-install) stays deferred. Settles the three open questions the issue left open, each argued on the repo's three decision axes: - Reservation granularity is the bare namespace, not (namespace, version-range). A table name has no version axis, and a range-keyed reservation would grant ownership the per-installation, version-blind install gate cannot honour. - The shared-namespace whitelist is exactly the install gate's RESERVED_NAMESPACES + `sys`, derived from the same constant so the two gates cannot drift. Exemption from exclusivity is not open season: publishing into the whitelist is restricted to the first-party publisher tier. - Existing packages are grandfathered first-come, and the grandfathered set is closed at switch-on and shrink-only thereafter. - The install-time reverse lookup against the platform registry is specified as advisory and fail-open, never authoritative — the install path acquires no network dependency, and local/air-gapped installs behave exactly as today. Also records the open/commercial boundary on the cloud ADR-0016 iron rule (as applied in ADR-0105 D12): the gate that keeps an installation correct ships open; the operated global registry is the governance surface. And records the hard activation precondition from the ruling — the namespace->publisher uniqueness check must land before third-party publishing opens, because every package published before it exists becomes a grandfathered entry that can never be revoked. Non-goals kept explicit: version-range granularity, reservation rename/transfer, rename-on-install, and any change to install-gate semantics. Fixes #1825 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
os-zhuang
marked this pull request as ready for review
August 8, 2026 13:55
os-zhuang
deleted the
claude/issue-1825-adr0048-addendum-namespace-exclusivity
branch
August 8, 2026 14:23
This was referenced Aug 8, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #1825
Drafts the publish-time / marketplace half of ADR-0048 that was deferred when the install-time gate shipped (#1810): a global
namespace → publisherexclusive registration made at marketplace publish time, sitting in front of the existing install gate.ADR text only — no code, no gate changes, no spec changes. The enterprise-side implementation is split into separate cards per the ruling ("enterprise 面实现随 ADR 结论另行拆单").
Executes the maintainer-approved fleet decision of 2026-08-06, quoted verbatim in the addendum:
Status
Proposed. Additive to §3.2; supersedes no text above it — §§1–6 stand verbatim, the install gate's semantics are untouched, and §3.5 (namespace rename-on-install) stays deferred. Prime Directive #13 is satisfied trivially here: nothing was reversed, so nothing needed marking SUPERSEDED.
Verified premises (against
main@f123670)SchemaRegistry.installPackage()inpackages/objectql/src/registry.tsthrowsNamespaceConflictError;isShareableNamespace()exemptsRESERVED_NAMESPACES = {base, system}plussys; pinned byregistry-namespace-install-gate.test.ts.PackageSchemainpackages/spec/src/cloud/package.zod.tscarriesmanifestId/ownerOrgId/visibility/publisherand has nonamespacefield at all;CreatePackageRequestSchemadoes not accept one;packages/cli/src/commands/package/publish.tstransmitsmanifest_idonly. The namespace never leaves the artifact.So the exposure is precise: two vendors can each publish a package claiming namespace
crm, both listings are valid, and the collision is discovered by the tenant — the one party who cannot fix it — when they install the second one.The three open questions, settled
Each argued on the three axes (实际业务需求 / 长远合理性 / 防 AI 犯错).
D1 — Reservation granularity is the bare
namespace. Not(namespace, version-range). What breaks is a physical table name, and a table name has no version axis. Worse, a range-keyed reservation would let vendor A owncrm@1.xand vendor Bcrm@2.xwhile the install gate — per-installation and version-blind — still collides on them: it would grant ownership the enforcement layer cannot honour. Granularity can only be widened later, so starting narrow is the reversible direction.D3 — Shared-namespace whitelist is exactly the install gate's set,
RESERVED_NAMESPACES ∪ {sys}, read from the same constant so the two gates cannot drift into disagreeing about what is shareable (the issue's "compose without drift" criterion, met by construction). Two rules, and conflating them is the trap: those namespaces are never reserved and never conflict (required for correctness —registerNamespaceis intentionally many-to-one forsys_*), but they are restricted by publisher tier — onlypublisher: 'objectstack'may publish into them. Exemption from exclusivity must not read as open season, or the whitelist becomes the cheapest way for a third party to ship objects shadowing platform tables.D4 — Grandfathering is first-come, frozen, and shrink-only. Back-fill the registry rather than starting empty: the earliest published version carrying a namespace wins it (deterministic tie-breaks on
created_atthenmanifest_id); every other publisher already shipping it is recorded as a grandfathered claim that may keep publishing new versions of packages that already existed, and nothing else. The set is closed at switch-on and can only shrink. First-come is the only rule needing no adjudicator — and a judgement call in a gate is a call an agent will make wrongly and confidently.D5 — Install-time cross-check of the platform registry is advisory and fail-open, never authoritative. Contract defined here, implementation deferred. On mismatch: a warning and a trust signal in the install surface; it installs anyway. On lookup failure, timeout, or no platform connection: fails open silently. Three reasons it must not be load-bearing — §4's "local development is unaffected" is a load-bearing property of this ADR; the correctness invariant is already fully decided locally with no network (the registry adds provenance, not correctness, and a trust signal that can hard-fail an install turns a marketplace outage into a total install outage); and sideloading is a deliberate act where what the user lacks is information.
Also settled: D2 — the reservation is held by the publisher, not the package, so a vendor's successor package can reuse its own namespace (which would otherwise be stranded forever behind their own deprecated package, with no transfer flow to escape through). This is the one place the publish gate is looser than the install gate, and it is deliberate: cross-vendor collisions are unresolvable by any party and hard-fail; same-vendor ones are entirely within one party's control. D6 — the per-tenant alias (§3.6) and global reservation do not interact at all; routing is never resolved against the global registry.
Minimal version A
The addendum states the publish check as an algorithm (§A.2) so the enterprise implementation and this repo's gate cannot drift. Contract notes:
namespacemust travel with the publish payload (the one open-side schema change implied, and the first item of the deferred card); onlyvisibility: 'marketplace'publishes are gated; the rejection is a first-class ADR-0112 ledger code registered with the implementation, not with this ADR; and the error must name the remedy, on theNamespaceConflictErrorprecedent.Open / commercial boundary
Aligned with the cloud ADR-0016 iron rule 强制免费、治理收费, as this repo already applies it in ADR-0105 D12 (code vs. activation, not code vs. code). Open and free: the manifest field, the install gate, the advisory lookup port and its fail-open semantics, the error codes, this contract. Enterprise: the reservation registry, the publish-time gate, ownership records, the back-fill and grandfathered ledger, dispute handling. The iron rule holds because the free half is the one that keeps an installation correct — any deployment, any tier, offline or not, is fully protected from a namespace collision corrupting its schema. The paid half prevents a collision from being discovered late, which is governance.
Activation precondition (hard)
The
namespace → publisheruniqueness check must land before third-party publishing opens — an acceptance criterion for opening it, not a follow-up. Not stylistic: every package published before the check exists becomes, by D4, a grandfathered entry that can never be revoked, so each day the gate is late permanently enlarges a set that is otherwise shrink-only. While publishing is first-party only that set is bounded by our own discipline; the moment it is open it is bounded by nothing.Non-goals (explicit)
Version-range granularity (actively rejected, not pending design) · reservation rename/transfer (deferred, per 「暂不考虑」 and the 2026-08-06 disposition) · rename-on-install (unchanged §3.5) · squatting/dispute adjudication (operational policy) · any change to install-gate semantics, its shareable set, or
OS_METADATA_COLLISION=warn· enforcement outside the marketplace.Gates
Docs-only diff (one file). Every gate enumerated from
.github/workflows/lint.ymlwas run individually and is green:lint·slot-lookup·query-options-erasure·verify-stand-in·nul-bytes·doc-authoring·docs-audit-scope·role-word·quick-reference-counts·adr-anchors·org-identifier·authz-resolver·service-providers·route-envelope·error-code-casing·wildcard-fallthrough·meta-type-normalized·init-service-contract·durability-log-level·startup-registry-verdict·objectui-changeset·release-notes·release-body·node-version·workflow-status-functions·shard-attestation·published-files·engine-double-contract·resume-authority-declared·merge-driver·spec-parsed-alias·i18n·i18n-coverage·skill-frame-sync·skill-compatibility. The twoi18n*gates needed a workspace build as a prerequisite (they run the built CLI and lint the example configs) and are green after it. The TypeScript type-check job is unaffected — zero.ts/.jsonfiles changed.No changeset: docs-only. The PM applies
skip-changesetat acceptance.Generated by Claude Code