Skip to content

fix: show official owner badges across public surfaces#2407

Open
jesse-merhi wants to merge 1 commit into
mainfrom
jesse/official-badge-surfaces
Open

fix: show official owner badges across public surfaces#2407
jesse-merhi wants to merge 1 commit into
mainfrom
jesse/official-badge-surfaces

Conversation

@jesse-merhi
Copy link
Copy Markdown
Member

@jesse-merhi jesse-merhi commented May 26, 2026

Summary

  • Compute owner.official from the existing hard-coded OpenClaw policy: the openclaw org itself and users who are members of that publisher.
  • Render compact Official badges when either the skill/package is explicitly official or its owner is official by policy.
  • Keep local dev fixtures honest: local-agentic-risk-demo is no longer seeded as official and the local seed removes stale local membership in the openclaw publisher.

Intentional Surfaces

The broader coverage is intentional. buildPublicSkillEntryFromDigest is the shared entry builder for digest-backed list/card surfaces, so adding owner.official there keeps the Official badge consistent instead of fixing only one page.

Surface What changed Why this is in scope
Public skill detail: /$owner/$slug via skills.getBySlug Skill detail owners now include computed owner.official. Fixes member-owned skill detail pages like /steipete/codex-owner-move-debug-1778257045, where the owner is official even when the skill has no explicit skill.badges.official.
Main browse: /skills Skill rows built from digests can show the compact owner Official badge. A member-owned skill should not lose the badge just because it is shown in the browse list.
Sorted/filtered browse: /skills?sort=... and tag/search filters The same digest-backed result rows include owner.official. Sorting or filtering should not change whether the owner badge appears.
Related skills Related skill cards receive the same computed owner badge data. Detail-page recommendations should use the same trust signal as the main browse page.
Trending skills Trending skill cards receive the same computed owner badge data. Trending is another public discovery surface backed by the shared entry shape.
Highlighted skills Highlighted cards receive the same computed owner badge data. Editorial/discovery cards should not hide the owner badge while normal cards show it.
Audit rows Audit list entries receive the same computed owner badge data. Reviewer-facing rows should show the same owner status as public list rows.
Public package detail API: /api/v1/packages/:name Package/skill package detail owner JSON can include owner.official. Plugin pages load through the HTTP package API, so /plugins/@openclaw/whatsapp needs this field to render the compact owner badge.
Plugin detail UI: /plugins/$name Owner metadata renders the compact Official badge when owner.official is true. The title-level Official tag already existed; this adds the missing owner-level badge.
Skill card UI: SkillListItem and SkillCard browse results Badge calculation accepts owner metadata as well as skill badges. A member-owned skill without skill.badges.official now renders Official consistently.
Local dev seed data Removes explicit local official skill badges and stale local membership in openclaw. Prevents local moderation fixtures from being marked Official just because test data drifted.

Before / After Evidence

Screenshots were captured locally from true before/after runs:

  • Before: current origin/main functions + origin/main frontend on http://127.0.0.1:3010
  • After: this PR's functions + this PR's frontend on http://127.0.0.1:3011
  • Image files are hosted on the evidence branch pr-evidence/2407-official-badges so they render directly in this PR description without adding screenshots to this PR diff.
Area Before After
Main browse: /skills?sort=downloads Codex Owner Move Debug: 0 Official badges.
Local Agentic Risk Demo: incorrectly 1 Official badge.
Before main browse downloads
Codex Owner Move Debug: 1 Official badge.
Local Agentic Risk Demo: 0 Official badges.
After main browse downloads
Member-owned skill detail: /steipete/codex-owner-move-debug-1778257045 0 compact Official badges, 0 title Official tags.
Before steipete skill detail
2 compact Official badges, 1 title Official tag.
After steipete skill detail
OpenClaw plugin detail: /plugins/@openclaw/whatsapp Title Official tag existed; owner metadata only had 1 Official mark total.
Before OpenClaw plugin detail
Title Official tag remains; owner metadata adds the missing compact badge, 2 Official marks total.
After OpenClaw plugin detail
Non-official local fixture: /local/local-agentic-risk-demo Incorrectly official: 1 compact Official badge and 1 title Official tag.
Before local non-official fixture
Not official: 0 compact Official badges and 0 title Official tags.
After local non-official fixture

