Repository navigation
feat(skills): Skills page and SkillMarketplace registration - #225
Conversation
07841e6 to
3d6fc3a
Compare
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Core terminology, authorization, and file-preview acceptance criteria remain ambiguous or inconsistent.
Review effort: Balanced
Findings: 4
Open (6)
Define supported resource types, limits, and handling · New Clarify repository approval versus individual skill approval · New Resolve authorization scope and tenant membership policy · New Clarify Marketplace entity and UI terminology · New Fix subject–verb agreement in success metric · New Document or close gaps in requirement IDs · New
What changed in this PR
Adds the Agent Marketplace PRD to define tenant-scoped, read-only skill discovery before related ADRs and implementation.
Changes:
- Defines V1 goals, scope, requirements, and acceptance criteria.
- Adds the PRD to the repository index.
| File | Description |
|---|---|
docs/prd/agent-marketplace.md |
Defines Marketplace product requirements. |
docs/prd/README.md |
Indexes the new draft PRD. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
47dd642 to
3d6fc3a
Compare
Diagrams changed in this PR
The Files changed tab shows a before-and-after image diff for each SVG. |
Visual WalkthroughThis pull request adds a skill marketplace: an operator that renders SkillMarketplace resources, each tagged internal or external, into kora-system objects, and a skills-service that lists and reads skills from GitHub. Browsing skillsA skills request reads the cached list, keeps it when the commit is unchanged, and crawls GitHub when the commit changed or the list is partial. sequenceDiagram
participant C as Client
participant H as Skills handlers
participant S as Skills service
participant K as Redis cache
participant R as Crawler
participant G as GitHub
C->>H: list skills
alt invalid offset or limit
H-->>C: 400 invalid request
else valid page
H->>S: list skills
alt no usable key
S-->>H: 502 credential unavailable
else key usable
S->>K: read cached list
K-->>S: cached list
alt cache fresh
S-->>H: cached skills
else cache stale or missing
S->>R: revalidate or crawl
R->>G: resolve commit
alt resolve outage or rate limit
G-->>R: outage or rate limit
R-->>S: keep stale list when cached
else commit unchanged and list reusable
G-->>R: same commit
R-->>S: keep cached list
else commit changed, partial or other limits
R->>G: read tree and SKILL.md files
alt crawl outage or rate limit
G-->>R: outage or rate limit
R-->>S: keep stale list when cached
else other failure
G-->>R: not found
R-->>S: 404 or 502 fail closed
else read succeeded
G-->>R: tree and SKILL.md files
R-->>S: skill list
S->>K: store list
S-->>H: skills
end
end
end
end
end
H-->>C: skill page
Marketplace aggregationThe operator renders each SkillMarketplace, with its origin, into a ConfigMap and Secret that skills-service reads from mounted files. flowchart TB
subgraph op[kora-operator]
Rec[SkillMarketplace reconciler]
TR[Tenant reconciler]
Agg[Marketplace aggregator]
end
subgraph sys[kora-system]
CM[ConfigMap kora-skill-marketplaces]
Sec[Secret kora-skill-marketplace-secrets]
Reg[Skills registry]
end
SM[SkillMarketplace resource] --> Rec
Rec --> Agg
TR --> Agg
Agg --> CM
Agg --> Sec
Agg --> St[SkillMarketplace status]
CM -->|origin checked on load| Reg
Sec --> Reg
classDef added fill:#1f883d,stroke:#1a7f37,stroke-width:2px,color:#ffffff
classDef changed fill:#bf8700,stroke:#9a6700,stroke-width:2px,color:#ffffff
classDef removed fill:#cf222e,stroke:#a40e26,stroke-width:2px,stroke-dasharray:5,color:#ffffff
classDef context fill:transparent,stroke:#8c959f,stroke-width:1px,stroke-dasharray:3,color:#8c959f
linkStyle default stroke:#8c959f,stroke-width:2px
class SM,Rec,Agg,CM,Sec,Reg,St added
class TR changed
style op fill:transparent,stroke:#8c959f
style sys fill:transparent,stroke:#8c959f
Legend: green = added, amber = changed, red = removed, dashed grey = context
|
There was a problem hiding this comment.
Walkthrough
Adds the Agent Skills marketplace: a SkillMarketplace CRD and operator aggregator, a skills-service backend with GitHub crawling and Redis caching, Helm chart wiring, and a dashboard Skills UI.
| Severity | Finding | Location |
|---|---|---|
| Bound the total size of aggregated keys | backend/internal/operator/skillmarketplace_aggregator.go:191 |
|
| Strip only a closing # sequence from titles | backend/internal/skills/formatter.go:111 |
|
| Check how nested file paths are encoded | backend/internal/platform/clients/github/client.go:395 |
|
| Expose skills-service metrics to the ServiceMonitor | charts/kora/templates/skills-service/config.yaml:15 |
|
| Store SKILL.md content apart from the list metadata | backend/internal/skills/cache.go:56 |
|
| Add tests for the Redis cache | backend/internal/skills/cache.go:1 |
|
| Reject non-positive cache TTLs | backend/cmd/skills-service/config/config.go:84 |
|
| Serve marketplaces under /api/v1 | charts/kora/templates/api/virtualservice.yaml:170 |
Review effort: Very high. Split into operator, skills-service, chart and frontend PRs.
Risk: High, new service, CRD, secrets aggregation and public route
Coverage: 147 of 147 files reviewed
Where to focus
| Area | Why it needs a person |
|---|---|
backend/internal/operator/skillmarketplace_aggregator.go |
Decide how tenant GitHub App keys are bounded in one shared Secret. |
backend/internal/platform/clients/github/client.go |
Verify GitHub accepts encoded slashes in contents paths. |
backend/internal/skills/cache.go |
Decide the cache layout for large SKILL.md content and add tests. |
charts/kora/templates/skills-service/ |
Confirm metrics scraping and route prefix for the new service. |
Lower risk
| Area | Why |
|---|---|
frontend/src/app/(dashboard)/skills/ |
UI components and stories over a server-side data layer. |
docs/ |
Documentation and index entries. |
*_test.go |
Test files with no production logic. |
Changes by area
| Area | Files | Change |
|---|---|---|
backend/internal/operator |
13 | SkillMarketplace CRD, controller and aggregator |
backend/internal/skills, backend/cmd/skills-service |
19 | New skills service |
backend/internal/platform |
6 | GitHub client methods, marketplace file format, chart tests |
charts/, deploy/, scripts/ |
25 | Chart, CRD and local deploy wiring |
docs/ |
14 | PRD, ADR, guides |
frontend/ |
70 | Skills pages, API routes, e2e, preferences |
Checked
Operator controllers and tests, skills handlers and token handling, frontend skills routes and server client.
There was a problem hiding this comment.
Walkthrough
Adds an origin field (internal or external) to SkillMarketplace, the skills-service API and the Skills UI. Also fixes the earlier review points on titles, metrics, TTLs, routes and cache tests.
| Severity | Finding | Location |
|---|---|---|
| Serialize registry reloads | backend/internal/skills/registry.go:241 |
Review effort: High
Risk: Medium, new origin field in CRD and API
Coverage: 52 of 52 files reviewed
Where to focus
| Area | Why it needs a person |
|---|---|
backend/internal/skills/registry.go |
Decide whether overlapping debounced reloads need serialization. |
backend/internal/operator/skillmarketplace_types.go |
Confirm required origin needs no default for existing resources. |
Checked
Skills crawler and service caching, chart templates, Skills UI and tests.
Created the interfaces for the datatypes and created mock data, skills, that will be used for UI work
… Add Previous and Next pagination with URL state, Implement Marketplace overview request and availability states Covers pieces of three Notion stories under Marketplace Frontend Backlog. None fully meet their acceptance criteria, nothing here should close a ticket yet. Filters ticket done: no auto-select, "Select a Marketplace" prompt when none chosen, no Skill request until selected, alphabetical Marketplace order. Left: a Marketplace ID distinct from display name, the "Marketplace not available" error message, and the recorded responsive layout. Pagination ticket done: marketplace/page/pageSize in the URL, Previous/Next boundary disabling, page sizes 12/24/48 default 24. Left: offset validation and reset notice, scroll/keyboard-focus on page change, push vs replace history distinction, pageSize clamping for unsupported values, disabling controls during a pending request. Availability-states ticket: not implemented, beyond the no-selection prompt it shares with the filters ticket. No request-state model, no Current/Stale/Degraded/Pending/Unavailable/Not-configured handling, no Forbidden or session-expiry handling, no "Showing previous results," no manual Retry.
Updated the ADR for the Agent Marketplace Frontend Architecture to reflect changes in the development of it.
- Load the first result page on the server, and later browse changes through /api/marketplace/skills - Add search, sort, rows per page, page buttons, and a list or card view - Add loading and error notices that keep the current results on screen - Save the last selected Marketplace in the user preferences - Keep the browse values in the Skill URL for the header Back button - Add a retry to the Skill page, and move the mock client and fixtures to src/server - Make ViewToggle a component and move it to src/components (Tier 2) - Update the frontend ADR Exclude the tests until the skills-service API is ready, so that they come in one complete change. Complete these Notion stories (Marketplace Frontend Backlog): - Build Marketplace overview result views https://app.notion.com/p/3e6d125495bf8142a1f8c14136dd0816 - Implement Marketplace overview request states https://app.notion.com/p/3e6d125495bf8177977af3621a16a319 Partly complete these Notion stories, which still have open items: - Define and implement the Marketplace selector https://app.notion.com/p/3d1d125495bf8177876ee8ad9b3980c7 - Build the Marketplace overview search control https://app.notion.com/p/3cbd125495bf81ba8244e7ea0f1614ca - Add Previous and Next pagination with URL state https://app.notion.com/p/3cbd125495bf81c68080f43491b5ffd5 - Load a selected Skill by slug https://app.notion.com/p/3cbd125495bf8112b493f2b6afb81bd2 Update the ADR for this Notion story: - Record the Marketplace frontend architecture https://app.notion.com/p/3cbd125495bf81dbbe91c714a97ddab0
- Replace the header Back button, which #340 removed, with a back link on the Skill page - Show the link above the Skill, the failed-load message, and the not-found message, so that it returns to the same overview results (PRD FR-17) - Add the link to the Skill page skeleton - Update the frontend ADR Partly complete this Notion story (Marketplace Frontend Backlog): - Load a selected Skill by slug https://app.notion.com/p/3cbd125495bf8112b493f2b6afb81bd2
Cancel superseded requests and debounced searches on browser history navigation. Retain the loaded Marketplace list when Skills fail, so users can select another source without showing an unverified empty state. Encode opaque Skill slugs, decode their breadcrumb labels, and align fixture search with the name-only contract. Promote ViewToggle to Terra and commit routing, preferences, BFF, and browse regression coverage. References: docs/adr/2026-08-17-agent-marketplace-frontend-architecture.md Validation: make check CI=1 (844 unit, 213 integration, 358 Terra tests).
…earch in code points The upstream client sends one keys parameter per favourite, so a path holding a comma stays one key (C8); the e2e stand-in reads them with getAll. The three-character search minimum counts code points, as skills-service counts runes (C9).
The three-character hint and toSearch share one searchLength helper, so typing two emoji shows the hint instead of doing nothing.
… card descriptions Hades gains the approved link icon (D74). Each skill card's bottom bar has an icon-only Copy link button, sized like the heart, with a tooltip and a brief copied state; it copies the skill page's absolute address and does not open the card. The skill page header has a Copy link button beside View source. The card link reads View details. Card descriptions clamp to three lines with the list view's clamp, now one shared clampLines (D75); the skill page keeps the full text. The story fixtures carry a long claude-api description.
A SKILL.md that is not text, or longer than its tree size, reads the same on every crawl, so it no longer marks the list partial and forces a re-crawl at each commit check. The cache error test no longer waits on client retries.
A skill left out by a crawl limit no longer marks the list partial: other limits already force a crawl, so a limit-skipped list at an unchanged commit is kept after one commit read. Ignore the skills-service build output like the other cmd binaries.
… keys text to the code
SkillMarketplace gains a required spec.origin, internal or external, stating whether the content is the organization's own or third-party (D76).
…etplaces The aggregator writes spec.origin into each Marketplace entry, and internal/platform/marketplace carries it to skills-service.
skills-service reads origin from kora-skill-marketplaces and returns it on GET /v1/marketplaces items and the skill detail. An entry without a known origin is skipped and logged, matching the required CRD field (D76).
The local fixture reads anthropics/skills, so it is external. The guide's example-org Marketplace is internal, matching the ADR, and the guide lists the field.
Marketplace and Skill carry origin: internal or external (D76). An Internal (secondary) or External (outline) badge shows in each Marketplace chip after the name, in each card footer before the repository, from the Marketplace list the chips already load, and in an Origin row of the skill page's Source card. FilterChip takes an optional badge. The skill count beside the chips no longer wraps. Story and e2e fixtures carry one internal and several external Marketplaces.
Hades swaps the link icon this PR added for share-2 (D78); nothing else used link. The card button and the skill page button read Share, with Link copied as the copied state, and still copy the skill page's address without opening the card. The card button is now ShareButton.
…merges Signed-off-by: Justin Wilkin <justin.wilkin@clearroute.io>
df4a76a to
c215a15
Compare


