Skip to content

Refuse two providers claiming one attribute name on a node type - #533

Merged
dmealing merged 6 commits into
mainfrom
fm/mo-issue-527-provider-name-collision
Nov 4, 2026
Merged

dmealing merged 6 commits into
mainfrom
fm/mo-issue-527-provider-name-collision

Conversation

@dmealing

@dmealing dmealing commented Nov 4, 2026

Copy link
Copy Markdown
Member

Fixes #527

What this does

Two providers can no longer register the same attribute name on one node type. The registry now records which provider registered each attribute and refuses a second provider's claim at composition, in all five ports (TypeScript, Python, C#, Java, Kotlin).

  • A name another provider already holds on the same (type, subType) keeps ERR_PROVIDER_ATTR_CONFLICT. Its message now names the owning provider.
  • A name another provider holds on the shared <type>.base root, or on a subtype of it, is the new ERR_PROVIDER_ATTR_SHADOW. This is checked in both directions, so registration order does not matter.
  • A common attribute (accepted on every node type) is checked against other providers' common attributes and against per-type attributes, in both orders.
  • A refusal names both providers and the attribute, and leaves nothing behind in the registry.
  • Still allowed: the same provider repeating its own name, sibling subtypes sharing a name, and registration outside any provider.
  • meta types --detail prints the registering provider next to each attribute, and its JSON output has a provider field.

This is how per-paradigm physical-name words (@column, @field) stay safe as providers multiply (ADR-0007, ADR-0018). No wire-name attribute is added.

Notes for review

  • Core audit: the shipped core providers had no cross-provider collision in TypeScript, Python or C#. In Java, several providers re-declared @description per type next to the documentation provider's common one. Those per-type entries duplicated the common one (same type, same meaning), so they are removed and the common attribute is the single owner. @description is still accepted everywhere it was.
  • Scope: the rule is scoped to one node type. Java chains every <type>.base to metadata.base; no other port has that edge, and treating it as a collision would make Java stricter than the rest.
  • Java extend: a review finding showed Java's extendType accepted a name another provider owned on the inherited root. That is fixed, with a shared fixture so every port is held to it.
  • ADR-0062 records the rule and the alternatives considered. docs/features/extending-with-providers.md, the authoring skill reference and the CHANGELOG (unreleased) are updated.
  • New shared fixtures under fixtures/provider-composition-conformance/: attr-shadow-inherited, attr-shadow-descendant, the attr-ownership/ set, and the common-attribute cases for both orders.

Tests run

All on the rebased branch, serially:

  • scripts/ci-local.sh --quick: passed
  • TypeScript server suite: 13017 tests, 0 failures
  • Python (without integration): 4102 passed
  • C# conformance project: 2239 passed
  • Java reactor (metadata, render, om, omdb, codegen-base/mustache/plantuml/spring, metadata-ktx, codegen-kotlin): build success, 0 failures

How it was validated

The no-mistakes gate ran twice. Run 1 passed review (two rounds of fixes), test, document and lint on this change, then failed at push because GitHub Git Operations was degraded. Run 2 was refused at push because it had skipped review and so had no review-approved head. Neither run published. The captain then approved pushing from the working copy after the full local suites above; the head matches what run 1 validated, rebased onto the latest main.

… node type (#527)

The registry now records which provider registered each attribute and refuses a
second provider's claim at composition, in TypeScript, Python, C#, Java and Kotlin.

- Same (type, subType): ERR_PROVIDER_ATTR_CONFLICT, message now names the owner.
- Shared <type>.base root or a descendant subtype: new ERR_PROVIDER_ATTR_SHADOW,
  checked in both directions so registration order does not matter.
- The same provider repeating its own name, sibling subtypes, and registration
  outside a provider's turn stay legal. A refusal leaves nothing in the registry.
- The rule is scoped to one node type so the JVM registry's metadata.base chain
  does not make Java stricter than the other ports; the shipped core has no collision.
- meta types --detail prints the registering provider; JSON carries `provider`.
- ADR-0062, extending-with-providers docs, authoring skill, CHANGELOG, and shared
  provider-composition fixtures (attr-shadow-*, attr-ownership/).

No wire-name attribute is added.

Fixes #527
…ment, provider-composition README code count, stray CHANGELOG blank line. Docs state the full ADR-0062 rule. No unresolved findings.
@dmealing
dmealing merged commit 32cc7ff into main Nov 4, 2026
9 checks passed
@dmealing
dmealing deleted the fm/mo-issue-527-provider-name-collision branch November 4, 2026 15:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Design: where provider-specific physical names go, and what stops two providers colliding

1 participant