Validation

  • bun run test convex/skills.public.test.ts convex/skills.publicListCursor.test.ts convex/httpApiV1.handlers.test.ts packages/schema/src/schemas.test.ts src/components/SkillListItem.test.tsx src/__tests__/package-detail-route.test.tsx
  • bun run format:check
  • bun run lint
  • VITE_CONVEX_URL=https://example.invalid bunx tsc --noEmit
  • bunx tsc -p packages/schema/tsconfig.json --noEmit
  • bunx tsc -p packages/clawhub/tsconfig.json --noEmit
  • bunx convex dev --once
  • bunx convex run devSeed:seedLocalFixtures '{"reset":false}'
  • Playwright before/after screenshot capture at 1440x900 for the four documented routes.
  • Playwright after-state viewport audit at 390x844, 768x1024, and 1440x900: no horizontal overflow and no console errors after ignoring the existing dev-only theme hydration warning.

Copilot AI review requested due to automatic review settings May 26, 2026 13:53
@jesse-merhi jesse-merhi requested review from a team and Patrick-Erichsen as code owners May 26, 2026 13:53
@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented May 26, 2026

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

Project Deployment Actions Updated (UTC)
clawhub Ready Ready Preview, Comment Jun 1, 2026 8:34pm

@clawsweeper
Copy link
Copy Markdown

clawsweeper Bot commented May 26, 2026

Codex review: needs changes before merge. Reviewed June 1, 2026, 5:01 PM ET / 21:01 UTC.

Summary
The PR propagates policy-derived owner.official into public skill/package responses, renders compact owner Official badges on skill and plugin surfaces, updates schema/test coverage, and cleans local seed official fixture data.

Reproducibility: yes. Current main source omits owner.official from the affected list/detail paths, and the inspected before/after screenshot evidence shows the badge behavior changing after the PR; I did not run the app locally.

Review metrics: 2 noteworthy metrics.

  • Changed surface: 18 files, +349/-32. The diff spans Convex APIs, schema output, UI components/routes, seed data, and tests, so the review needs to cover both runtime behavior and public contract shape.
  • List fan-out ceiling: up to 200 entries per page. The new official-owner lookup runs inside the shared public list-entry builder, so page size directly affects query read fan-out.

Merge readiness
Overall: 🦐 gold shrimp
Proof: 🦞 diamond lobster ✨ media proof bonus
Patch quality: 🦐 gold shrimp
Result: needs maintainer review before merge.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • Cache or denormalize official-owner status for digest-backed list builders and cover repeated-owner/read fan-out in tests.
  • Correct both package owner casts to use publisher ids or a shared public publisher type.

Risk before merge

  • [P1] Merging as-is can add hundreds of extra indexed reads on common public browse/list responses because official-owner membership is recomputed for each digest entry rather than cached or denormalized.
  • [P1] The package-detail and npm mirror handler casts still describe publisher ids as user ids, leaving a misleading source of truth for future API edits and mocks.

Maintainer options:

  1. Cache official-owner lookups before merge (recommended)
    Cache official org lookups and membership checks per query, or denormalize the policy result into the digest, then keep the badge/UI changes on top of that bounded read pattern.
  2. Accept temporary list-read cost
    Maintainers could merge the visual fix while knowingly accepting higher public-list query latency and read-limit exposure until a follow-up performance patch lands.
  3. Pause until the list contract is settled
    If maintainers prefer a denormalized digest field instead of request-time membership checks, pause this PR and decide that contract before extending more public surfaces.
Copy recommended automerge instruction
@clawsweeper automerge

Special instructions:
Cache official publisher lookups for digest-backed skill list builders by official org handle and linkedUserId, correct both package owner casts to use publisher ids or a shared PublicPublisher shape, and add focused tests that repeated-owner list pages do not perform per-entry official-org/membership fan-out.

Next step before merge

  • The remaining blockers are narrow mechanical fixes on the PR branch: bound official-owner list lookups and correct package owner id types.

Security
Cleared: No concrete security or supply-chain concern was found; the trust signal remains computed from server-side publisher policy rather than uploaded metadata.

