Skip to content

docs(adr): ADR-0048 addendum — publish-time / marketplace namespace exclusivity contract - #6671

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-1825-adr0048-addendum-namespace-exclusivity
Aug 8, 2026
Merged

docs(adr): ADR-0048 addendum — publish-time / marketplace namespace exclusivity contract#6671
os-zhuang merged 1 commit into
mainfrom
claude/issue-1825-adr0048-addendum-namespace-exclusivity

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes #1825

Drafts the publish-time / marketplace 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, 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:

「准——立 ADR-0048 addendum,定 publish-time / marketplace 全局命名空间独占注册契约。起步口径:保留粒度从纯 namespace 起步(不含 version-range,复杂度不前置);共享命名空间(base/system/sys)白名单、存量 grandfathering、install-time 反查平台注册表三个 open question 在 ADR 内逐条定。」

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)

  • Install time is the only enforcement point that exists. SchemaRegistry.installPackage() in packages/objectql/src/registry.ts throws NamespaceConflictError; isShareableNamespace() exempts RESERVED_NAMESPACES = {base, system} plus sys; pinned by registry-namespace-install-gate.test.ts.
  • Publish time has zero namespace surface — not partial, none. PackageSchema in packages/spec/src/cloud/package.zod.ts carries manifestId / ownerOrgId / visibility / publisher and has no namespace field at all; CreatePackageRequestSchema does not accept one; packages/cli/src/commands/package/publish.ts transmits manifest_id only. 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 own crm@1.x and vendor B crm@2.x while 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 — registerNamespace is intentionally many-to-one for sys_*), but they are restricted by publisher tier — only publisher: '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_at then manifest_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: namespace must travel with the publish payload (the one open-side schema change implied, and the first item of the deferred card); only visibility: '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 the NamespaceConflictError precedent.

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 → publisher uniqueness 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.yml was 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 two i18n* 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/.json files changed.

No changeset: docs-only. The PM applies skip-changeset at acceptance.


Generated by Claude Code

…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>
@vercel

vercel Bot commented Aug 8, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 8, 2026 9:52am

Request Review

@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation labels Aug 8, 2026
@os-zhuang os-zhuang added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 8, 2026 — with Claude
@os-zhuang
os-zhuang marked this pull request as ready for review August 8, 2026 13:55
@os-zhuang
os-zhuang added this pull request to the merge queue Aug 8, 2026
Merged via the queue into main with commit 15370cd Aug 8, 2026
22 of 23 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-1825-adr0048-addendum-namespace-exclusivity branch August 8, 2026 14:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ADR-0048 follow-up: marketplace publish-time namespace registration & exclusivity

1 participant