chore(scripts): audit + reconcile brand-domain www/no-www mismatch (#4448)#4457
Merged
Conversation
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>
`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>
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>
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>
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>
…n 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>
Contributor
Author
|
Code review of Ship. Verified concerns:
Nits (not blockers, no change needed for this artifact):
Ready to push. |
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
Closes #4448. Two scripts and one applied prod reconciliation:
server/src/scripts/audit-brand-domain-www-mismatch.ts— read-only audit identifying orgs whose pastbrand_revisionslive on a different brand domain than their currentorganization_domains.is_primary=truerow. Tags each row withmanifest_orphanedstatus so future runs distinguish "still drifting" from "historical residue".server/src/scripts/reconcile-brand-domain-www-mismatch.ts— one-shot reconciliation, hardcoded to the three orgs the audit surfaced. Per affected org: copiesbrand_manifest.agentsfromwww.<domain>→<domain>(deduped on URL), marks the www brand rowmanifest_orphaned=true, inserts abrand_domain_aliasesrow routing www → root. Idempotent; dry-run by default;--applyto persist.Applied on prod 2026-05-12 11:33 UTC
Run via
fly ssh console -a adcp-docsagainst machine6835e5ef7e3548:Post-apply verification:
affinityanswers.combrand row: 1 agent in manifest, orphaned=falsewww.affinityanswers.combrand row: 1 agent (preserved), orphaned=truebidmachine.iobrand row: 1 agent in manifest, orphaned=falsewww.bidmachine.iobrand row: 1 agent (preserved), orphaned=truescope3.combrand row: 0 agents, orphaned=false (unchanged)www.scope3.combrand row: 0 agents, orphaned=trueGET /api/registry/agents?limit=500now surfaceshttps://adcp.bidmachine.io/adcp/mcp(BidMachine's JSONB saidvisibility: publicall along, but their root brand.json was empty — they were "publicly listed" in the dashboard yet invisible to the catalog. They're now actually publicly listed.)Affinity Answers (escalation #334)
This unblocks Santhosh's publish flow. Their member_profiles JSONB is still at
visibility: members_only— they need to retry the publish action from the dashboard. The path now succeeds end-to-end becausegetBrandPrimaryDomainreturnsaffinityanswers.comand the brand row at that domain is ready to receive the agent.Not in this PR
email_domain = "www.scope3.com"drift (separate column inconsistency from the "four overlapping domain columns" pattern) — out of scope; would need a careful update path that doesn't trip the FK/trigger machinery.organization_domains.is_primaryis set to the same canonical brand-domain variant the registry crawler uses. The three affected orgs were a one-time backlog from the column drop, not an ongoing source.Test plan
[RECONCILED — www row orphaned]tag on all 3 entries/api/registry/agents) shows BidMachine's previously-strandedpublicagent🤖 Generated with Claude Code