Review findings

  • [P1] Cache official owner lookups on public digest lists — convex/skills.ts:5191-5195
  • [P3] Use publisher ids in package owner casts — convex/httpApiV1/packagesV1.ts:2666
Review details

Best possible solution:

Land the owner-official badge propagation with per-query cached or denormalized official status and correct publisher-id typing, while keeping server-side official policy as the only trust source.

Do we have a high-confidence way to reproduce the issue?

Yes. Current main source omits owner.official from the affected list/detail paths, and the inspected before/after screenshot evidence shows the badge behavior changing after the PR; I did not run the app locally.

Is this the best way to solve the issue?

No, not as-is. The product direction matches the official-publisher specs, but the implementation should bound list-query reads and fix the package owner id type before merge.

Full review comments:

  • [P1] Cache official owner lookups on public digest lists — convex/skills.ts:5191-5195
    buildPublicSkillEntryFromDigest now calls isOfficialPublisher for every digest returned by browse, related, trending, and audit queries. For personal publishers, that helper reads official org rows and publisher membership, so a 200-item public page can add hundreds of serial reads and increase latency/read-limit exposure on hot pages; cache the official org and membership results per query or denormalize this signal before returning list entries.
    Confidence: 0.9
  • [P3] Use publisher ids in package owner casts — convex/httpApiV1/packagesV1.ts:2666
    getByNameForViewerInternal returns a PublicPublisher, whose _id is Id<"publishers">, but this cast still declares Id<"users"> and the same pattern is repeated in the npm mirror handler. That keeps tests and future handler edits pointed at the wrong owner identity; change both casts to Id<"publishers"> or a shared PublicPublisher shape.
    Confidence: 0.94

Overall correctness: patch is incorrect
Overall confidence: 0.89

AGENTS.md: found and applied where relevant.

Codex review notes: model gpt-5.5, reasoning high; reviewed against cb6ced7906f7.

Label changes

Label justifications:

  • P2: This is a normal-priority public trust-signal bug fix with limited blast radius, but it needs focused performance/type repairs before merge.
  • merge-risk: 🚨 availability: The branch adds per-digest official-owner database lookups to hot public list surfaces, which can increase latency and Convex read-limit exposure.
  • rating: 🦐 gold shrimp: Overall readiness is 🦐 gold shrimp; proof is 🦞 diamond lobster and patch quality is 🦐 gold shrimp.
  • status: ⏳ waiting on author: ClawSweeper has contributor-facing work open and is waiting for author action. Sufficient (screenshot): The PR body includes before/after screenshot proof, and inspected after screenshots show the intended Official badges on browse, skill detail, and plugin detail surfaces.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes before/after screenshot proof, and inspected after screenshots show the intended Official badges on browse, skill detail, and plugin detail surfaces.
  • proof: 📸 screenshot: Contributor real behavior proof includes screenshot evidence. The PR body includes before/after screenshot proof, and inspected after screenshots show the intended Official badges on browse, skill detail, and plugin detail surfaces.
Evidence reviewed

Acceptance criteria:

  • [P1] bun run test convex/skills.public.test.ts convex/skills.publicListCursor.test.ts convex/httpApiV1.handlers.test.ts packages/schema/src/schemas.test.ts src/components/SkillListItem.test.tsx src/tests/package-detail-route.test.tsx.
  • [P1] bun run format:check -- convex/skills.ts convex/httpApiV1/packagesV1.ts convex/skills.publicListCursor.test.ts convex/httpApiV1.handlers.test.ts.
  • [P1] bun run lint.
  • [P1] VITE_CONVEX_URL=https://example.invalid bunx tsc --noEmit.
  • [P1] bunx tsc -p packages/schema/tsconfig.json --noEmit.

