Skip to content

feat(platform): list knowledge skills in the marketplace and install them to AutoPilot - #14431

Open
Pwuts wants to merge 23 commits into
devfrom
pwuts/secrt-2593
Open

feat(platform): list knowledge skills in the marketplace and install them to AutoPilot#14431
Pwuts wants to merge 23 commits into
devfrom
pwuts/secrt-2593

Conversation

@Pwuts

@Pwuts Pwuts commented Sep 7, 2026

Copy link
Copy Markdown
Member

Why / What / How

The marketplace now lists knowledge skills as their own kind of thing, next to Experts to hire and Workflows to install, and a signed-in user can add one to their AutoPilot in two clicks from the front page.

A skill is instructions and examples the copilot reads before doing a job — a brand voice guide, an outreach playbook. It is not a runnable graph. The product already has skills: the copilot distils them with store_skill, they live as SKILL.md files in the user's workspace, and /library/skills lists them. What it had no way to do was get one from anywhere but your own chat history or a file on your disk.

Worse, the one place that offered a marketplace skill did not deliver one. The "Add a skill" dialog on an expert says "Pick one from your library or install one from the marketplace", and its Marketplace tab is typed StoreAgent[] — picking an entry appends the workflow listing's name to Expert.skills as a display chip. No content moves, and nothing reads Expert.skills into a prompt: expert_context.py builds an expert's block from name, role, identity, voice and boundaries only. This PR does not touch that path (it is update_skills, which #14414 is rewriting); it builds the real thing beside it, and the chip path goes when both have landed.

Why a separate listing type. StoreListing.agentGraphId is NOT NULL and unique, and the StoreAgent view exposes the graph as non-null, so a skill cannot ride the agent tables without changing the busiest table and view in the product to gain a discriminator we would then branch on everywhere. SkillListing / SkillListingVersion mirror the store's shape — slug, version, submission status, review fields, the canonical categories from #14428 — and hold the SKILL.md instead. No accompanying SQL view: StoreAgent exists to aggregate run counts and review stats, and a skill has neither.

Install goes through the existing capability. POST /api/store/skills/{slug}/install calls store_user_skill, the same function the store_skill tool and the SKILL.md upload endpoint use, so a marketplace skill is validated, capped at the same 50 and stored exactly like any other. The listing's slug is also the installed skill's name, so the two cannot drift and a re-install picks up a newer approved version in place.

Compatibility is a declared list of integrations the instructions assume, shown as "Works with Google" on the card and the page. When one is not connected it appears after the install has already succeeded, as a connect step — not a warning, not a blocker. People connect an integration when they first need it, and the install never waits on it.

Changes 🏗️

  • SkillListing / SkillListingVersion + migration. owningUserId is nullable so the platform can publish starter listings with no creator profile, the way a roster Expert has no owner until it is hired; slug is globally unique so such a listing needs no creator segment in its URL.
  • GET /api/store/skills, GET /api/store/skills/{slug}, POST /api/store/skills/{slug}/install. Browse is rooted at the version rather than the listing because every filter and the ordering read version columns, and orders on updatedAt DESC.
  • Two platform-authored starter listings, seeded by python -m backend.api.features.store.skill_seed, so the shelf is not empty before creator publishing ships. Their content is a real SKILL.md under starter_skills/, parsed with parse_skill_markdown, so a seeded skill cannot drift from the format an installed one has.
  • Marketplace SkillsSection, /marketplace/skills/{slug}, and the install panel with its connect step. Behind the skills-hub flag; off hides the shelf and issues no request for it.

Scope

This is the first of two. Creator publishing and admin review are the follow-up — with no way to submit a listing yet, there is nothing for a reviewer to review, so both belong together in the second PR. Also deliberately out: installing a marketplace skill onto an expert rather than personal AutoPilot, public expert publishing (the ticket excludes it), skill listings in unified search, and multi-file skill bundles (nothing can create sibling files today).

Verified badges are out, descoped by Product on 2026-09-08. Every listing already goes through manual human review, so a separate verified state buys nothing until the marketplace admits automated listings. There is no isVerified column, no API field and no badge; browse orders on updatedAt DESC alone. #14428 dropped the same thing on the agent side.

Expert-owned install is one keyword away. #14414 gives store_user_skill an optional trailing expert_id; this PR has exactly one call site for it, so the follow-up passes the target through rather than restructuring anything. That is why this is built on dev rather than stacked on #14414.

Verification

I ran the backend suites against a throwaway Postgres of my own with this branch's migrations applied — the shared dev database was never touched, and in fact cannot be: it has no SkillListing tables, because this branch's migration is unmerged and was never applied there. 14 skill-listing tests, the full store suite (165), architecture_test.py (3) and blocks/test/test_block.py (1,647 passed, 84 skipped) are green, as are 13 frontend marketplace test files (53 tests) and tsc --noEmit.

I verified the migration by applying all 197 migrations to that database and diffing the result against schema.prisma. Zero drift on these tables; the 21 remaining lines are pre-existing dev drift (trigram indexes, organizationId indexes, a dropped StoreListingVersion.search column) that CI carries too. The same check caught an index name Postgres truncates at 63 characters.

I then broke guards on purpose and watched the right test go red. Two absence tests were passing because the listing-level filter hid the row before the version's status was consulted, and awaiting mutateAsync in an onClick made every failed install an unhandled rejection. Both are fixed and each now has a test that fails without the fix. For the browse ordering I removed order=[{"updatedAt": "desc"}] and confirmed the test fails on ['older-one', 'newer-one'] == ['newer-one', 'older-one'] — it creates its rows oldest-first, so insertion order alone cannot satisfy it.

What I did not execute: the post-install connect step in a real browser — it needs a signed-in session my local fixture stack cannot issue, so it rests on the integration test and its two mutations. Backend CI is hand-fired because this PR is stacked on #14428 and a stacked base never triggers platform-backend-ci.yml.

Closes SECRT-2593.

Agents and large language models used

Claude Code with Claude Opus 5

Checklist 📋

For code changes:

  • I have clearly listed my changes in the PR description
  • I have made a test plan
  • I have tested my changes according to the test plan

Pwuts and others added 11 commits September 7, 2026 20:18
…d a category filter

Marketplace listings are now filed under one of eight canonical categories,
shoppers can filter the front page by them, and verified listings rank above
unverified ones everywhere the store is browsed.

SECRT-2594.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…g and the required category

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…it matches

The same three-line choice — asked-for category, else the canonical set when
the setting is on, else no filter — was spelled out at all three query sites.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
"Other" was one of the ten legacy options and is not in the canonical eight,
so the publish happy path could no longer find it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Both shipped untested, which put the backend patch coverage at 69% against
an 80% target.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A skill is instructions and examples, not a runnable graph, so it cannot ride
StoreListing: that model's agentGraphId is required and the StoreAgent view
exposes the graph as non-null. SkillListing and SkillListingVersion mirror the
store's shape — slug, version, submission status, review fields, isVerified,
canonical categories — and hold the SKILL.md instead.

owningUserId is nullable so the platform can publish starter listings with no
creator profile, the way a roster Expert has no owner until it is hired, and
slug is globally unique so such a listing needs no creator segment in its URL.

No accompanying SQL view: StoreAgent exists to aggregate run counts and review
stats, and a skill has neither, so its install count sits on the listing row.

Schema and migration only; nothing reads these tables yet.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Adds GET /api/store/skills, GET /api/store/skills/{slug} and
POST /api/store/skills/{slug}/install, plus two platform-authored starter
listings so the catalogue is not empty before creator publishing ships.

A listing's slug is both its marketplace URL segment and the name the skill
takes once installed, so the two cannot drift and a re-install picks up a newer
approved version in place. Install goes through store_user_skill, the same
function the copilot's store_skill tool and the SKILL.md upload endpoint use,
so a marketplace skill is validated, capped and stored exactly like any other.

Browse is rooted at the version rather than the listing so verified-first
ordering reads its own column. The starter skills' content is a real SKILL.md
under starter_skills/, parsed with parse_skill_markdown, so a seeded skill
cannot drift from the format an installed one has.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Mutation testing found both absence tests passing for the wrong reason: they
set the listing's hasApprovedVersion false, so the listing-level filter hid the
row before the version's status was ever consulted. hasApprovedVersion means
some version was approved once, not that the live one still is, so a version
rejected on re-review is exactly the case the status check exists for and
nothing covered it.

The detail and install cases are parametrized over both shapes, and the
ordering test now creates the verified listing first so recency ordering alone
cannot satisfy it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The marketplace now reads Experts to hire, Workflows to install, Skills to
teach. A skill card opens /marketplace/skills/{slug}, which shows the
instructions the AutoPilot will follow and adds it in one click — two from the
front page.

Integrations the skill assumes are shown as "Works with …" beside the button
and, after the install has already succeeded, as a connect step for the ones
not yet connected. Connecting is the normal next step rather than a
precondition, so nothing here is styled or worded as a warning and the install
never waits on it.

Behind the skills-hub flag; off hides the shelf and issues no request for it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…itors

The listing body was a <pre>, so a reader judging a skill before installing it
saw "## Before writing anything" instead of a heading. It renders as markdown
now, with the file's own heading levels demoted under the page's.

A signed-out visitor got a button that called an authenticated endpoint. The
listing is public, so the call to action stays and links to log in rather than
disappearing the way the agent install button does — a listing with no CTA is
the wrong trade for a marketplace shelf aimed at people who have not signed up.

Provider names on the card go through formatProviderName, so a card no longer
says "Works with google" beside a page that says "Google".

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

🗂️ Base branches to auto review (1)
  • dev

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: e0100139-aebe-4409-a2c2-c74f94265c80

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added cla: signed CLA signed by all contributors platform/frontend AutoGPT Platform - Front end platform/backend AutoGPT Platform - Back end labels Sep 7, 2026
@Pwuts

Pwuts commented Sep 7, 2026

Copy link
Copy Markdown
Member Author

🤖 Evidence for this PR: what I executed, what the mutations proved, and what I could not capture.

Screenshots. Both are real browser renders at 1440px, signed out, against a local fixture backend. The signed-out state is why the call to action reads as a link rather than a button — that is the change, not an artifact.

Tests executed. Backend suites ran against a throwaway pgvector/pgvector:pg16 container of my own with this branch's migrations applied; the shared dev database was never written to, which pg_stat_user_tables confirms (16 inserts, 16 deletes on the throwaway; the local Supabase has no SkillListing table at all).

suite result
store/skill_db_test.py 14 passed
backend/api/features/store (whole feature) 170 passed
backend/util/architecture_test.py 3 passed
backend/blocks/test/test_block.py 1,647 passed, 84 skipped
frontend src/app/(platform)/marketplace 12 files passed
tsc --noEmit clean

The migration was verified, not eyeballed. I applied every migration to the throwaway database and diffed the result against schema.prisma. The first pass came back with a rename — Postgres truncates an index name at 63 characters and mine was 64, so ..._isAvaila_idx landed as ..._isAvaila_id. Fixed; the diff now reports zero Skill-related lines with both tables present. I also proved the slug constraint both ways: two owner-less platform listings coexist, and a duplicate slug is rejected by SkillListing_slug_key.

Mutations. Every guard below was broken on purpose and the named test watched to fail.

mutation result
drop verified-first ordering 1 failed ✅
drop submissionStatus from the browse query 1 failed ✅
drop isAvailable from the browse query 1 failed ✅
stop rejecting a non-approved active version 2 failed ✅
connect step always shows once installed 1 failed ✅
connect step never shows 1 failed ✅

Two of those did not fail on the first attempt, and both were real gaps rather than harmless ones. The ordering test created the verified listing second, so recency ordering alone satisfied it — it proved nothing about verified-first until the fixtures were reordered. And both absence tests set the listing's hasApprovedVersion false, so the listing-level filter hid the row before the version's status was ever consulted. hasApprovedVersion means some version was approved once, not that the live one still is, so a version rejected on re-review is exactly the case the status check exists for and nothing covered it. That case is now its own test, and the detail and install cases are parametrized over both shapes.

What I did not execute. The post-install connect step in a real browser. Installing calls an authenticated endpoint and my local fixture stack cannot issue a signed-in session, so that state rests on the integration test and the two mutations above rather than a screenshot. Everything else in the Verified paragraph I ran.

Backend CI is hand-fired. This PR is based on pwuts/secrt-2594 (#14428), and platform-backend-ci.yml only fires on pull_request for base master/dev/release-* — a stacked base leaves no check row at all, so a green checks list would not mean the backend suite ran. The dispatched run and its sha are in a follow-up comment.

🤖 The two screenshots below are superseded. They crop tightly to the new surface and show nothing of where it sits in the Marketplace; the fuller set is in #14431 (comment). Everything above still stands.

The marketplace now reads Experts to hire, Workflows to install, Skills to teach

A skill listing: what the AutoPilot will follow, and the compatibility line beside the CTA

Pwuts and others added 2 commits September 7, 2026 23:42
…ules

Neither module logs anything; the logger and its import were carried over from
the store modules they sit beside.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…nhandled

`mutateAsync` rejects on failure, and awaiting it inside an onClick made every
failed install an unhandled rejection even with `onError` set — reverting this
change fails the vitest run with "Unhandled Rejection", which is what the new
test pins. `mutate` with `onSuccess` does the same work with nothing to catch.

`pendingConnections` also carried an `id` no caller read; it is a list of
display names now.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@codecov

codecov Bot commented Sep 7, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.39466% with 29 lines in your changes missing coverage. Please review.
✅ Project coverage is 81.37%. Comparing base (d97a714) to head (b7f6b27).

Additional details and impacted files
@@                 Coverage Diff                  @@
##           pwuts/secrt-2594   #14431      +/-   ##
====================================================
- Coverage             81.44%   81.37%   -0.07%     
====================================================
  Files                  3550     3538      -12     
  Lines                265163   264074    -1089     
  Branches              24598    24465     -133     
====================================================
- Hits                 215956   214890    -1066     
- Misses                43789    43827      +38     
+ Partials               5418     5357      -61     
Flag Coverage Δ
platform-backend 86.37% <90.76%> (-0.05%) ⬇️
platform-frontend 60.50% <93.18%> (-0.37%) ⬇️
platform-frontend-e2e 28.64% <15.38%> (+0.26%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Components Coverage Δ
Platform Backend 86.37% <90.76%> (-0.05%) ⬇️
Platform Frontend 62.80% <93.18%> (-0.39%) ⬇️
AutoGPT Libs ∅ <ø> (∅)
Classic AutoGPT 28.43% <ø> (ø)
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Patch coverage came in at 63.63% against a 70% target, and the uncovered lines
were the ones worth testing rather than filler: the page module was untested
outright, SkillBody's heading, list, bold and code renderers never ran because
the fixture body had only a paragraph and an ordered list, and the card's
unverified and no-integrations branches had no case.

Adds the "gone from the marketplace" state, the empty shelf, and a
generateMetadata test that pins the tab title to the listing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Pwuts

Pwuts commented Sep 7, 2026

Copy link
Copy Markdown
Member Author

🤖 Backend CI, hand-fired, and the coverage gap that followed the first push.

platform-backend-ci.yml fires on pull_request only for base master/dev/release-*, and this PR is based on pwuts/secrt-2594. A skipped workflow leaves no check row at all, so a green checks list here would not have meant the backend suite ran. Dispatched run on the current head: https://github.com/Significant-Gravitas/AutoGPT/actions/runs/34165509678success, sha 59b2511d864d15ca12d7e8f15c211a62232b31f9.

ci.sh reads the PR GREEN: 34 pass, 3 skipping, nothing failing.

codecov result
codecov/patch/platform-backend 90.65% of diff hit (target 80%)
codecov/patch/platform-frontend 93.18% of diff hit (target 70%)

The frontend patch first came in at 63.63%, and the uncovered lines were worth testing rather than filler, so I wrote tests instead of arguing the target down. The page module had no test at all; SkillBody's heading, list, bold and code renderers never ran because the fixture body held only a paragraph and an ordered list; and the card's unverified and no-integrations branches had no case. All five new frontend files now sit at 100% statements.

Two of those additions are behaviour rather than coverage padding — the "this skill is gone from the marketplace" state, and an empty shelf rendering nothing. The second caught a race in my own test: the section renders its heading over skeletons while loading, so an immediate queryByText assertion passed before the query had settled and would have passed against a broken empty state too. It waits properly now.

Three superseded backend runs were cancelled rather than left to finish, so the earlier run links in this thread are stale — the one above is the current head.

`shows the compatibility line only for a skill that needs one` went straight to
findByRole after render, so its single 1s testing-library wait had to cover the
request and the query together. That holds locally and not on CI, where it
failed on #14432 while passing here.

Reproduced by delaying the handler 900ms: the original fails, and so does a
plain waitFor, because that default is 1s too. The first data-gated await in
each of these tests now carries an explicit timeout, which passes under the
same delay.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Pwuts added a commit that referenced this pull request Sep 7, 2026
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Pwuts and others added 2 commits September 8, 2026 16:04
The label and every chip were siblings in one wrapping flex container, so
the chips wrapped around the label one at a time and a single stray chip
landed on the second line. Grouping the chips into their own flex item
makes the list move down as a unit, and it only breaks within itself once
it has a line of its own.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`isVerified` reached the query layer but never the API, so a shopper had
no way to tell a reviewed listing from any other. `StoreAgent` now carries
it on all three read paths — the Prisma browse, the hybrid search and its
lexical fallback — and each marketplace card states which it is: Verified,
or Community for everything else.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Pwuts and others added 4 commits September 8, 2026 16:12
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…roduct

Product descoped Verified on 2026-09-08: all listings keep manual human
review, so a separate verified state buys nothing until the marketplace
allows automated listings. Out go the `isVerified` column and its
migration, `StoreAgent.verified` and the API field, `verified DESC`
leading the browse ordering, featured requiring verified, and the badge
on the marketplace card. Canonical categories, the classifier backfill
and the category filter are untouched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…mit and an unreachable category list

Three review findings. The classifier read `response.choices[0]` outside its
try, so a completion with no choices raised IndexError out of the backfill's
`asyncio.gather` and took the whole run down — the module's docstring already
promised it never raises. `--limit` took any int, and a negative one sliced
`pending[:-1]`, classifying everything but the last listing. And the publish
and edit forms fed a required category select from an API call whose loading
and error states neither read, so a slow or failed request left the field
empty with nothing said; it is now disabled and says which.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Both tests waited on the combobox, which the loading state disables too, so
neither actually proved the error path — the edit one would have passed with
the request still in flight. They now wait for the copy only the failed state
renders. Making the hook ignore `isError` turns both red.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Pwuts
Pwuts marked this pull request as ready for review September 8, 2026 15:37
@Pwuts
Pwuts requested a review from a team as a code owner September 8, 2026 15:37
@Pwuts
Pwuts requested review from Abhi1992002 and kcze and removed request for a team September 8, 2026 15:37
@Pwuts

Pwuts commented Sep 8, 2026

Copy link
Copy Markdown
Member Author

Note

Superseded — six of the eight shots below show the Verified pill, which Product descoped on 2026-09-08.
Re-captured from the descoped head in #14431 (comment).
The divergences named here are unchanged and still stand.

🤖 Fuller screenshots of the Skills Hub in place, replacing the crops in the earlier evidence comment — those were tight on the new surface and showed nothing of where it sits. Everything here is a real browser render, signed out, at 1440 and at 430. The fixture backend behind them populates the whole marketplace rather than only the skills endpoints, which is why the earlier shots had no context to show.

The Skills Hub is the third shelf on the Marketplace home: experts to hire, then workflows to install, then skills to teach, then creators.

Marketplace home at 1440, whole page

The Skills shelf between the workflow grid and Featured Creators

The path in is the card. The whole card is the link to /marketplace/skills/<slug>; there is no separate button.

The click target: the whole skill card is the link

Where the skill page diverges from an agent listing

Naming these rather than changing them — whether the Skills Hub should match the agent pages is a decision above this PR.

Skill listing and agent listing side by side

  • Page width. The skill page is container max-w-4xl (SkillPage.tsx:46). Marketplace home, agent and creator pages are all mx-auto w-full max-w-[1360px] (MainAgentPage.tsx:81, MainCreatorPage.tsx:56, MainMarketplacePage.tsx:61). It is the only narrow page in the marketplace.
  • Back navigation. A plain text link, "Back to Marketplace" (SkillPage.tsx:53), against the agent page's ghost "Go back" button plus breadcrumbs (MainAgentPage.tsx:92-95).
  • Creator. The skill page prints By {creator} as grey text (SkillPage.tsx:75) and the card omits the creator entirely — MarketplaceSkill carries creator_avatar and nothing renders it. An agent listing leads with the creator's avatar and name.
  • The action, signed out. The skill page shows a filled "Add to AutoPilot" that routes to /login. The agent page gates its primary action on user (AgentInfo.tsx:187) and offers "Download here" instead, so the same intent gets two different affordances.
  • Prerequisites and metadata. "Works with Google" is grey body text on both the card and the page. Agent listings express metadata as chips (Categories) and labelled blocks (Version, Last updated); the skill page has neither.
  • The tail. The agent page continues into "Other AI workflows by …", "Similar AI workflows" and the Become a Creator band (MainAgentPage.tsx:158-170). The skill page ends at the body card — no related skills, no route onward.

Skill listing page, whole

Agent listing page, whole

At 430px the shelf stacks to one column and keeps its position between the workflow grid and Featured Creators.

430px: the Skills shelf in place

430px: the skill listing page

What is fixture data

The agents and creators are local fixtures. The two skills are the real platform starter skills, brand-voice-guide and outreach-playbook, so the shelf fills two of its three columns — at launch there genuinely are two, and that is what makes the section read lighter than the grid above it rather than anything about the card design.

Pwuts and others added 2 commits September 8, 2026 18:16
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…descoped by Product

Product descoped Verified on 2026-09-08: every listing already goes through
manual human review, so a separate verified state buys nothing until the
marketplace admits automated listings. Out go `SkillListingVersion.isVerified`
and its column in the skill-listing migration, the API field, the badge on the
skill card and the skill page, and the seed marking starter skills verified.

Browse now orders on `updatedAt DESC` alone. The test that pinned verified-first
becomes one that pins that ordering, creating its rows oldest-first so insertion
order alone fails it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Pwuts added a commit that referenced this pull request Sep 8, 2026
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Pwuts

Pwuts commented Sep 8, 2026

Copy link
Copy Markdown
Member Author

🤖 Design and interface audit — findings only, nothing actioned

The Vercel web-design-guidelines audit of the ten Skills Hub files, plus a comparison against the agent and expert surfaces. Nothing here has been actioned — per Reinier, items get posted as they come in and worked through once they are all in. The visual side is in the screenshot comment, not repeated here. Findings the Verified descope made moot are dropped.

🟠 Should Fix

  • 🟠 Install success lives only in useState, so a reload offers to re-install an already-installed skill and a second install can be fired. Both siblings derive it from the server (MainAgentPage.tsx:123, ExpertHireActions.tsx:47); MarketplaceSkillDetails has no is_installed, so this needs a backend field, not a frontend fix — InstallSkillPanel/useInstallSkillPanel.ts:24
  • 🟠 The page title renders the literal slug. skill_seed.py:42-47 enforces parsed.name == slug, so the <h2> reads outreach-playbook, not "Outreach playbook". Agents render a free-text agent_nameskill_model.py:35
  • 🟠 The detail-page skeleton mirrors nothing in the final layout — two grey bars against a header with a back link, icon tile, title, description, metadata line and install panel, plus space-y-6 loading vs space-y-8 loaded, so everything moves on load. Both sibling pages ship layout-faithful skeletons — SkillPage.tsx:26
  • 🟠 The install count is the lowest-contrast text on page and card, at under half the AA floor. !text-zinc-400 is #ADADB3 in this repo's palette: 2.12:1 on the #f9f9f9 page, 2.21:1 on the card, where StoreCard.tsx:114 uses text-zinc-500. The card's focus ring fails the 3:1 non-text minimum the same way — SkillPage.tsx:74, SkillCard.tsx:52, SkillCard.tsx:21
  • 🟠 The signed-out CTA drops the user at /login with no way back. The sibling in this same PR does it properly and says why: ExpertHireActions.tsx:33-37 builds /signup?next=…InstallSkillPanel.tsx:65
  • 🟠 "Connect" lands on the generic service picker with the whole catalogue to search, right after naming the provider the user needs. ConnectServiceDialog accepts title and description props that are not passed — InstallSkillPanel.tsx:81
  • 🟠 The error state drops the back link, stranding the user; experts/[expertId]/page.tsx:91 keeps its back link above the ErrorCardSkillPage.tsx:33
  • 🟠 SkillBody is the file most exposed to creator-authored markdown and guards none of it — no break-words/overflow-x-auto, so a long URL overflows the card; remarkGfm enables links and tables but overrides neither, so links render indistinguishable from body text — SkillBody.tsx:10-11

Should the Skills Hub match the agent pages?

One decision, not 24 fixes. Where the surfaces differ it is usually the newer one that is thinner. Grouped as the audit groups them: header (three different back-navigation treatments across three sibling pages, max-w-4xl where every other marketplace page is max-w-[1360px], creator as plain text, updated_at fetched and never rendered); primary action (a separate bordered panel rather than inline, different size, a post-action state neither sibling uses); prerequisites ("Works with Google" exists nowhere else in the marketplace, and the platform names integrations with IntegrationLogo, not a plug icon plus join(", ")); social proof (installs smaller and paler than agents' runs, no toLocaleString, no zero state, no rating); cards (title as a <div> not an <h3>, no mobile carousel, a 40px icon where agent cards carry media); discovery (no browse-all page, no search coverage, no category display, SectionHeader's "view all" unused — the six-item shelf is the entire reachable catalogue).

Two divergences run the other way and are worth keeping: the skill card is a real <Link> where StoreCard is a <div role="button">, and the connect step is deliberately offered after install rather than as a precondition.

🟡 Nice to Have

  • 🟡 Flag-gated section with no readiness guard, so the shelf pops in after LaunchDarkly resolves and pushes the sections below it down; useFlagStatus exists for this and line 64 already guards Experts — MainMarketplacePage.tsx:88
  • 🟡 Three skeletons for a six-item fetch, at h-48 against a ~13rem card — SkillsSection.tsx:24-25
  • 🟡 An error makes the whole shelf vanish silently; ExpertsSection.tsx:14-24 keeps a door open — SkillsSection.tsx:13
  • 🟡 No role="status"/aria-busy on any loading branch and no aria-live on install success, so both are silent to a screen reader — SkillsSection.tsx:23, SkillPage.tsx:24, InstallSkillPanel.tsx:35
  • 🟡 Raw {install_count} with no Intl.NumberFormat, and join(", ")/join(" and ") where Intl.ListFormat belongs — SkillCard.tsx:48,53, InstallSkillPanel.tsx:71,105

🔵 Nits

  • 🔵 Decorative icons carry no aria-hidden; HugeiconsIcon emits a bare <svg>, which is why Button.tsx:53 passes it by hand — SkillCard.tsx:25,47, InstallSkillPanel.tsx:40,103
  • 🔵 "Skills to teach" is sentence case where siblings are Title Case — SkillsSection.tsx:19
  • 🔵 Card title has no line-clamp; StoreCard.tsx:87 uses line-clamp-1 plus a title attribute — SkillCard.tsx:37

transition-all is in the audit but is repo-wide across nine marketplace files including StoreCard.tsx:52 — fix it everywhere or nowhere, not as a Skills-specific complaint. Full list of all 42 rule findings and 24 divergences on request.

@Pwuts

Pwuts commented Sep 8, 2026

Copy link
Copy Markdown
Member Author

🤖 Re-captured from the descoped head (b7f6b27e0a), replacing the earlier set — six of those eight shots showed the Verified pill, which Product descoped on 2026-09-08. Same capture, same fixture backend, same widths; the only difference is the pill is gone and the skill card's icon now sits alone in its row. Everything else in the earlier comment still stands, and the divergences it names are unchanged.

The Skills Hub is the third shelf on the Marketplace home: experts to hire, then workflows to install, then skills to teach, then creators.

Marketplace home at 1440, whole page

The Skills shelf between the workflow grid and Featured Creators

The path in is the card. The whole card is the link to /marketplace/skills/<slug>; there is no separate button.

The click target: the whole skill card is the link

Where the skill page diverges from an agent listing

Naming these rather than changing them — whether the Skills Hub should match the agent pages is a decision above this PR, and the design audit comment groups all 24 of them as that one decision.

Skill listing and agent listing side by side

  • Page width. The skill page is container max-w-4xl (SkillPage.tsx:46). Marketplace home, agent and creator pages are all mx-auto w-full max-w-[1360px]. It is the only narrow page in the marketplace.
  • Back navigation. A plain text link, "Back to Marketplace" (SkillPage.tsx:53), against the agent page's ghost "Go back" button plus breadcrumbs.
  • Creator. The skill page prints By {creator} as grey text and the card omits the creator entirely — MarketplaceSkill carries creator_avatar and nothing renders it. An agent listing leads with the creator's avatar and name.
  • The action, signed out. The skill page shows a filled "Add to AutoPilot" that routes to /login. The agent page gates its primary action on user and offers "Download here" instead.
  • Prerequisites and metadata. "Works with Google" is grey body text on both the card and the page. Agent listings express metadata as chips (Categories) and labelled blocks (Version, Last updated); the skill page has neither.
  • The tail. The agent page continues into "Other AI workflows by …", "Similar AI workflows" and the Become a Creator band. The skill page ends at the body card — no related skills, no route onward.

Skill listing page, whole

Agent listing page, whole

At 430px the shelf stacks to one column and keeps its position between the workflow grid and Featured Creators.

430px: the Skills shelf in place

430px: the skill listing page

What is fixture data

The agents and creators are local fixtures. The two skills are the real platform starter skills, brand-voice-guide and outreach-playbook, so the shelf fills two of its three columns — at launch there genuinely are two.

@Torantulino

Copy link
Copy Markdown
Member

/review

@autogpt-pr-reviewer

autogpt-pr-reviewer Bot commented Sep 8, 2026

Copy link
Copy Markdown

🤖 Reviewing b7f6b27 since 17:07 UTC, usually about 40 minutes. Track it on the dashboard (workspace members).

@Pwuts

Pwuts commented Sep 8, 2026

Copy link
Copy Markdown
Member Author

/review

@autogpt-pr-reviewer

autogpt-pr-reviewer Bot commented Sep 8, 2026

Copy link
Copy Markdown

⚠️ Code review could not be completed

The review service stopped receiving worker progress and could not finish. Re-run the review; if this repeats, the review service needs attention.

If this persists, please contact support with job ID 1ede1bd7-426f-4745-8018-5e357a96e5a5.

Base automatically changed from pwuts/secrt-2594 to dev September 9, 2026 00:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla: signed CLA signed by all contributors platform/backend AutoGPT Platform - Back end platform/frontend AutoGPT Platform - Front end size/xl

Projects

Status: 🆕 Needs initial review
Status: No status

Development

Successfully merging this pull request may close these issues.

2 participants