Fix allOf + additionalProperties validation and add oneOf titles#334
Merged
Conversation
Fixes JSON Schema validation failures when using allOf composition with
additionalProperties: false. Each sub-schema was independently rejecting
the other's properties.
**Schema fixes:**
- dimensions.json: removed additionalProperties: false (composition-only)
- push-notification-config.json: removed additionalProperties: false
- video-asset.json: inlined width/height, removed allOf
- image-asset.json: inlined width/height, removed allOf
**Bundled schemas:**
- Added bundled schemas at /schemas/{version}/bundled/
- Only bundles request/response schemas (root schemas)
- Core objects are embedded inside response schemas when bundled
- Custom resolveRefs() handles multiple refs to same schema correctly
- Generated at build time for tools that don't support $ref resolution
**Testing:**
- New test:composed script validates allOf composition patterns
- Tests bundled schemas work without $ref resolution
- Added to CI test suite
Fixes #275.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds explicit title properties to all oneOf variants in response schemas so downstream UI tools display meaningful labels instead of "Option 1", "Option 2" when rendering discriminated unions. Schemas updated: - create-media-buy-response: CreateMediaBuySuccess/Error - update-media-buy-response: UpdateMediaBuySuccess/Error - sync-creatives-response: SyncCreativesSuccess/Error - build-creative-response: BuildCreativeSuccess/Error - provide-performance-feedback-response: ProvidePerformanceFeedbackSuccess/Error - activate-signal-response: ActivateSignalSuccess/Error - preview-creative-response: PreviewCreativeSingleResponse/BatchResponse + nested batch result titles 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The dimensions schema was no longer referenced after inlining width/height properties directly into video-asset.json and image-asset.json. This removes the unused schema file and updates tests accordingly. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds a warning that bundled schemas are experimental and may change, with a link to share feedback on use cases. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This was referenced May 12, 2026
bokelley
added a commit
that referenced
this pull request
May 12, 2026
* fix(copy): clarify which tiers qualify for public agent listing The publish-agent gate accepts four API-access tiers (Professional, Builder, Member, Leader) per server/src/services/membership-tiers.ts. User-facing copy across 11 surfaces said "Professional tier or higher", which Addie and customers read as "tiers priced above Professional" — the reverse of the actual ordering, since Builder ($3K), Member ($15K), and Leader ($50K) all cost more than Professional ($250) but qualify. Affinity Answers (escalation #334, Builder plan) was told by Addie to upgrade to Professional after failing to publish. Builder always qualified; the copy misled both the user and the agent. Replace the phrase with explicit lists ("Professional, Builder, Member, or Leader" / "paying AAO members") across error messages, dashboard tooltips, Addie's behavior rules, OpenAPI schema descriptions, and the registry-of-an-agent doc. No behavior change; tests assert on the `tier_required` error code, not the message text. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(docs): regenerate addie-tools.mdx after save_agent description fix `docs/aao/addie-tools.mdx` is auto-generated from MCP tool descriptions. The `save_agent` fix in the prior commit only updated the source file (`server/src/addie/mcp/member-tools.ts`); the published doc still carried the misleading "Professional-tier (or higher)" phrasing. This runs `npm run build:addie-tools` to reflect the updated description. The regen also picks up unrelated pre-existing drift (the `list_github_issues` description and the new `agent_conformance` tool set) that had not yet been regenerated. CI doesn't gate on this, so the drift was silently accumulating; folding the catch-up in here. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(copy): update OpenAPI YAML tier-copy descriptions Two surgical edits in the published `static/openapi/registry.yaml` mirroring the source descriptions in `server/src/schemas/member-agents-openapi.ts`. The full regen output includes a lot of unrelated pre-existing drift (new fields like `discovery_method`, `resolved_url`, `self_redirected` hosting mode); keeping those out of this copy-fix PR — they should land in a separate regen-only PR. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 tasks
bokelley
added a commit
that referenced
this pull request
May 12, 2026
…4448) (#4457) * fix(copy): clarify which tiers qualify for public agent listing The publish-agent gate accepts four API-access tiers (Professional, Builder, Member, Leader) per server/src/services/membership-tiers.ts. User-facing copy across 11 surfaces said "Professional tier or higher", which Addie and customers read as "tiers priced above Professional" — the reverse of the actual ordering, since Builder ($3K), Member ($15K), and Leader ($50K) all cost more than Professional ($250) but qualify. Affinity Answers (escalation #334, Builder plan) was told by Addie to upgrade to Professional after failing to publish. Builder always qualified; the copy misled both the user and the agent. Replace the phrase with explicit lists ("Professional, Builder, Member, or Leader" / "paying AAO members") across error messages, dashboard tooltips, Addie's behavior rules, OpenAPI schema descriptions, and the registry-of-an-agent doc. No behavior change; tests assert on the `tier_required` error code, not the message text. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(docs): regenerate addie-tools.mdx after save_agent description fix `docs/aao/addie-tools.mdx` is auto-generated from MCP tool descriptions. The `save_agent` fix in the prior commit only updated the source file (`server/src/addie/mcp/member-tools.ts`); the published doc still carried the misleading "Professional-tier (or higher)" phrasing. This runs `npm run build:addie-tools` to reflect the updated description. The regen also picks up unrelated pre-existing drift (the `list_github_issues` description and the new `agent_conformance` tool set) that had not yet been regenerated. CI doesn't gate on this, so the drift was silently accumulating; folding the catch-up in here. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(copy): update OpenAPI YAML tier-copy descriptions Two surgical edits in the published `static/openapi/registry.yaml` mirroring the source descriptions in `server/src/schemas/member-agents-openapi.ts`. The full regen output includes a lot of unrelated pre-existing drift (new fields like `discovery_method`, `resolved_url`, `self_redirected` hosting mode); keeping those out of this copy-fix PR — they should land in a separate regen-only PR. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(scripts): audit-brand-domain-www-mismatch (#4448 scoping) Adds a read-only audit identifying orgs whose `brand_revisions` history points at a different brand domain than their `organization_domains.is_primary=true` row — most commonly `www.<domain>` vs `<domain>`. Stage 2 of #4159 (`5163d21425`, drop of `member_profiles.primary_brand_domain`) moved the publish path's brand-row authority to the resolver, so any org whose previous curation lived on the www variant now has its next publish write land on the root variant — a brand row they haven't edited. Affinity Answers (escalation #334) is the known case; this script quantifies the rest before deciding on a reconciliation strategy (flip is_primary, merge brand rows, or add a lookup-time canonicalization fallback at the publish site). Output groups results into: - `www_in_revisions` — revisions on www.<primary>; root primary - `www_in_primary` — revisions on root; www.<primary> primary - `unrelated` — different brand domain entirely (parent-portfolio / multi-brand-curation case; not a regression) Dry-run only. Run via: fly ssh console -a adcp-docs -C \\ 'node /app/dist/scripts/audit-brand-domain-www-mismatch.js' Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(scripts): reconcile-brand-domain-www-mismatch (#4448 fix) One-shot reconciliation for the three orgs identified by the audit (Affinity Answers, BidMachine, Scope3). Each affected org has brand content on `www.<domain>` while their `organization_domains.is_primary` points at `<domain>` — Stage 2 of #4159 (5163d21) moved publish-path brand-domain authority to the resolver, and the prior `member_profiles.primary_brand_domain` column had been holding the www variant for these orgs. Per affected org, the script: 1. Copies `brand_manifest.agents` from www brand row into root brand row, deduped on agent URL. 2. Marks the www brand row `manifest_orphaned=true` (the lookup helpers already skip orphaned rows; preserves history without serving from the wrong variant). 3. Inserts a `brand_domain_aliases` row (`www.<domain>` → `<domain>`). For Scope3 the alias already exists, so this step is a no-op there. Hardcoded list — re-deriving at runtime risked silently expanding scope if a new mismatch appeared between audit and apply. Audit was run via fly ssh on 2026-05-12 against prod; results captured in the script header. Dry-run by default: npx tsx server/src/scripts/reconcile-brand-domain-www-mismatch.ts npx tsx server/src/scripts/reconcile-brand-domain-www-mismatch.ts --apply fly ssh console -a adcp-docs -C \\ 'node /app/dist/scripts/reconcile-brand-domain-www-mismatch.js --apply' Idempotent: re-running is a no-op (no agents to copy, www already orphaned, alias already present). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(scripts): audit reports manifest_orphaned on the revision-domain row The reconciliation marks the www brand row `manifest_orphaned=true` and moves agents to the root row. `brand_revisions` is a permanent log, so its rows stay on `www.<domain>` — re-running the audit otherwise looks identical to the pre-fix state. This adds a `revision_row_orphaned` flag derived from `brands.manifest_orphaned` at the row the revisions are recorded against, and tags reconciled rows in the output ("[RECONCILED — www row orphaned]"). Lets a future operator distinguish "still drifting" from "historical residue". Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bokelley
added a commit
that referenced
this pull request
May 12, 2026
#4459) WorkOS's `org.domains` array order is not stable. Orgs with a verified root + a `failed` www variant can have WorkOS list the www variant first, which overwrote `organizations.email_domain` on every `organization.updated` webhook fire even though our table's `organization_domains.is_primary=true` was already pointing at the correct root. Scope3 hit this in prod (escalation #334 follow-up): WorkOS: [{domain: 'www.scope3.com', state: 'failed'}, {domain: 'scope3.com', state: 'verified'}] Our DB: organization_domains.is_primary=true → scope3.com organizations.email_domain → www.scope3.com (drifted) `upsertWorkosDomain` already preserves `is_primary` across conflicts, so our table's choice survives. Reading `email_domain` from the same canonical source (`organization_domains.is_primary=true`) keeps the two in lockstep. Fallback to `org.domains[0]` covers the initial-sync case (cold start, no is_primary row yet); in normal operation the `upsertWorkosDomain` loop sets `i===0` as primary, so the SELECT finds a row. Downstream impact: lookups like `brand-enrichment.ts`'s `WHERE email_domain = $1` were missing Scope3's org row entirely. Adds: - `server/src/scripts/sync-email-domain-from-is-primary.ts` — backfill pre-fix drift. Applied on prod 2026-05-12 11:55 UTC for Scope3. - Integration test pinning the new behavior (non-primary first in WorkOS array stays at the canonical is_primary value). Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
Summary
allOf+additionalProperties: falseconflict causing AJV validation failures (fixes allOf + additionalProperties: false in asset schemas causes validation failures for official examples #275)oneOfvariants in response schemas for better UI tooling$refresolutiondimensions.jsonschemaChanges
Schema fixes:
video-asset.jsonandimage-asset.jsoninstead of usingallOfwithdimensions.jsononeOfvariants in response schemas (e.g.,CreateMediaBuySuccess,CreateMediaBuyError) so UI tools show meaningful labels instead of "Option 1", "Option 2"dimensions.jsonschemaNew features (experimental):
build-schemas.js- creates self-contained schemas with all$refresolved inlineschema-versioning.mdxwith experimental warningTesting:
composed-schema-validation.test.jsto catchallOf+additionalPropertiesconflictsTest plan
loadSchemaconfiguration🤖 Generated with Claude Code