What I checked:

  • Repo policy applied: Read the full repository AGENTS.md and applied its Convex query/bandwidth guidance and public trust-signal/spec guidance during the review. (AGENTS.md:1, cb6ced7906f7)
  • Convex guidance applied: Read the generated Convex guidelines; the query guidance reinforces avoiding unbounded or excessive per-row reads in hot query paths. (convex/_generated/ai/guidelines.md:1, cb6ced7906f7)
  • Current main lacks owner official badges on list cards: Current main calls getSkillBadges(skill) without owner metadata in the list item and grid card paths, so owner-official status cannot affect those badges today. (src/components/SkillListItem.tsx:24, cb6ced7906f7)
  • Current main omits owner official status in detail data: Current main builds skill detail owners with toPublicPublisher(ownerPublisher) and package API owners without an official field, so the central request is not already implemented on main. (convex/skills.ts:2300, cb6ced7906f7)
  • Policy supports owner-official trust signal: The official-publishers spec defines official as a server-side publisher policy flag for org publishers and personal publishers for current official-org members, not uploaded metadata. (specs/official-publishers.md:1, cb6ced7906f7)
  • PR adds per-digest official lookups: The branch calls isOfficialPublisher inside buildPublicSkillEntryFromDigest, which is shared by browse, related, trending, and audit list builders. (convex/skills.ts:5191, f701e079fc5d)