What and why
Implements Agent Skills (PRD): a read-only Skills page where tenant members find and read agent skills from the GitHub repositories their tenant registers.
SkillMarketplace(runkora.io/v1alpha1) in its namespace, with the repository, an optional ref, a requiredorigin(internalorexternal) and the GitHub App that reads it. The operator renders every tenant's Marketplaces into thekora-skill-marketplacesConfigMap and their App keys into thekora-skill-marketplace-secretsSecret inkora-system, and reportsAggregated,PendingorInvalidwith a reason such asCredentialUnavailablein status./v1/marketplacesbehindskills:read. Each Marketplace is read with an installation token scoped to its one repository andcontents:read, and a public repository outside the installation falls back to ametadata:readtoken. The ref resolves to a commit,SKILL.mdfiles are read on crawl and supporting files when opened, within limits set in the chart.fileCacheBytesPerMarketplace(32 MiB) in stored bytes, and files over the cap are served uncached./skillslists skills across Marketplaces in title order, ten per page, as cards or a table, with Marketplace and Favourites chips, search, and an origin badge on each card. Favourites are stored in the preferences document. The skill page rendersSKILL.mdwith a raw view, a file explorer, the source repository, origin and commit, a View source link at that commit, and Share./v1/marketplacesroute on the api host, and a ServiceEntry forapi.github.comunder the platform egress policy.Cache layout, operator resync and the shared GitHub App code are tracked in #920.
How it was tested
make checkpasses, including a Redis cache test against a RESP fake and chart tests for the route, metrics and ServiceEntry.anthropics/skills, read through the metadata-only fallback:SkillMarketplacewithoutoriginis rejected at admission./v1/marketplacesXdoes not reach skills-service.contents:readtoken: the list, a skill page with 313 files, and a nested file.Checklist
make checkpasses.