feat(brand-protocol): schema cut for distributed brand.json RFC#3764
feat(brand-protocol): schema cut for distributed brand.json RFC#3764bokelley wants to merge 10 commits into
Conversation
Draft RFC proposing per-brand canonical brand.json documents linked by mutual-assertion pointers, replacing the monolithic-with-inline-children shape. Hosting (static / CDN / brand-agent / AAO / self) stays an implementation choice independent of the data model. Key proposed changes (subject to discussion): - Each brand publishes one canonical brand.json owning its own attributes - New `house` pointer for declaring the immediate parent (multi-level chains via recursion) - New `brand_refs[]` (pointer-only) replacing inline `brands[]` content - New `house_attributes` block for inheritable house-wide metadata - Mutual-assertion as the trust primitive — child's `house` must be reciprocated by parent's `brand_refs[]` Migration path: 3.x accepts both shapes with deprecation warnings; brand-protocol 2.0 (decoupled from AdCP major) cuts over. Lives at docs/brand-protocol/proposals/distributed-brand-json-rfc.mdx under a new "Proposals" subgroup. Not yet normative — needs spec-owner sign-off before any code or schema changes land. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
RFC stays at docs/brand-protocol/proposals/distributed-brand-json-rfc.mdx as a working document, but isn't surfaced in the published docs nav. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ouse rename Addresses Pawel's review on PR #3533: 1. Lead with the operational pain point — Converse can't update its own logo in AdCP without editing Nike, Inc.'s file. Reframed Motivation. 2. Hybrid model — brands[] (inline, parent-owned) and brand_refs[] (pointer, child-owned) both first-class. Pull-based migration: brands[] is no longer deprecated. Sub-brands without their own domain stay inline. 3. Flat hierarchy — only the house declares ownership via brand_refs[] or brands[]. A brand cannot have its own brand_refs[]. Trust collapses to a single hop; no recursive walking. 4. Renamed child-side pointer from `house` to `parent_house` to avoid collision with the existing `house` declaration object on the root. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ed example
check:owned-links treated the example URL as a real link and failed CI
when the path 404'd. Wrap the brand-domain segment as \${domain} so the
linter recognizes it as a placeholder (it skips URLs containing \${).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…arent_house example Code-reviewer + protocol-expert findings on PR #3764: - Brand Canonical Document variant lacked oneOf disambiguation. brand definition has additionalProperties: true and the inner allOf member had no constraint, so a malformed Portfolio with a stray parent_house could silently re-type as a Canonical Document. Replaced the narrow not: {required: ["brand_refs"]} with not: {anyOf: [...]} blocking all house-only top-level keys (house, brands, brand_refs, house_attributes, authorized_operators). - Fixed example: parent_house: { domain: "nikeinc.com", brand_id: "converse" } read as "I'm pointing at converse inside nikeinc.com" — but Converse owns this document; the brand_id was the self, not the parent. parent_house is a pointer UP, only domain is meaningful for a typical house pointer. Updated both the schema example and the docs section. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…_domain; add managed_by Resolves the second round of expert review on PR #3533. Substantive changes: - Drop house_attributes / house_attributes_overrides / house_attributes_locked entirely. Inheritance/override semantics turned out to be muddy: if a brand could weaken a house policy, it wasn't really a house policy. House-level fields (data_subject_contestation, trademarks, authorized_operators) are already on the house schema; consumers walk house_domain to read them. Brand-level additions are just brand-level fields. - Rename child-side pointer from parent_house ({domain}) to house_domain (string). Reuses the existing #/definitions/domain pattern. Drops the proposed core/house-ref.json file. Matches the existing House Redirect's string-domain convention. - Add managed_by (string, optional) on brand_refs[] entries. House-declared delegation for grouping/discovery. Non-trust-bearing. Captures WPP/Publicis reality without reintroducing recursive trust. - Make house_domain optional on the Brand Canonical Document so standalone brands (Patagonia, Liquid Death) have a valid shape. Acquisition adds house_domain later; no migration required. - Add an explicit M&A section: existing redirect variants (House Redirect, Authoritative Location Redirect) handle reorganizations; resolution follows redirects through house_domain. - Add a clear field-resolution table: where each consumer-side question is answered. No inheritance/override, just per-brand vs per-house ownership. Trust model still single-hop, mutual-assertion via house_domain ↔ brand_refs[]. Migration still pull-based. brands[] still first-class (not deprecated). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Concrete schema additions for review against RFC #3533. Additive only — existing publishers unchanged. brand.json schema: - New variant: Brand Canonical Document. Self-published per-brand doc with `parent_house: BrandRef` pointer + optional house_attributes_overrides. Composes the existing `brand` definition via allOf so identity fields match the inline `brands[]` shape. - House Portfolio variant gains `brand_refs[]` (pointer brands, child-owned data) and `house_attributes` (house-wide inheritable attributes). Required changed from ["house","brands"] to ["house"] with anyOf at-least-one of brands[]/brand_refs[]. - Two new examples illustrating mixed inline+pointer house and a self- published Converse canonical document. docs/brand-protocol/brand-json.mdx: added a "Proposed (RFC)" callout pointing at the RFC and PR #3533. Existing four variants documented as-is. Cross-array invariant — a brand_id MUST NOT appear in both brands[] and brand_refs[] of the same house — is documented in field descriptions. JSON Schema can't express it; lint/validator follow-up needed if RFC ratifies. Status: review-only. Not normative until RFC ratifies. Marked DRAFT. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…arent_house example Code-reviewer + protocol-expert findings on PR #3764: - Brand Canonical Document variant lacked oneOf disambiguation. brand definition has additionalProperties: true and the inner allOf member had no constraint, so a malformed Portfolio with a stray parent_house could silently re-type as a Canonical Document. Replaced the narrow not: {required: ["brand_refs"]} with not: {anyOf: [...]} blocking all house-only top-level keys (house, brands, brand_refs, house_attributes, authorized_operators). - Fixed example: parent_house: { domain: "nikeinc.com", brand_id: "converse" } read as "I'm pointing at converse inside nikeinc.com" — but Converse owns this document; the brand_id was the self, not the parent. parent_house is a pointer UP, only domain is meaningful for a typical house pointer. Updated both the schema example and the docs section. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…managed_by + drop house_attributes Tracks RFC #3533 v3. Replaces the v2 cut with the simplified model decided during expert review. Schema deltas (relative to previous impl cut): - Replace parent_house ($ref core/brand-ref.json) with house_domain (string, reuses #/definitions/domain). Strings match the existing House Redirect convention and drop the planned core/house-ref.json file entirely. - Make house_domain optional on Brand Canonical Document so standalone brands (Patagonia, Liquid Death) have a valid shape without spinning up a degenerate "house of one." If a standalone brand is later acquired, it adds house_domain and the new house adds it to brand_refs[]. No new variant needed. - Drop house_attributes / house_attributes_overrides entirely. Inheritance/ override semantics turned out muddy — if a brand could weaken a house policy, it wasn't really a house policy. House-level fields stay where they already are (data_subject_contestation, trademarks, authorized_operators on the house schema). Brand-level constraints are additive, not overrides. - Replace the brand_refs[] $ref to core/brand-ref.json with an inline {domain, brand_id?, managed_by?} shape. brand-ref.json's existing governance-override fields (industries, data_subject_contestation) don't belong on a house-side declaration; they're consumer-side overrides. - Add managed_by (string, optional) on brand_refs[] entries — house-declared delegation for grouping/discovery, non-trust-bearing. Captures WPP/Publicis reality (BBH manages this brand for WPP) without reintroducing recursive trust. Examples updated: - Nike Inc. mixed-shape (inline Nike SB + pointer Converse, Jordan) - WPP with managed_by (BBH Sport managed_by bbh.com, etc.) - Converse self-published with house_domain - Patagonia standalone (no house_domain) docs/brand-protocol/brand-json.mdx: rewritten Distributed Extensions section to match the new shape. Adds the four worked examples. Adds field-resolution table showing where each consumer-side question is answered (no inheritance, no overrides). Adds explicit M&A section showing existing redirect variants handle reorganizations. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
6d591ab to
7df840d
Compare
…rmance, strictest-of compliance Addresses round-2 expert review on RFC v3: - managed_by gets normative language: MUST NOT be used for trust/auth decisions; verifiers MUST ignore for authorization. UIs SHOULD render as unilateral house claim, not aggregate cross-house. - New "Compliance fields: strictest-of resolution" section. Identity fields (logos, voice, tone) stay brand-wins. Compliance/governance fields (data_subject_contestation, compliance_policies, regulated-category flags) resolve as union/strictest of house-level and brand-level — brand cannot weaken house assertions, only add stricter constraints. - New "Standalone brands" subsection: absence of house_domain ⇒ standalone, regardless of one-sided third-party brand_refs[] claims. - New "Conformance" section formalizing brand_id cross-array uniqueness, within-array uniqueness, mutual-assertion as canonical trust primitive, managed_by non-trust, standalone trumps third-party claim, strictest-of rule, 180-day TTL. - New "Prior art" section citing IAB Tech Lab ads.txt/sellers.json reciprocal publication model — same trust shape, deployed industry pattern. No structural changes to the data model. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ion + tighten not deny-list Code-reviewer round-2 nits on PR #3764: - Extract brand_refs[] item shape from inline to #/definitions/brand_ref (named type for SDK codegen / generator output). Description includes the managed_by non-trust normative language directly on the field. - Tighten Brand Canonical Document not.anyOf to also block House Redirect's region and note keys. Disambiguation against House Redirect was already clean via id+names requirement, but the deny-list now matches all four other variants' top-level fields explicitly. No example or behavior changes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Schema v3 + review fixes commit `07cfa8c61` — second-round expert review returned no blockers; folded in two cleanups:
Round-2 confirmed:
Validation: schemas (7/7), examples (34/34), json-schema (259 blocks), composed (32/32), typecheck, owned-links — clean. Open follow-up (not in this PR): typed `trademarks` field on Brand Canonical Document. Brand definition currently accepts trademarks via `additionalProperties: true` but doesn't type them. Track separately. Pairs with RFC PR #3533 commit `91f80f5be7`. |
|
Issue #3910 proposes folding the distributed-brand-json RFC content into Generated by Claude Code |
|
Issue #3909 proposes typed Generated by Claude Code |
…ademarks (#4505) * docs(brand-protocol): RFC for distributed brand.json (#3409) Draft RFC proposing per-brand canonical brand.json documents linked by mutual-assertion pointers, replacing the monolithic-with-inline-children shape. Hosting (static / CDN / brand-agent / AAO / self) stays an implementation choice independent of the data model. Key proposed changes (subject to discussion): - Each brand publishes one canonical brand.json owning its own attributes - New `house` pointer for declaring the immediate parent (multi-level chains via recursion) - New `brand_refs[]` (pointer-only) replacing inline `brands[]` content - New `house_attributes` block for inheritable house-wide metadata - Mutual-assertion as the trust primitive — child's `house` must be reciprocated by parent's `brand_refs[]` Migration path: 3.x accepts both shapes with deprecation warnings; brand-protocol 2.0 (decoupled from AdCP major) cuts over. Lives at docs/brand-protocol/proposals/distributed-brand-json-rfc.mdx under a new "Proposals" subgroup. Not yet normative — needs spec-owner sign-off before any code or schema changes land. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(brand-protocol): drop Proposals subgroup from docs.json RFC stays at docs/brand-protocol/proposals/distributed-brand-json-rfc.mdx as a working document, but isn't surfaced in the published docs nav. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(brand-protocol): RFC v2 — hybrid model, flat hierarchy, parent_house rename Addresses Pawel's review on PR #3533: 1. Lead with the operational pain point — Converse can't update its own logo in AdCP without editing Nike, Inc.'s file. Reframed Motivation. 2. Hybrid model — brands[] (inline, parent-owned) and brand_refs[] (pointer, child-owned) both first-class. Pull-based migration: brands[] is no longer deprecated. Sub-brands without their own domain stay inline. 3. Flat hierarchy — only the house declares ownership via brand_refs[] or brands[]. A brand cannot have its own brand_refs[]. Trust collapses to a single hop; no recursive walking. 4. Renamed child-side pointer from `house` to `parent_house` to avoid collision with the existing `house` declaration object on the root. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(brand-protocol): use template-syntax placeholder in RFC AAO-hosted example check:owned-links treated the example URL as a real link and failed CI when the path 404'd. Wrap the brand-domain segment as \${domain} so the linter recognizes it as a placeholder (it skips URLs containing \${). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(brand-protocol): RFC v3 — drop house_attributes; rename to house_domain; add managed_by Resolves the second round of expert review on PR #3533. Substantive changes: - Drop house_attributes / house_attributes_overrides / house_attributes_locked entirely. Inheritance/override semantics turned out to be muddy: if a brand could weaken a house policy, it wasn't really a house policy. House-level fields (data_subject_contestation, trademarks, authorized_operators) are already on the house schema; consumers walk house_domain to read them. Brand-level additions are just brand-level fields. - Rename child-side pointer from parent_house ({domain}) to house_domain (string). Reuses the existing #/definitions/domain pattern. Drops the proposed core/house-ref.json file. Matches the existing House Redirect's string-domain convention. - Add managed_by (string, optional) on brand_refs[] entries. House-declared delegation for grouping/discovery. Non-trust-bearing. Captures WPP/Publicis reality without reintroducing recursive trust. - Make house_domain optional on the Brand Canonical Document so standalone brands (Patagonia, Liquid Death) have a valid shape. Acquisition adds house_domain later; no migration required. - Add an explicit M&A section: existing redirect variants (House Redirect, Authoritative Location Redirect) handle reorganizations; resolution follows redirects through house_domain. - Add a clear field-resolution table: where each consumer-side question is answered. No inheritance/override, just per-brand vs per-house ownership. Trust model still single-hop, mutual-assertion via house_domain ↔ brand_refs[]. Migration still pull-based. brands[] still first-class (not deprecated). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(brand-protocol): schema cut for distributed brand.json RFC (#3533) Concrete schema additions for review against RFC #3533. Additive only — existing publishers unchanged. brand.json schema: - New variant: Brand Canonical Document. Self-published per-brand doc with `parent_house: BrandRef` pointer + optional house_attributes_overrides. Composes the existing `brand` definition via allOf so identity fields match the inline `brands[]` shape. - House Portfolio variant gains `brand_refs[]` (pointer brands, child-owned data) and `house_attributes` (house-wide inheritable attributes). Required changed from ["house","brands"] to ["house"] with anyOf at-least-one of brands[]/brand_refs[]. - Two new examples illustrating mixed inline+pointer house and a self- published Converse canonical document. docs/brand-protocol/brand-json.mdx: added a "Proposed (RFC)" callout pointing at the RFC and PR #3533. Existing four variants documented as-is. Cross-array invariant — a brand_id MUST NOT appear in both brands[] and brand_refs[] of the same house — is documented in field descriptions. JSON Schema can't express it; lint/validator follow-up needed if RFC ratifies. Status: review-only. Not normative until RFC ratifies. Marked DRAFT. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(brand-protocol): tighten Brand Canonical Document variant + fix parent_house example Code-reviewer + protocol-expert findings on PR #3764: - Brand Canonical Document variant lacked oneOf disambiguation. brand definition has additionalProperties: true and the inner allOf member had no constraint, so a malformed Portfolio with a stray parent_house could silently re-type as a Canonical Document. Replaced the narrow not: {required: ["brand_refs"]} with not: {anyOf: [...]} blocking all house-only top-level keys (house, brands, brand_refs, house_attributes, authorized_operators). - Fixed example: parent_house: { domain: "nikeinc.com", brand_id: "converse" } read as "I'm pointing at converse inside nikeinc.com" — but Converse owns this document; the brand_id was the self, not the parent. parent_house is a pointer UP, only domain is meaningful for a typical house pointer. Updated both the schema example and the docs section. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(brand-protocol): align impl with RFC v3 — house_domain string + managed_by + drop house_attributes Tracks RFC #3533 v3. Replaces the v2 cut with the simplified model decided during expert review. Schema deltas (relative to previous impl cut): - Replace parent_house ($ref core/brand-ref.json) with house_domain (string, reuses #/definitions/domain). Strings match the existing House Redirect convention and drop the planned core/house-ref.json file entirely. - Make house_domain optional on Brand Canonical Document so standalone brands (Patagonia, Liquid Death) have a valid shape without spinning up a degenerate "house of one." If a standalone brand is later acquired, it adds house_domain and the new house adds it to brand_refs[]. No new variant needed. - Drop house_attributes / house_attributes_overrides entirely. Inheritance/ override semantics turned out muddy — if a brand could weaken a house policy, it wasn't really a house policy. House-level fields stay where they already are (data_subject_contestation, trademarks, authorized_operators on the house schema). Brand-level constraints are additive, not overrides. - Replace the brand_refs[] $ref to core/brand-ref.json with an inline {domain, brand_id?, managed_by?} shape. brand-ref.json's existing governance-override fields (industries, data_subject_contestation) don't belong on a house-side declaration; they're consumer-side overrides. - Add managed_by (string, optional) on brand_refs[] entries — house-declared delegation for grouping/discovery, non-trust-bearing. Captures WPP/Publicis reality (BBH manages this brand for WPP) without reintroducing recursive trust. Examples updated: - Nike Inc. mixed-shape (inline Nike SB + pointer Converse, Jordan) - WPP with managed_by (BBH Sport managed_by bbh.com, etc.) - Converse self-published with house_domain - Patagonia standalone (no house_domain) docs/brand-protocol/brand-json.mdx: rewritten Distributed Extensions section to match the new shape. Adds the four worked examples. Adds field-resolution table showing where each consumer-side question is answered (no inheritance, no overrides). Adds explicit M&A section showing existing redirect variants handle reorganizations. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(brand-protocol): RFC v3 tightening — normative managed_by, conformance, strictest-of compliance Addresses round-2 expert review on RFC v3: - managed_by gets normative language: MUST NOT be used for trust/auth decisions; verifiers MUST ignore for authorization. UIs SHOULD render as unilateral house claim, not aggregate cross-house. - New "Compliance fields: strictest-of resolution" section. Identity fields (logos, voice, tone) stay brand-wins. Compliance/governance fields (data_subject_contestation, compliance_policies, regulated-category flags) resolve as union/strictest of house-level and brand-level — brand cannot weaken house assertions, only add stricter constraints. - New "Standalone brands" subsection: absence of house_domain ⇒ standalone, regardless of one-sided third-party brand_refs[] claims. - New "Conformance" section formalizing brand_id cross-array uniqueness, within-array uniqueness, mutual-assertion as canonical trust primitive, managed_by non-trust, standalone trumps third-party claim, strictest-of rule, 180-day TTL. - New "Prior art" section citing IAB Tech Lab ads.txt/sellers.json reciprocal publication model — same trust shape, deployed industry pattern. No structural changes to the data model. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(brand-protocol): schema review fixes — extract brand_ref definition + tighten not deny-list Code-reviewer round-2 nits on PR #3764: - Extract brand_refs[] item shape from inline to #/definitions/brand_ref (named type for SDK codegen / generator output). Description includes the managed_by non-trust normative language directly on the field. - Tighten Brand Canonical Document not.anyOf to also block House Redirect's region and note keys. Disambiguation against House Redirect was already clean via id+names requirement, but the deny-list now matches all four other variants' top-level fields explicitly. No example or behavior changes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(brand-protocol): ratify distributed brand.json + fold RFC + typed brand-level trademarks Consolidates #3409 / #3533 (RFC) / #3764 (schema cut) / #3910 (fold) / #3909 (typed trademarks) into one normative PR. **Fold (#3910):** RFC content (Motivation, Conformance, strictest-of compliance resolution, Prior art) absorbed into docs/brand-protocol/brand-json.mdx as the normative spec. Proposals subdir and the standalone RFC file deleted. Variant list now reads as five variants from the top; Brand Canonical Document slots in as #5 alongside the other four. **Typed trademarks (#3909):** New #/definitions/trademark extracts the inline house-portfolio shape ({registry, number, mark}) with optional status, license_type, countries. brand definition gains trademarks: Trademark[]. House Portfolio's inline trademarks[] migrated to $ref. House-level + brand- level resolution is union (both lists are valid claims). Existing publishers are unaffected — all additions are optional, the existing four variants are unchanged, and the inline trademark shape continues to validate against the extracted definition. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(brand-protocol): expert-review fixes — third trust tier, schema tightening, conformance polish Folds protocol/product/docs expert review on PR #4505. No design rollbacks; all changes tightening or expanding. Schema - Rename #/definitions/brand_ref → portfolio_entry (disambiguate from buyer-side core/brand-ref.json which identifies brands in media plans). Make brand_id required on portfolio_entry so cross-array invariant is enforceable. Add optional effective_at (ISO 8601) so consumers age edges from a publisher-anchored date. - Brand Canonical Document not.anyOf deny-list adds authoritative_location, redirect_reason, redirect_effective_at — closes ambiguous-match holes against redirect variants. - Trademark gains optional licensor_domain (when license_type=licensed_in) and nice_classes (Nice Classification for cross-industry disambiguation — Delta-airline vs Delta-faucet). Trust model - Three-tier trust table: brand-identity (TLS-only) and brand-relationships (mutual-assertion-gated) resolve separately. A leaf-only edge keeps identity trust; only relationships block. - Self-healing notification SHOULD: consumers SHOULD email the house's contact.email when they encounter a leaf-only edge, so the parent team can complete the reciprocal entry. Rate-limited per {leaf, house}. - managed_by reframed as a directory field (aggregation across houses is the intended use); MUST NOT trust kept, SHOULD NOT aggregate dropped — the latter was fiction. Conformance - New invariants: house_domain MUST NOT appear in brands[]; brand_refs[] unique by domain (not just brand_id); mutual-assertion verification MUST follow House Redirects on the house side; strictest-of compliance expanded to include policy_categories and brand-level disclaimers[]; edge-aging language reframed around effective_at rather than a fixed 180-day SHOULD. - Resolution algorithm: "resolve recursively (single hop)" → "resolve once" with explicit clause that the followed document MUST be a Brand Canonical Document. Docs / structure - Variant 4/5 cross-reference trust model forward instead of asserting it inline — variant 5 reads cleanly when first encountered. - Variant 5 field table fully enumerates top-level fields and explicitly lists prohibited fields. - New "Adopting brand_refs[] for an existing portfolio" subsection documenting the migration path and AAO registry behaviour. - New "Out of scope" subsection: JVs with two parents, PE-opacity rollups, jurisdictional governance divergence — explicitly outside brand.json's scope (brand identity ≠ corporate legal structure). - Prior art expanded with app-ads.txt and WebFinger / host-meta (RFC 7033, 6415) as IETF analogues for well-known + JSON resource discovery. - Frontmatter description and managed_by prose updated. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(release-notes): kick off 3.1.0 entry with distributed brand.json headline 3.1 doesn't ship today; minor releases accumulate from changesets. This opens the 3.1 section with PR #4505 as the headline feature so the narrative space exists when subsequent 3.1 changesets land. Includes adopter-action table for the publisher-visible behaviour change (trademark string drift) and links to the four design follow-ups (#4521 verification endpoint, #4522 JV multi-parent, #4523 PE-opacity tradeoff, #4524 manager-edge reciprocation). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Draft / do-not-merge. Implementation cut for RFC #3533. Lets reviewers see the concrete schema shape and signal formal approval against actual JSON Schema diffs rather than prose.
Pairs with #3533 (RFC). This PR will land only after the RFC ratifies.
What's in the schema
Additive additions to
static/schemas/source/brand.json. Existing publishers and existing four variants are unchanged.New variant — Brand Canonical Document
Self-published per-brand document carrying the brand's identity attributes plus:
parent_house: BrandRef— pointer to the corporate house this brand belongs to. The named house'sbrand_refs[]MUST reciprocate for mutual-assertion trust.house_attributes_overrides— per-brand overrides for inheritedhouse_attributes.Composes the existing
branddefinition viaallOfso a self-published canonical document carries the same identity fields abrands[]entry would.House Portfolio variant additions
brand_refs: BrandRef[]— pointer brands (child-owned data). Each entry references a brand whose canonical document is published elsewhere.house_attributes— house-wide inheritable attributes (privacy policy, compliance flags, corporate legal entity).requiredwidened from["house", "brands"]to["house"]withanyOfrequiring at least one ofbrands[]orbrand_refs[].Cross-array invariant
A
brand_idMUST NOT appear in bothbrands[]andbrand_refs[]of the same house. Documented in field descriptions; JSON Schema can't express it directly. If the RFC ratifies, follow-up work adds a lint/validator rule.Examples
Two new entries in the schema's
examplesarray illustrating:brands[]: [Nike SB inline]andbrand_refs[]: [converse, jordan]).parent_house: { domain: nikeinc.com }).Docs
docs/brand-protocol/brand-json.mdxgets a "Proposed (RFC)" callout pointing at the RFC and this PR. Existing four-variant documentation stays unchanged.Validation
npm run build:schemas— compilesnpm run test:schemas— 7/7 passes (schemas valid, refs resolve, examples validate)npm run test:examples— 34/34 passesnpm run test:json-schema— 255 $schema-tagged JSON blocks validatenpm run test:composed— 32/32 passes (bundled schemas compile standalone)npm run check:owned-links— cleannpm run typecheck— cleanTest plan
brand_refs[]should referencecore/brand-ref.json(current choice) or get its own type🤖 Generated with Claude Code