Likely related people:

  • Patrick Erichsen: Introduced the current digest-backed list builder and package API cast in commit 1f56a71 and has recent adjacent work across public skills/package HTTP paths. (role: recent area contributor; confidence: high; commits: 1f56a7143043, 667bc55299c4, cc16d7fbd924; files: convex/skills.ts, convex/httpApiV1/packagesV1.ts)
  • Jesse Merhi: Introduced the official publisher policy helper on main in commit 90de729, so they are connected to the policy source this PR reuses beyond merely authoring this branch. (role: official policy contributor; confidence: high; commits: 90de729fe135, f701e079fc5d; files: convex/lib/officialPublishers.ts, convex/skills.ts, convex/packages.ts)
  • Vyctor H. Brzezowski: Recently changed the shared digest list-builder region while guarding moderated skill files and tags, so they are a useful routing candidate for public skill list behavior. (role: recent adjacent contributor; confidence: medium; commits: 97023d3123f4; files: convex/skills.ts)
  • Peter Steinberger: Recently worked in the package HTTP API area that this PR extends, including package artifact handling near the same handler surface. (role: recent package API contributor; confidence: medium; commits: 23932ec7de88, cbbb6e7a6182; files: convex/httpApiV1/packagesV1.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR ensures “official” publisher status is preserved end-to-end (Convex public queries → HTTP v1 responses → shared schema parsing → UI rendering) so detail pages can render Official owner badges correctly, including member-owned content like the reported steipete skill.

Changes:

  • Compute and return owner.official for public skill/package detail queries via toPublicPublisherWithOfficial.
  • Extend /api/v1/packages/:name (and skill fallback) responses + ApiV1PackageResponseSchema to include optional owner.official.
  • Render the compact OfficialBadge in the plugin detail owner metadata row and add targeted tests.

Reviewed changes

Copilot reviewed 10 out of 14 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/routes/plugins/$name.tsx Renders compact Official badge next to owner name when owner.official is true.
src/tests/package-detail-route.test.tsx Adds route test asserting owner compact badge + title Official tag for official publishers.
packages/schema/src/schemas.test.ts Adds schema parse test for v1 package owner official metadata.
packages/schema/src/packages.ts Extends ApiV1PackageResponseSchema.owner with optional official.
packages/schema/dist/packages.js.map Updates built sourcemap output for schema changes.
packages/schema/dist/packages.js Updates built JS output for schema changes.
packages/schema/dist/packages.d.ts Updates built type declarations for schema changes.
convex/skills.ts Uses toPublicPublisherWithOfficial so skill owners preserve computed official status.
convex/skills.public.test.ts Adds coverage for member-owned publishers being marked official in public skill responses.
convex/packages.ts Uses toPublicPublisherWithOfficial so package owners preserve computed official status.
convex/packages.public.test.ts Adds coverage asserting official owners in public package detail.
convex/httpApiV1/packagesV1.ts Threads owner.official through v1 package/skill detail responses.
convex/httpApiV1.handlers.test.ts Updates handler tests to assert owner.official is emitted in relevant responses.
convex/_generated/api.d.ts Updates generated API typings to include the official publishers lib module.
Comments suppressed due to low confidence (1)

convex/packages.ts:2164

  • This loop now calls toPublicPublisherWithOfficial for every package. Because official detection can require extra DB queries for user publishers, this can introduce an N+1 query pattern on the audit page. Consider adding a small cache for the official-org publisher doc and membership checks (similar to membershipCache) so repeated owners don’t trigger repeated lookups within the same request.
    for (const pkg of result.page) {
      if (pkg.family === "skill") continue;
      if (!(await canViewerReadPackage(ctx, pkg, undefined, membershipCache))) continue;

      const owner = await toPublicPublisherWithOfficial(
        ctx,
        await getOwnerPublisher(ctx, {
          ownerPublisherId: pkg.ownerPublisherId,
          ownerUserId: pkg.ownerUserId,
        }),
      );

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +2709 to +2715
owner: {
_id: Id<"users">;
handle?: string;
displayName?: string;
image?: string;
official?: boolean;
} | null;
Comment on lines +3187 to +3193
owner: {
_id: Id<"users">;
handle?: string;
displayName?: string;
image?: string;
official?: boolean;
} | null;
Comment thread convex/skills.ts
Comment on lines 1849 to 1854
const ownerPromise = getOwnerPublisher(ctx, {
ownerPublisherId,
ownerUserId,
}).then((ownerDoc) => {
const publicOwner = toPublicPublisher(ownerDoc);
}).then(async (ownerDoc) => {
const publicOwner = await toPublicPublisherWithOfficial(ctx, ownerDoc);
if (!publicOwner) {
@clawsweeper clawsweeper Bot added rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. labels May 26, 2026
@clawsweeper
Copy link
Copy Markdown

clawsweeper Bot commented May 26, 2026

ClawSweeper PR egg

🔥 Warming up: real-behavior proof passed; findings, security review, or rank-up moves are still in progress.

Hatch command

Comment @clawsweeper hatch when this PR is hatchable.

Hatchability rules:

  • Merged PRs are hatchable.
  • Open PRs are hatchable when they are status: 👀 ready for maintainer look, status: 🚀 automerge armed, or labeled clawsweeper:automerge.
  • Closed unmerged PRs are hatchable only when one of those hatchable labels is still present in the durable record.
What is this egg doing here?
  • Eggs appear after the PR passes real-behavior proof. It is here for vibes, not verdicts: it does not change labels, ratings, merge decisions, or automation.
  • The shell reacts to review momentum: open follow-up work warms it up, re-review makes it wobble, and a clean final review lets it hatch.
  • Hatchability usually comes from sufficient real-behavior proof, no blocking P0/P1/P2 findings, no security attention needed, and clean correctness. A merged PR is already final, so merge makes the egg hatchable independently.
  • The hatch is seeded from this repository and PR number, so the same PR keeps the same creature; the reviewed head SHA can only change safe visual details.
  • Rarity is just collectible sparkle: 🥚 common, 🌱 uncommon, 💎 rare, ✨ glimmer, and 🌈 legendary.

@jesse-merhi jesse-merhi force-pushed the jesse/official-badge-surfaces branch from 1d1425c to 0b840fc Compare May 26, 2026 14:30
@jesse-merhi jesse-merhi changed the title fix: show official owner badges on detail pages fix: show official owner badges across public surfaces May 26, 2026
@clawsweeper clawsweeper Bot added the P2 Normal backlog priority with limited blast radius. label May 26, 2026
@jesse-merhi jesse-merhi force-pushed the jesse/official-badge-surfaces branch from 0b840fc to 3a175dd Compare May 26, 2026 15:06
@clawsweeper clawsweeper Bot added proof: sufficient Contributor real behavior proof is sufficient. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. merge-risk: 🚨 availability 🚨 Merging this PR could cause crashes, hangs, restart loops, stalls, or process outages. and removed rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. labels May 26, 2026
@jesse-merhi jesse-merhi force-pushed the jesse/official-badge-surfaces branch from 3a175dd to f701e07 Compare June 1, 2026 20:34
@clawsweeper clawsweeper Bot added the proof: 📸 screenshot Contributor real behavior proof includes screenshot evidence. label Jun 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge-risk: 🚨 availability 🚨 Merging this PR could cause crashes, hangs, restart loops, stalls, or process outages. P2 Normal backlog priority with limited blast radius. proof: 📸 screenshot Contributor real behavior proof includes screenshot evidence. proof: sufficient Contributor real behavior proof is sufficient. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants