Skip to content

[AGE-3997] feat(platform): skill registry — publish, browse, install, sync - #6604

Open
ardaerzin wants to merge 48 commits into
release/v0.115.4from
feat/skill-registry
Open

[AGE-3997] feat(platform): skill registry — publish, browse, install, sync#6604
ardaerzin wants to merge 48 commits into
release/v0.115.4from
feat/skill-registry

Conversation

@ardaerzin

Copy link
Copy Markdown
Contributor

Context

Skills existed only as inline SKILL.md packages buried inside one agent's config. There was no way to publish a skill once and reuse it, no versioning, and no way to bring in the skill folders and marketplaces that already exist on GitHub. This PR ships the skill registry designed in #5512: a skill is now a first-class workflow that agents reference, with pin-or-follow versioning, repo imports with sync, and an agent-facing search tool.

Design docs and reviewed mockups live in docs/design/agent-workflows/projects/skill-registry/.

What this adds

A registry page (desktop route + /m), with sections for project skills, one section per imported repo (with a synced tag and a Refresh action), and the Agenta built-ins. One + New skill menu everywhere a skill can be created: write from scratch, upload a folder/.zip/.skill, or import from a GitHub repo.

One skill drawer, three modes. A card opens the detail view: read-only editor, a versions rail for navigating revisions, used-by chips, and source provenance in the header (repo · sha · synced Xm ago, or a "modified locally" badge when a local edit detached a skill from sync). Edit skill turns the same drawer editable; saving opens a blast-radius dialog (v1 -> v2, which agents follow latest vs stay pinned) instead of a silent commit. Add to agent is a second step in the same drawer: pick agents, follow latest or pin, batch install. Archive/unarchive lives here too, with a warning listing the agents that still reference the skill.

Install flows on both sides. The agent config's Skills + opens a registry picker (split Add | pin per row, Add all, and the same + New skill menu; a skill created there lands in the registry and on the agent). Clicking an existing skill row opens the detail drawer instead of the old raw JSON editor. Rows read Latest or Pinned vN, with a gold vN available nudge when a pin falls behind the head.

Versioning by reference level. An @ag.embed entry with a workflow ref follows the head; a workflow_revision ref with a version pins. The entry must carry "@ag.selector": {"path": "parameters.skill"}; without it the resolver inlines the whole revision data and the run fails (found and fixed during QA).

Imports are snapshots. POST /skills/sources/scan previews a repo (Claude marketplace manifest, root SKILL.md, or a multi-skill tree) with per-candidate validity; POST /skills/sources creates each selected skill as a v1 workflow and records provenance in two new tables (migration oss000000027). POST /skills/sources/{id}/refresh re-scans and commits new versions of unedited skills; a hand-edited skill detaches instead of being overwritten. Nothing ever executes from a repo.

Agent-driven discovery (M3). A new search_skills platform op (endpoint mode over /skills/query, registered in the playground build kit) lets a builder agent search the registry and install a skill onto itself through the existing self-config commit and approval flow.

Data model. No flag migration: is_skill stays a revision-level flag, and the registry lists via a new head-revision query (DISTINCT ON (variant_id) ... ORDER BY id DESC with flag containment in SQL before windowing). That query also now excludes revisions of archived artifacts; before this, an archived workflow's head kept listing everywhere the query is used.

Also in this PR

  • Fern client regenerated from the current spec; the new skills resource is consumed through a getSkillsClient() accessor, with zod kept at the boundary.
  • Runner: skills that fail to materialize are stamped on the agent span as ag.meta.skills.dropped ("name: reason"), instead of only a stderr line.
  • /m agents and skills toolbars aligned with the desktop layout ([create] [search] ... [archived link]), including a new /m/agents/archived page with unarchive.
  • EmptyState gains a real title prop; title= used to silently render as an HTML tooltip attribute at several call sites.
  • Structural seams: two new packages (@agenta/skills headless, @agenta/skills-ui presentational), a skills bridge on the drill-in context wired in both hosts, and small entity-ui extensions (railBottomSlot on SkillFormView, exported CatalogListRow).

Tests

  • API: 3136 unit tests green, including new suites for the head-revision query, the SKILL.md parser (SDK-contract mirror), import (stub fetcher, collision and invalid paths), and sync refresh (changed / hand-edited / missing / detached).
  • Web: package unit tests for the embed writer round-trip and schemas; entity-ui suite at 624 including new scan and summary tests; tsc and lint clean across oss, ee, mobile, and all touched packages; Storybook lint and static build green.
  • Runner: skills and spool-protocol suites green with new dropped-visibility coverage. Some unrelated runner suites fail on the unmodified base too; verified not from this branch.
  • Full manual QA on /m against the mockups, including live sessions: agents invoked an uploaded skill, a repo-imported skill, and a URL-imported skill end to end.
  • Desktop routes typecheck but have no browser QA: the dev stack serves /m only.

What to QA

  • Skills page: create a skill from scratch, upload a folder with several SKILL.mds (the recovery list lets you pick), and import github.com/anthropics/skills. Each lands in the right section.
  • Open an imported skill: the header shows repo, commit, and synced age. Hit Refresh on its section header: "up to date" or an update summary.
  • Edit a skill used by an agent: the save dialog lists that agent and the version bump; committing bumps the card and versions rail.
  • Agent config: + on Skills opens the picker; Add follows latest, the caret pins. The added row shows a green Latest or a Pinned vN tag; clicking it opens the detail drawer, not a JSON editor.
  • Pin a skill, then commit a new version of it: the config row gains a gold "vN available" tag.
  • Run a session and ask the agent to use an installed skill; it should read the SKILL.md and act on it.
  • Archive a skill from its drawer (warning lists referencing agents), find it again via "Archived skills", unarchive it.
  • Regression: an agent with an inline (non-registry) skill still edits it through the existing form, and a config the panel cannot parse still round-trips as JSON.

Closes #5512

Full design record for #5512 (AGE-3997): codebase discovery, the UX plan
matching the interactive mockups, and the master + per-surface technical
plans (api/runner/web) revised after three independent code-verified
reviews. Data model lands on Option C (head-revision query, no flag-model
change); auto-discovery resolves to agent-driven install via a registry
search tool + existing self-config ops.
Adds GitDAO.query_head_revisions — one revision per variant, revision-flag
and artifact-search filters applied in SQL BEFORE windowing, so skill
listings paginate correctly (head selection rides the UUID7 id, no
version-string casts). WorkflowsService gains the thin
query_workflow_head_revisions wrapper reusing the existing flag
normalization and batch workflow fetch.

New skills domain (core/skills + apis/fastapi/skills) exposes
POST /skills/query (mounted at /skills and /preview/skills): the paginated
DB block plus the code-defined Agenta built-ins as a separate unpaginated
block, per the registry plan. VIEW_WORKFLOWS-gated; cursor id = the head
revision id.

Option C from the data-model decision: no flag-ownership changes, no
backfill — is_skill on revisions is already SQL-filterable and backfilled.
The head-revision DAO method is shared with the upcoming usage query (A3)
and the agent discovery tool.

Unit tests: registry mapping, builtin-block filtering, payload tolerance.
Full unit suite green (3116 passed).
POST /skills/usage {workflow_id | workflow_slug} walks the head revision of
every agent in the project (the head-revision query keeps this bounded by
agent count, not total revisions) and classifies each matching embed by its
reference level: artifact-level = follows latest, revision-level = pinned
with its version. Feeds the USED-BY chips and the save dialog's
blast-radius panel. Revision-level refs carrying only an opaque revision id
are documented as unmatched in v1.
Pure-function parser over an extracted tree: layout detection
(Claude marketplace manifest → root SKILL.md → nested glob, matching the
upload UX's clean/single/multi-recovery states), YAML frontmatter, and a
validation contract mirroring the SDK's SkillTemplate exactly — kebab name,
description/body/file-size caps, and all four path rules (absolute,
backslash, traversal, reserved root SKILL.md). Trust gates: binary and
oversized files are skipped with warnings, executables are imported
disarmed. A round-trip test proves parser-accepted output constructs
SkillTemplate unmodified. pyyaml promoted to a direct api dependency.
Import skills from public GitHub repos and Claude plugin marketplaces as
ordinary registry skills, snapshot-only (nothing ever executes from the
source):

- POST /skills/sources/scan previews a repo as skill candidates (layout
  detection: marketplace.json -> root SKILL.md -> glob) with per-candidate
  validity and issues; VIEW_WORKFLOWS.
- POST /skills/sources imports the selected candidates: each valid skill
  becomes a workflow (is_skill + is_snippet, agenta:builtin:skill:v0,
  snake_case skill payload) via the one-call simple create; EDIT_WORKFLOWS.
- Fetch is the GitHub REST tarball endpoint (no git binary, no clone),
  size-capped and safely extracted; tests inject a local fetcher.
- Provenance lands in new skill_sources / skill_source_links tables
  (migration oss000000027) so sync can offer new versions later;
  content_hash enables change detection, detached opts a skill out.
- Name collisions and invalid candidates are skipped and reported, not
  fatal; domain errors map to the agent-actionable envelope
  {code, message, retryable, next_step, details}.
- Config: AGENTA_SKILLS_IMPORT_TIMEOUT_SECONDS / _MAX_TARBALL_MB via env.
Manual refresh for imported skill sources — no cron in v1, the FE calls
it lazily from the registry page:

- POST /skills/sources/{id}/refresh re-fetches the source, re-scans it,
  and commits a new revision for every linked skill whose upstream
  content changed (message 'sync: <repo>@<sha>', meta.skill_sync as the
  origin marker for log readers).
- Locally edited skills are DETACHED, never overwritten: the link's
  content_hash records what sync last wrote, so a head that no longer
  matches it means a hand-edit happened in between. The commit goes
  through commit_workflow_revision_checked with base_revision_id, so a
  moved head races to 409 -> reported as 'conflict', not clobbered.
- Paths deleted upstream are marked missing_in_source (and cleared if
  they come back); the workflow is never deleted. Detached links are
  skipped entirely.
- GET /skills/sources lists a project's sources for the import UI.
- One canonical skill_content_hash (None-stripped sorted JSON) shared by
  import and refresh so stored payloads and fresh parses compare equal.
- DAO gains update_source / update_link; refresh scenarios covered by
  stub-fetcher unit tests (changed, hand-edited, missing, detached).
Two new workspace packages for the skill registry, mirroring the
sessions pair:

- @agenta/skills is HEADLESS (schema, API calls, atoms, embed writer):
  deps on entities + shared only, with lint bans on antd and every UI
  package — including @agenta/skills-ui — so mobile can consume it.
- @agenta/skills-ui is presentational and wraps existing entity-ui
  components (SkillFormView etc.), so antd is allowed; its eslint config
  spreads restrictedImportPaths so the shared singleton/barrel bans
  survive. The reverse edge is the contract: entity-ui never imports
  skills-ui.
- Registered in the allowlists that exist: oss next.config
  transpilePackages + package.json, ee package.json (ee inherits oss's
  transpilePackages), storybook next.config + package.json. Mobile
  registration lands with the /m wiring (W3).
- Dep versions match the app (antd ^6.1.3, next 15.5.21); both packages
  pass pnpm check (tsc + eslint).
The headless half of the skill registry:

- core/schema: zod mirror of the SDK SkillTemplate in its STORAGE shape
  (snake_case model_dump at data.parameters.skill — never the camelCase
  wire shape), plus the /skills/query and /skills/usage response shapes.
- api: querySkills / querySkillUsage against the dedicated /skills/*
  endpoints. Raw axios in the entities style because the generated Fern
  client cannot send a flags body to workflows/query and has no /skills
  resources yet; signatures are the contract a Fern swap must keep.
- state: list atoms mirroring the evaluator atoms' SHAPE (atomWithQuery,
  30s staleTime, derived skills/builtin views, server-side search atom)
  but NOT their invalidation — invalidateSkillsListCache resolves
  getHostQueryClient() per call so /m's host client is never orphaned.
- embed: buildSkillEmbedEntry / parseSkillEmbedEntry. Reference level
  encodes pin/follow (workflow ref = latest, workflow_revision ref =
  pinned) and a SIBLING name always rides next to @ag.embed because
  describeSkill renders the raw slug without it. List mutations stay in
  entity-ui's index-based itemListOps, driven by the host — this module
  never touches the list.
- entities: is_skill added to WorkflowQueryFlags (matched against head
  revisions server-side).

10 unit tests (embed round-trips, schema shapes); pnpm check green on
skills and entities.
Presentational skill-registry components (options in, callbacks out;
data arrives from @agenta/skills via the host):

- NewSkillMenuButton: THE single '+ New skill ▾' action (write / upload
  / import), identical everywhere a skill can be created.
- SkillCard + SkillsGalleryPage: the registry browse page on
  FilterRailLayout (the templates-gallery frame, not a TemplateGallery
  copy) — source rail with counts, search, sectioned card grids; origin
  tints the sk avatar (olive project / gray imported / ink builtin).
- VersionsRailCard: revision navigation for the drawer's detail mode.
- SkillPickerDrawer: AddSubagentDrawer anatomy plus what it lacks — a
  split [Add | ▾] per row (plain Add follows latest; the caret is where
  pinning lives, per the progressive-disclosure convention) and the
  footer '+ New skill ▾'. Add all acts on visible rows only.
- SkillSaveBlastRadius: the save dialog's panel (version bump, per-agent
  effect, running-sessions note) that replaces silent auto-commit.

entity-ui seams (extend, don't duplicate): SkillFormView gains
railBottomSlot so detail mode swaps the upload zone for the versions
card; CatalogListRow exported from the drill-in barrel.

Stories in storybook/stories/skills-ui cover populated/empty gallery,
mixed picker states (added / pinned / builtin), blast radius with
followers+pins and with no users; storybook lint + static build green.
- /w/[ws]/p/[project]/skills renders SkillsPage: @agenta/skills atoms
  (project + builtin blocks, server-side search) feeding the
  presentational SkillsGalleryPage. Card/drawer navigation and the
  create flows land with the follow-up checkpoints (W3.3 drawer rework,
  W5 upload/import) — the page lists and searches today.
- EE route stub mirrors the sessions pattern (import-then-export;
  EE does NOT inherit OSS routes, and app-layer pages may not re-export
  @agenta/* directly).
- Sidebar: SKILLS_SIDEBAR_KEY in @agenta/navigation constants; Skills
  sits between Agents and Sessions with the puzzle-piece icon. Selected
  state rides the standard route matching.

tsc clean on oss, ee, and navigation.
Mobile is a separate host with its own providers; without this wiring
/m would silently miss the registry (and later, the drawer seams):

- @agenta/skills + @agenta/skills-ui registered in mobile's
  transpilePackages and package.json (the last of the six allowlists).
- SkillGallerySections extracted from SkillsGalleryPage: the sectioned
  card grid + empty state is now ONE shared body, rendered by the
  desktop page inside FilterRailLayout and by /m inside its own
  scaffold — same cards on both hosts, no copy.
- /m route /w/../skills + SkillListScreen following the AgentListScreen
  browse shape (toolbar or rail per BROWSE_RAIL_MODE, NavDrawer + title
  + search); fed by the same @agenta/skills atoms as desktop.
- Skills nav row between Agents and Sessions (SKILLS_SIDEBAR_KEY,
  lucide Puzzle), matching the desktop sidebar placement.

Card taps stay no-op until the shared drawer seams land (W3.3/W5) —
same staging as desktop. mobile tsc + lint clean; skills-ui check
green after the refactor.
…n 1c/1d/1e

Two strands, one dependency chain (the upload rework builds on the
sources API surface):

Create + import wiring (from live QA on the running stack):
- @agenta/skills gains the /skills/sources surface: scanSkillSource /
  importSkillSource + response schemas, and createSkillWorkflow (skill
  flags on artifact AND v1 revision — the registry query filters on the
  revision flag, so a commit without it is invisible).
- SkillImportDrawer (URL -> scan -> candidate checkboxes -> keep-in-sync
  -> import) + story; SkillCreateDrawer commits a registry skill and
  invalidates the list; both wired on the desktop page and /m.
- API-side fixes surfaced by QA (parser/import service/DAO + tests);
  ruff + 23 skills unit tests green.

Upload flow aligned with the agreed design (1c/1d/1e):
- 'Upload' now opens as a FULL-DRAWER dropzone ('nothing is created
  until you review'); 'Write from scratch' opens the editor as before.
- One valid skill morphs the drawer into the prefilled editor with an
  'N files parsed' tag; errors stay IN the upload view — red panel,
  selectable nested-skills recovery ('Import N skills'), gold warnings
  for skipped files, dropzone below as the retry target.
- entity-ui skillUpload gains scanSkillFiles: every SKILL.md becomes a
  candidate scoped to its own subtree; binary/oversized files skipped
  with a reason, never mojibaked. 4 new unit tests; 623 green.
- Drawer sizes to its stage: compact (520) for upload/recovery, wide
  (960) for the editor, animated width transition. State resets on the
  OPEN transition so closing no longer restages mid-exit-animation.
…(W3.3a)

Clicking a registry card now opens the detail drawer (artboards 2/2b),
one shell with the editor anatomy throughout:

- Read-only SkillFormView with the VERSIONS rail card in place of the
  drop zone; clicking a version row navigates that revision's content.
  USED BY chips (agent + its pin) sit above the form.
- Viewing an older revision: 'viewing vN — read-only' tag, footer
  becomes 'Restore as vN+1' — a normal new commit.
- 'Edit skill' turns the same drawer editable; Save opens the
  blast-radius dialog (5b: vN -> vN+1, per-agent effect, running
  sessions note, commit message) and commits — the explicit replacement
  for silent auto-commit. The dialog is a Radix Dialog, not the
  antd-backed EnhancedModal, because /m renders this drawer and antd is
  banned there.
- @agenta/skills gains fetchSkillRevisions (entities revision query,
  v0 bootstrap filtered, content mapped from data.parameters.skill) and
  commitSkillRevision (skill flags stamped explicitly on the revision —
  the registry query filters on them).
- Wired on both hosts: desktop SkillsPage and /m SkillListScreen; the
  clicked item survives the exit animation (only the open flag flips).
- Builtin cards open read-only info (maintained by Agenta, no
  versions/edit).

tsc + lint clean on skills, skills-ui, oss, mobile.
The config panel's Skills '+' now opens the registry picker (artboard
4b) instead of the inline-skill editor, on BOTH hosts:

- New drill-in skills bridge in @agenta/ui (component injection, so
  the package stays dependency-free — parallels GatewayToolsBridge);
  implementation in @agenta/skills-ui (useSkillsBridge/SkillPickerHost);
  wired in OSSdrillInUIProvider AND /m's DrillInBridgeProvider. Hosts
  without the bridge keep the inline-editor fallback.
- SkillPickerHost drives SkillPickerDrawer over the live registry
  (project + builtin blocks; embed identity is the WORKFLOW slug,
  display is the skill name). Add emits a fully-built @ag.embed entry —
  sibling name/description, workflow ref for follow-latest, revision
  ref for pin — and the PANEL owns the list write (append/filter), so
  entries it cannot parse survive untouched.
- Rows already on the agent show Added / Pinned vN and flip to Remove;
  remove filters by referenced slug and never touches inline packages.
- The picker's '+ New skill' menu opens the same create/upload/import
  drawers; a skill created or imported from here lands in the registry
  AND on this agent (onCreated/onImported callbacks added to both
  drawers).
- AgentTemplateControl parses the agent's existing embeds
  (staticEmbedSlug + now-exported embedRevisionVersion) to feed the
  picker's Added state.

tsc + lint green on ui, entity-ui, skills-ui, oss, mobile; 623
entity-ui tests pass.
Added rows were only distinguishable by a small check suffix in one
flat list. The picker now renders two groups — 'On this agent' (rows
with Remove) above 'Available' (rows with the split Add | pin caret,
and Add all on the group header) — so membership reads at a glance.
Rows also display the skill NAME rather than the storage slug, which
un-mangles the __ag__ builtin rows.
The grouped picker reshuffled rows on every add/remove. Back to ONE
stable flat list; membership now reads through a success-tinted row
background (CatalogListRow gains a className seam) alongside the green
Added/Pinned suffix and the Remove action — a clear cue with zero
layout shift. Rows keep displaying the skill name over the storage
slug.
…ed without it

Found in live QA: every skill added from the registry picker broke the
agent run with 'Invalid skill configuration: name/description/body
missing'. The embed writer emitted only @ag.references, so the resolver
inlined the whole revision.data (uri + parameters) instead of the
SkillTemplate. The documented skill-embed shape (sdk agenta_builtins)
carries '@ag.selector': {path: 'parameters.skill'} — the writer now
emits it on both the latest and pinned forms.

Verified end-to-end on the live stack: registry skill added via the
picker -> run succeeds -> agent invoked the skill and returned its
fixture content.
…rview fix

Closes three QA gaps in one pass:

Registry grouping + counts (API + web):
- /skills/query now returns used_by_count per skill (one pass over the
  agent HEAD revisions, counted once per agent per skill) and source
  attribution: skills[].source_id plus a sources block (repo_url, sync
  state, updated_at) joined from skill_source_links.
- ONE shared buildRegistrySections (skills-ui) maps the response for
  BOTH hosts: This project / one section per imported repo with a
  'synced Xd ago' tag / Agenta; orphaned links fall back to This
  project rather than disappearing. Desktop's source rail now actually
  filters; cards show the used-by count in their meta line.

Config skill rows say the choice, not the plumbing:
- describeSkill drops the raw '@ag.embed' tag for a green 'Latest' or a
  neutral 'Pinned vN' (per the embed's reference level), with matching
  subtitles; the redundant 'skill' tag on inline rows is gone (ux-plan
  issue 3). ItemDescriptor tags accept a toned object form; the slash
  palette's tail reads the label.

Agent overview Configuration card:
- 'N available' -> 'N skills'; the row now expands to the skill NAMES
  (summary gains skillNames, embed refs named by sibling name/slug);
  rows with no expansion and no onEdit render inert instead of an
  empty accordion (the /m overview passes no editor).

3136 API unit tests, 624 entity-ui tests, tsc clean on oss + mobile.
'Add to agent' now works from the registry side — the second step of
the SAME drawer, behind a back chevron, at the compact width (the
resize animates):

- 'Add to' lists every agent that does not yet have the skill (checkbox
  rows, 'will follow latest'); 'Already added' lists the rest read-only
  with their pin, plus a gold 'vN available' nudge when a pin is behind
  the head.
- The roster is the CANONICAL agent list
  (agentWorkflowsListQueryStateAtom): is_agent is a revision flag, so a
  plain workflows/query cannot filter by it — the first cut listed
  every workflow in the project, evaluators included.
- Footer: split 'Add to N agents' with the version caret (follow latest
  / pin to vN). Confirm returns to the registry (the drawer closes) and
  the list invalidates so used-by counts refresh.
- addSkillToAgents (@agenta/skills): per agent, read the head revision,
  append the embed entry to parameters.agent.skills, commit with
  base_revision_id so a concurrent edit conflicts instead of being
  clobbered. Partial failures are reported, successes kept.
- registrySections: the 'synced' tag falls back to created_at (a source
  has no updated_at until its first refresh).

Live-verified on /m: installed haiku-writer onto an agent from the
registry, card count went 0 -> 1 agent, usage API confirms
latest-mode, and the agent still runs (skill invoked in-session).
Imported skills now say where they came from, everywhere it matters:

- API: registry items carry source_detached alongside source_id —
  detached links KEEP their provenance (the drawer can say 'modified
  locally') while only the grouping treats them as project-owned again.
- Detail drawer: a SOURCE row under the header — repo (linked,
  owner/name), short commit sha, 'synced Xm ago' (+ 'sync off' when the
  source has sync disabled), or a gold 'modified locally — no longer
  synced' badge for detached skills.
- Picker rows: imported skills carry a small repo tag (owner/name) so
  provenance reads at a glance when adding to an agent.
- toSourceInfo (registrySections) is the one mapping from a registry
  source to that display shape; the section builder and the picker host
  share it, and detached imports fall back to the This project group
  with provenance intact.

Live-verified on /m: brainstorming's drawer shows
'obra/superpowers · b36e082 · synced 11m ago · sync off'; picker rows
show anthropics/skills and obra/superpowers tags.
The SOURCE line moved into the drawer title as a subtitle under the
name (repo link · commit chip · synced/sync-off, or the gold
modified-locally badge) so the body keeps its full height. Live-checked
on /m.
Found uploading the real gstack folder (root SKILL.md + nested careful/
canary): the recovery panel claimed 'No single skill at the root' while
listing the root skill itself. Root-plus-nested uploads now say what
they are: 'This folder contains N skills (the root plus nested ones).'
Found in QA (importing gstack by URL after archiving the uploaded
copy): workflow slugs are unique per project INCLUDING archived rows
(workflow_artifacts_project_id_slug_key is not partial on deleted_at),
so the reject is correct — but the message claimed the skill 'already
exists' while nothing was visible in the registry. An archived holder
now gets its own message: unarchive it instead of importing.
QA found archiving had no UI at all — and worse, an archived skill kept
LISTING: the head-revision query filtered only revision-level
deleted_at while archiving stamps the ARTIFACT. Both fixed:

- DAO: query_head_revisions excludes revisions of archived artifacts
  unless include_archived; registry items gain an archived flag.
- Detail drawer: an Archive action (confirm dialog warns which agents
  still reference the skill and that the name stays reserved); archived
  skills open read-only with an Archived tag and a single Unarchive
  action.
- Registry: a 'Show archived' toggle (desktop rail + /m toolbar) folds
  include_archived into the list query; archived cards render dimmed
  with an Archived tag.
- @agenta/skills: archiveSkill/unarchiveSkill wrappers over the
  entities API, skillsShowArchivedAtom.

Live-verified the full loop on /m: archive gstack (dialog warned '1
agent still references'), card vanished, Show archived revealed the
dimmed card, Unarchive restored it.
Clicking a project-owned embed-ref skill row in the agent config now
opens the SAME detail drawer as the registry page (versions rail, edit
+ blast-radius save, provenance, archive) instead of the raw JSON
'Skill reference' editor:

- SkillsBridge gains DetailHost (skill addressed by embed slug);
  SkillDetailHost (skills-ui) resolves the slug against the live
  registry list and renders SkillDetailDrawer.
- AgentTemplateControl intercepts openEdit for the skills list: embed
  refs that are not static and resolve to a slug route to the detail
  drawer; static __ag__ embeds, inline packages, and shapes the bridge
  can't resolve keep today's editor (the JSON round-trip guarantee is
  untouched).

Live-verified on /m: clicking the haiku-writer row opens the full
detail drawer.
Each imported-repo section header gains a Refresh action wired to
POST /skills/sources/{id}/refresh: busy spinner, then a one-line result
summary (N updated · modified locally · conflicted · gone upstream, or
'up to date'), and a list invalidation when anything updated so new
versions and the synced tag appear. Connected component
(SourceRefreshButton) so the call and its state live once; sections
carry the sourceId from the shared builder.

Live-verified on /m: refreshed anthropics/skills -> 'up to date'.
…y (M3)

The 'auto-discovery' milestone lands as designed: no resolver mount, no
new machinery — the agent that performs an explicit install IS the
discovery.

- New endpoint-mode platform op search_skills (POST /api/skills/query,
  read-only): searches the registry by name/description and answers
  with slug, name, description, head version and file count, plus the
  builtin block. Its description teaches the loop's second half: append
  an @ag.embed entry to skills via commit_revision, latest by workflow
  slug or pinned by workflow_revision version (the shape the config
  reference already documents).
- Registered in the playground build kit (DEFAULT_BUILD_KIT_OPS), so
  builder agents can search the registry and install skills onto
  themselves through the existing self-config + approval flow.

Catalog + build-kit expectation tests updated; API 3136 and SDK
platform suites green. (test_streaming has one pre-existing failure
unrelated to this change.)
Dropped skills now reach the platform, not just stderr:

- resolveSkillDirs returns dropped: string[] ('name: reason' per skill
  that did NOT materialize — unsafe name, wire-cap, duplicate, write
  error); threaded through the plan workspace and stamped as
  ag.meta.skills.dropped beside skills.loaded/count at BOTH agent-span
  emission sites (the runner's sandbox-agent otel and the Pi extension
  via the per-turn trace control, whose parser gains the bounded
  skillsDropped field). A first-class warnings[] on AgentRunResult
  stays out of scope (wire-contract change).
- Tests: dropped-list coverage in skills.test.ts, spool-protocol
  round-trip updated, every resolveSkillDirs stub carries the field.
  Runner tsc clean; skills + spool suites green (other runner suites
  carry pre-existing failures unrelated to this change, verified
  against the unmodified tree).

Stale docs swept per plan-runner R1.2: harnesses.py no longer claims
Codex skills are a later milestone (the runner writes .codex/skills),
and skills-config/architecture.md catches up with reality
(SkillTemplate, services/runner/, __ag__/is_static/
StaticWorkflowCatalog, no harness drops skills).
- SkillUploadPanel story: the full-drawer empty dropzone (1c), with the
  invalid/recovery states documented as drop-reachable in the docs tab.
- SkillsGalleryPage story data catches up with the shipped feature set:
  an imported card with source provenance and a dimmed Archived card.

Storybook lint + static build green.
/m's browse pages now mirror prod's toolbar shape — title row, then
[primary create] [search, max-w-80] ... [archived link, right-aligned]:

- Agents: New agent moved from the title row's right edge to lead the
  toolbar beside search; 'Archived agents' links to a new
  /agents/archived route (the desktop archived page is app-layer legacy
  /m cannot import): its screen runs its own include_archived query,
  keeps only agents via each workflow's latest-revision is_agent flag,
  and offers Unarchive (invalidating the roster, which reads absence
  from the active list as archived).
- Skills: same toolbar; the Show-archived checkbox becomes the
  right-aligned 'Archived skills' ⇄ 'Hide archived' link (skills'
  archived view is inline in the same grid, so the link toggles rather
  than routes).
- Templates and sessions already render their shared desktop components
  and needed no change.

Live-verified on /m: both toolbars render per the prod screenshot; the
archived route loads with its empty state; tsc + lint clean.
… resilience

API: the tarball size cap is enforced while streaming; provenance links
persist with their workflow (a mid-import failure can no longer leave a
skill a rescan re-offers); duplicate slugs retry with a fresh suffix; the
sources listing goes through the service, not the DAO; query_head_revisions
raises on filters it does not implement instead of silently ignoring them.

Web: config rows referencing archived skills resolve through an
archived-inclusive list, so their detail drawer opens; a skipped nested
SKILL.md still fences its directory off the parent upload; pinned embeds
without a version degrade to follow-latest instead of dangling; the
pick-agents install skips agents that already embed the skill and both
skill commits ride the Fern client; publish-inline swaps by item identity,
not captured index; versions rail scrolls instead of truncating; archive,
batch-import and refresh flows surface errors and invalidate consistently;
gallery shows skeletons on first load. Long comments trimmed, stale doc
claims (raw-axios fallback, Claude skill-drop, registry source kind)
corrected.
@ardaerzin

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@github-actions

github-actions Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

📘 Docs preview

Status ✅ Ready
Preview https://pr-6604-agenta-docs-preview.mahmoud-637.workers.dev/docs
Inspect Actions run
Commit 7d2342a76e36f4f46559d8bf99ec7792f0869428

This comment updates in place on every push.

@coderabbitai

coderabbitai Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 11


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Team

Run ID: 2c12f76a-b341-4502-8a62-7ab892e0960c

📥 Commits

Reviewing files that changed from the base of the PR and between 95a9e22 and 0dd7846.

⛔ Files ignored due to path filters (55)
  • api/uv.lock is excluded by !**/*.lock
  • web/packages/agenta-api-client/src/generated/Client.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/resources/index.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/resources/sessions/client/Client.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/resources/sessions/client/requests/RemovePendingSessionInputRequest.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/resources/sessions/client/requests/SessionHeartbeatRequest.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/resources/sessions/client/requests/SessionInteractionRespondRequest.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/resources/sessions/client/requests/SessionRecordIngestRequest.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/resources/sessions/client/requests/SessionRecordQueryRequest.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/resources/sessions/client/requests/WatchSessionEventsRequest.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/resources/sessions/client/requests/index.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/resources/sessions/index.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/resources/sessions/types/IngestRecordRequest.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/resources/sessions/types/index.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/resources/skills/client/Client.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/resources/skills/client/index.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/resources/skills/client/requests/RefreshSkillSourceRequest.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/resources/skills/client/requests/SkillSourceImportRequest.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/resources/skills/client/requests/SkillSourceScanRequest.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/resources/skills/client/requests/SkillUsageRequest.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/resources/skills/client/requests/SkillsQueryRequest.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/resources/skills/client/requests/index.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/resources/skills/exports.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/resources/skills/index.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/types/ImportResult.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/types/ImportedSkill.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/types/OssSrcApisFastapiSessionsModelsSessionCapabilities.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/types/OssSrcCoreSessionsStreamsDtosSessionCapabilities.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/types/ParsedSkill.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/types/ParsedSkillFile.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/types/RefreshResult.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/types/RefreshedLink.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/types/ScanCandidate.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/types/ScanResult.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/types/SessionCancelRequest.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/types/SessionInteractionAnswerRequest.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/types/SessionListItem.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/types/SessionRecord.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/types/SessionRecordIngestRequest.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/types/SessionSnapshotResponse.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/types/SessionStream.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/types/SessionStreamResponse.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/types/SessionTranscriptWindowing.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/types/SkillIssue.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/types/SkillRegistryItem.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/types/SkillSource.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/types/SkillSourceRefreshRequest.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/types/SkillSourcesResponse.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/types/SkillUsageItem.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/types/SkillUsageResponse.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/types/SkillsResponse.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/types/SkippedSkill.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/types/SourceScanResult.ts is excluded by !**/generated/**
  • web/packages/agenta-api-client/src/generated/api/types/index.ts is excluded by !**/generated/**
  • web/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (146)
  • api/entrypoints/routers.py
  • api/oss/databases/postgres/migrations/core_oss/versions/oss000000027_add_skill_sources.py
  • api/oss/src/apis/fastapi/skills/__init__.py
  • api/oss/src/apis/fastapi/skills/exceptions.py
  • api/oss/src/apis/fastapi/skills/models.py
  • api/oss/src/apis/fastapi/skills/router.py
  • api/oss/src/core/git/interfaces.py
  • api/oss/src/core/skills/__init__.py
  • api/oss/src/core/skills/dtos.py
  • api/oss/src/core/skills/exceptions.py
  • api/oss/src/core/skills/fetcher.py
  • api/oss/src/core/skills/import_service.py
  • api/oss/src/core/skills/parser.py
  • api/oss/src/core/skills/service.py
  • api/oss/src/core/skills/sources_dtos.py
  • api/oss/src/core/workflows/build_kit.py
  • api/oss/src/core/workflows/service.py
  • api/oss/src/dbs/postgres/git/dao.py
  • api/oss/src/dbs/postgres/skills/__init__.py
  • api/oss/src/dbs/postgres/skills/dao.py
  • api/oss/src/dbs/postgres/skills/dbas.py
  • api/oss/src/dbs/postgres/skills/dbes.py
  • api/oss/src/utils/env.py
  • api/oss/tests/pytest/unit/applications/test_build_kit_overlay.py
  • api/oss/tests/pytest/unit/skills/__init__.py
  • api/oss/tests/pytest/unit/skills/test_import_service.py
  • api/oss/tests/pytest/unit/skills/test_parser.py
  • api/oss/tests/pytest/unit/skills/test_registry_listing.py
  • api/pyproject.toml
  • docs/design/agent-workflows/projects/skill-registry/backend-interface-review.md
  • docs/design/agent-workflows/projects/skill-registry/discovery.md
  • docs/design/agent-workflows/projects/skill-registry/plan-api.md
  • docs/design/agent-workflows/projects/skill-registry/plan-runner.md
  • docs/design/agent-workflows/projects/skill-registry/plan-web.md
  • docs/design/agent-workflows/projects/skill-registry/plan.md
  • docs/design/agent-workflows/projects/skill-registry/ux-plan.md
  • docs/design/agent-workflows/projects/skills-config/architecture.md
  • sdks/python/agenta/sdk/agents/adapters/harnesses.py
  • sdks/python/agenta/sdk/agents/platform/op_catalog.py
  • sdks/python/oss/tests/pytest/unit/agents/platform/test_op_catalog.py
  • services/runner/package.json
  • services/runner/src/engines/sandbox_agent/harness-trace-port.ts
  • services/runner/src/engines/sandbox_agent/run-plan.ts
  • services/runner/src/engines/sandbox_agent/run-turn.ts
  • services/runner/src/engines/skills.ts
  • services/runner/src/extensions/agenta.ts
  • services/runner/src/tracing/otel.ts
  • services/runner/src/tracing/pi-spool-protocol.ts
  • services/runner/tests/unit/cancel-continuity.test.ts
  • services/runner/tests/unit/kill-inflight-scope.test.ts
  • services/runner/tests/unit/pi-permission-failclosed.test.ts
  • services/runner/tests/unit/pi-spool-protocol.test.ts
  • services/runner/tests/unit/pi-trace-turn-export.test.ts
  • services/runner/tests/unit/reconstruct-resume-nonfatal.test.ts
  • services/runner/tests/unit/sandbox-agent-orchestration.test.ts
  • services/runner/tests/unit/sandbox-agent-qa-transcript-replay.test.ts
  • services/runner/tests/unit/sandbox-agent-run-plan.test.ts
  • services/runner/tests/unit/sandbox-lifecycle.test.ts
  • services/runner/tests/unit/session-keepalive-approval.test.ts
  • services/runner/tests/unit/session-keepalive-engine.test.ts
  • services/runner/tests/unit/skills.test.ts
  • services/runner/tests/unit/stuck-substitution-rebuild.test.ts
  • services/runner/tests/utils/sandbox-agent-harness.ts
  • services/runner/tests/utils/silent-turn.ts
  • web/ee/package.json
  • web/ee/src/pages/w/[workspace_id]/p/[project_id]/skills/index.tsx
  • web/mobile/next.config.ts
  • web/mobile/package.json
  • web/mobile/src/features/agents/AgentListScreen.tsx
  • web/mobile/src/features/agents/ArchivedAgentListScreen.tsx
  • web/mobile/src/features/chat/DrillInBridgeProvider.tsx
  • web/mobile/src/features/nav/useMobileNavItems.tsx
  • web/mobile/src/features/skills/SkillListScreen.tsx
  • web/mobile/src/pages/w/[workspace_id]/p/[project_id]/agents/archived/index.tsx
  • web/mobile/src/pages/w/[workspace_id]/p/[project_id]/skills/index.tsx
  • web/mobile/src/styles/globals.css
  • web/oss/next.config.ts
  • web/oss/package.json
  • web/oss/src/components/AgentChatSlice/hooks/useChatSlashCommands.tsx
  • web/oss/src/components/DrillInView/OSSdrillInUIProvider.tsx
  • web/oss/src/components/Sidebar/hooks/useSidebarConfig/index.tsx
  • web/oss/src/components/pages/skills/SkillsPage.tsx
  • web/oss/src/pages/w/[workspace_id]/p/[project_id]/skills/index.tsx
  • web/oss/tailwind.config.ts
  • web/packages/agenta-entities/src/workflow/api/api.ts
  • web/packages/agenta-entities/src/workflow/core/schema.ts
  • web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/AgentTemplateControl.tsx
  • web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/SkillFormView.tsx
  • web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/agentTemplate/CatalogListRow.tsx
  • web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/agentTemplate/ConfigItemList.tsx
  • web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/agentTemplate/ItemRow.tsx
  • web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/agentTemplate/itemDescriptors.tsx
  • web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/skillUpload.ts
  • web/packages/agenta-entity-ui/src/DrillInView/index.ts
  • web/packages/agenta-entity-ui/src/agent/AgentConfigSummaryCard.tsx
  • web/packages/agenta-entity-ui/src/agent/agentConfigSummary.ts
  • web/packages/agenta-entity-ui/tests/unit/agentConfigSummary.test.ts
  • web/packages/agenta-entity-ui/tests/unit/skillUpload.test.ts
  • web/packages/agenta-navigation/src/constants.ts
  • web/packages/agenta-sdk/src/resources.ts
  • web/packages/agenta-skills-ui/eslint.config.mjs
  • web/packages/agenta-skills-ui/package.json
  • web/packages/agenta-skills-ui/src/NewSkillMenuButton.tsx
  • web/packages/agenta-skills-ui/src/SkillCard.tsx
  • web/packages/agenta-skills-ui/src/SkillCreateDrawer.tsx
  • web/packages/agenta-skills-ui/src/SkillDetailDrawer.tsx
  • web/packages/agenta-skills-ui/src/SkillDetailHost.tsx
  • web/packages/agenta-skills-ui/src/SkillGallerySections.tsx
  • web/packages/agenta-skills-ui/src/SkillImportDrawer.tsx
  • web/packages/agenta-skills-ui/src/SkillPickerDrawer.tsx
  • web/packages/agenta-skills-ui/src/SkillPickerHost.tsx
  • web/packages/agenta-skills-ui/src/SkillSaveBlastRadius.tsx
  • web/packages/agenta-skills-ui/src/SkillUploadPanel.tsx
  • web/packages/agenta-skills-ui/src/SkillsGalleryPage.tsx
  • web/packages/agenta-skills-ui/src/SourceRefreshButton.tsx
  • web/packages/agenta-skills-ui/src/VersionsRailCard.tsx
  • web/packages/agenta-skills-ui/src/bridge.tsx
  • web/packages/agenta-skills-ui/src/index.ts
  • web/packages/agenta-skills-ui/src/registrySections.ts
  • web/packages/agenta-skills-ui/src/types.ts
  • web/packages/agenta-skills-ui/tsconfig.json
  • web/packages/agenta-skills-ui/vitest.config.ts
  • web/packages/agenta-skills/eslint.config.mjs
  • web/packages/agenta-skills/package.json
  • web/packages/agenta-skills/src/api/index.ts
  • web/packages/agenta-skills/src/core/schema.ts
  • web/packages/agenta-skills/src/embed/index.ts
  • web/packages/agenta-skills/src/index.ts
  • web/packages/agenta-skills/src/state/index.ts
  • web/packages/agenta-skills/tests/unit/embed.test.ts
  • web/packages/agenta-skills/tests/unit/schema.test.ts
  • web/packages/agenta-skills/tsconfig.json
  • web/packages/agenta-skills/vitest.config.ts
  • web/packages/agenta-ui/src/components/ui/empty-state.tsx
  • web/packages/agenta-ui/src/drill-in/context.ts
  • web/packages/agenta-ui/src/drill-in/context/DrillInUIContext.tsx
  • web/packages/agenta-ui/src/drill-in/context/index.ts
  • web/packages/agenta-ui/src/drill-in/index.ts
  • web/storybook/next.config.mjs
  • web/storybook/package.json
  • web/storybook/stories/skills-ui/SkillImportDrawer.stories.tsx
  • web/storybook/stories/skills-ui/SkillPickerDrawer.stories.tsx
  • web/storybook/stories/skills-ui/SkillSaveBlastRadius.stories.tsx
  • web/storybook/stories/skills-ui/SkillUploadPanel.stories.tsx
  • web/storybook/stories/skills-ui/SkillsGalleryPage.stories.tsx
  • web/storybook/stories/skills-ui/VersionsRailCard.stories.tsx
🚧 Files skipped from review as they are similar to previous changes (133)
  • services/runner/tests/unit/sandbox-lifecycle.test.ts
  • services/runner/src/engines/sandbox_agent/run-turn.ts
  • web/oss/tailwind.config.ts
  • web/packages/agenta-skills-ui/tsconfig.json
  • services/runner/tests/unit/reconstruct-resume-nonfatal.test.ts
  • web/mobile/src/pages/w/[workspace_id]/p/[project_id]/agents/archived/index.tsx
  • services/runner/src/extensions/agenta.ts
  • services/runner/tests/unit/cancel-continuity.test.ts
  • web/mobile/package.json
  • web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/agentTemplate/ConfigItemList.tsx
  • web/storybook/next.config.mjs
  • web/oss/src/pages/w/[workspace_id]/p/[project_id]/skills/index.tsx
  • services/runner/tests/unit/kill-inflight-scope.test.ts
  • web/packages/agenta-ui/src/drill-in/index.ts
  • web/mobile/src/features/nav/useMobileNavItems.tsx
  • services/runner/tests/unit/pi-trace-turn-export.test.ts
  • api/oss/src/core/workflows/build_kit.py
  • web/mobile/src/pages/w/[workspace_id]/p/[project_id]/skills/index.tsx
  • web/mobile/src/features/agents/ArchivedAgentListScreen.tsx
  • web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/agentTemplate/CatalogListRow.tsx
  • web/packages/agenta-entities/src/workflow/core/schema.ts
  • web/packages/agenta-ui/src/drill-in/context/index.ts
  • web/packages/agenta-skills-ui/eslint.config.mjs
  • web/mobile/src/features/chat/DrillInBridgeProvider.tsx
  • api/oss/tests/pytest/unit/skills/test_parser.py
  • web/ee/package.json
  • web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/SkillFormView.tsx
  • api/oss/src/apis/fastapi/skills/models.py
  • web/storybook/package.json
  • web/packages/agenta-skills-ui/src/SkillGallerySections.tsx
  • web/oss/src/components/pages/skills/SkillsPage.tsx
  • services/runner/tests/unit/skills.test.ts
  • web/oss/src/components/DrillInView/OSSdrillInUIProvider.tsx
  • web/packages/agenta-skills/package.json
  • web/packages/agenta-skills/vitest.config.ts
  • web/mobile/next.config.ts
  • api/oss/tests/pytest/unit/applications/test_build_kit_overlay.py
  • services/runner/src/tracing/pi-spool-protocol.ts
  • web/storybook/stories/skills-ui/SkillImportDrawer.stories.tsx
  • web/packages/agenta-entity-ui/tests/unit/agentConfigSummary.test.ts
  • web/packages/agenta-entity-ui/src/DrillInView/index.ts
  • web/packages/agenta-skills/tests/unit/schema.test.ts
  • web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/agentTemplate/ItemRow.tsx
  • web/oss/next.config.ts
  • services/runner/package.json
  • web/packages/agenta-entity-ui/tests/unit/skillUpload.test.ts
  • web/packages/agenta-skills-ui/src/bridge.tsx
  • sdks/python/oss/tests/pytest/unit/agents/platform/test_op_catalog.py
  • web/packages/agenta-skills/eslint.config.mjs
  • api/pyproject.toml
  • services/runner/tests/utils/silent-turn.ts
  • web/storybook/stories/skills-ui/SkillUploadPanel.stories.tsx
  • api/oss/src/core/skills/sources_dtos.py
  • web/packages/agenta-skills-ui/src/SkillImportDrawer.tsx
  • web/packages/agenta-ui/src/drill-in/context.ts
  • web/oss/src/components/AgentChatSlice/hooks/useChatSlashCommands.tsx
  • web/packages/agenta-skills-ui/src/types.ts
  • web/oss/package.json
  • api/oss/src/core/skills/exceptions.py
  • web/packages/agenta-skills-ui/src/NewSkillMenuButton.tsx
  • services/runner/tests/unit/stuck-substitution-rebuild.test.ts
  • web/packages/agenta-ui/src/components/ui/empty-state.tsx
  • web/storybook/stories/skills-ui/SkillsGalleryPage.stories.tsx
  • web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/skillUpload.ts
  • web/packages/agenta-skills/tsconfig.json
  • web/packages/agenta-skills-ui/src/SkillSaveBlastRadius.tsx
  • api/oss/src/core/skills/dtos.py
  • services/runner/tests/unit/sandbox-agent-qa-transcript-replay.test.ts
  • web/packages/agenta-skills-ui/vitest.config.ts
  • web/storybook/stories/skills-ui/SkillSaveBlastRadius.stories.tsx
  • sdks/python/agenta/sdk/agents/adapters/harnesses.py
  • services/runner/tests/utils/sandbox-agent-harness.ts
  • web/packages/agenta-skills-ui/src/SkillsGalleryPage.tsx
  • web/packages/agenta-navigation/src/constants.ts
  • web/packages/agenta-skills-ui/src/VersionsRailCard.tsx
  • services/runner/tests/unit/session-keepalive-approval.test.ts
  • web/packages/agenta-entities/src/workflow/api/api.ts
  • web/storybook/stories/skills-ui/SkillPickerDrawer.stories.tsx
  • services/runner/tests/unit/sandbox-agent-run-plan.test.ts
  • web/oss/src/components/Sidebar/hooks/useSidebarConfig/index.tsx
  • services/runner/src/engines/sandbox_agent/run-plan.ts
  • web/storybook/stories/skills-ui/VersionsRailCard.stories.tsx
  • api/oss/src/utils/env.py
  • api/oss/src/core/workflows/service.py
  • api/oss/src/dbs/postgres/skills/dbes.py
  • services/runner/src/tracing/otel.ts
  • api/oss/src/core/skills/service.py
  • services/runner/tests/unit/sandbox-agent-orchestration.test.ts
  • web/packages/agenta-skills-ui/package.json
  • api/oss/src/apis/fastapi/skills/exceptions.py
  • api/oss/src/dbs/postgres/skills/dbas.py
  • web/packages/agenta-skills-ui/src/SkillCard.tsx
  • web/ee/src/pages/w/[workspace_id]/p/[project_id]/skills/index.tsx
  • services/runner/tests/unit/pi-permission-failclosed.test.ts
  • api/oss/databases/postgres/migrations/core_oss/versions/oss000000027_add_skill_sources.py
  • web/packages/agenta-sdk/src/resources.ts
  • services/runner/tests/unit/session-keepalive-engine.test.ts
  • web/packages/agenta-skills/tests/unit/embed.test.ts
  • api/oss/tests/pytest/unit/skills/test_import_service.py
  • api/oss/src/dbs/postgres/skills/dao.py
  • web/mobile/src/features/skills/SkillListScreen.tsx
  • api/oss/src/core/git/interfaces.py
  • api/oss/src/core/skills/parser.py
  • web/packages/agenta-skills-ui/src/index.ts
  • services/runner/src/engines/sandbox_agent/harness-trace-port.ts
  • api/oss/src/apis/fastapi/skills/router.py
  • web/packages/agenta-skills/src/index.ts
  • web/packages/agenta-skills-ui/src/SourceRefreshButton.tsx
  • api/oss/tests/pytest/unit/skills/test_registry_listing.py
  • docs/design/agent-workflows/projects/skill-registry/plan-api.md
  • services/runner/src/engines/skills.ts
  • web/mobile/src/features/agents/AgentListScreen.tsx
  • web/packages/agenta-skills/src/embed/index.ts
  • web/packages/agenta-skills-ui/src/SkillPickerDrawer.tsx
  • docs/design/agent-workflows/projects/skill-registry/plan-runner.md
  • web/packages/agenta-skills-ui/src/registrySections.ts
  • web/packages/agenta-skills-ui/src/SkillPickerHost.tsx
  • web/packages/agenta-skills-ui/src/SkillDetailHost.tsx
  • services/runner/tests/unit/pi-spool-protocol.test.ts
  • web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/AgentTemplateControl.tsx
  • api/entrypoints/routers.py
  • web/packages/agenta-skills/src/core/schema.ts
  • web/packages/agenta-entity-ui/src/agent/agentConfigSummary.ts
  • web/packages/agenta-entity-ui/src/DrillInView/SchemaControls/agentTemplate/itemDescriptors.tsx
  • web/packages/agenta-ui/src/drill-in/context/DrillInUIContext.tsx
  • web/packages/agenta-skills-ui/src/SkillUploadPanel.tsx
  • api/oss/src/core/skills/fetcher.py
  • api/oss/src/dbs/postgres/git/dao.py
  • web/packages/agenta-entity-ui/src/agent/AgentConfigSummaryCard.tsx
  • web/packages/agenta-skills-ui/src/SkillCreateDrawer.tsx
  • docs/design/agent-workflows/projects/skill-registry/plan.md
  • web/packages/agenta-skills/src/api/index.ts
  • web/packages/agenta-skills-ui/src/SkillDetailDrawer.tsx

Included review availability: Your plan provides up to 8 included reviews per hour; 6 remain after this review.

from sqlalchemy.exc import IntegrityError

from oss.src.core.shared.exceptions import EntityCreationConflict
from oss.src.dbs.postgres.skills.dao import SkillSourcesDAO

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

Remove the concrete DAO dependency. SkillImportService must depend on a SkillSourcesDAOInterface; wire SkillSourcesDAO in api/entrypoints/*. As per coding guidelines: “Core services depend on interfaces (*DAOInterface), not concrete DB implementations.”

Source: Coding guidelines

Comment thread api/oss/src/core/skills/import_service.py Outdated
Comment thread api/oss/src/core/skills/import_service.py Outdated
Comment thread docs/design/agent-workflows/projects/skill-registry/discovery.md
Comment thread docs/design/agent-workflows/projects/skill-registry/plan-web.md Outdated
Comment thread docs/design/agent-workflows/projects/skill-registry/ux-plan.md
Comment thread docs/design/agent-workflows/projects/skill-registry/ux-plan.md Outdated
Comment thread docs/design/agent-workflows/projects/skills-config/architecture.md Outdated
Comment thread docs/design/agent-workflows/projects/skills-config/architecture.md Outdated
const search = get(skillsSearchAtom).trim()
const includeArchived = get(skillsShowArchivedAtom)
return {
queryKey: ["skills", "registry", "list", projectId, search, includeArchived],

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- state/index.ts ---'
cat -n web/packages/agenta-skills/src/state/index.ts
printf '%s\n' '--- sessionAtom definitions and uses ---'
rg -n -A12 -B8 'sessionAtom|auth.*change|logout|signOut|clear.*[Qq]uery|removeQueries|clearQueries|queryClient' web/packages web/oss/src 2>/dev/null | head -n 260
printf '%s\n' '--- skills state imports and cache helpers ---'
rg -n -A18 -B8 'invalidateSkillsListCache|atomWithQuery|queryKey' web/packages/agenta-skills web/packages/agenta-skills-ui

Repository: Agenta-AI/agenta

Length of output: 50372


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- sessionAtom definition ---'
rg -l 'export const sessionAtom|const sessionAtom' web/packages web/oss/src | while read -r f; do
  printf '%s\n' "--- $f"
  rg -n -A20 -B8 'export const sessionAtom|const sessionAtom' "$f"
done
printf '%s\n' '--- host QueryClient lifecycle ---'
rg -l 'function getHostQueryClient|const getHostQueryClient|export.*getHostQueryClient|HostQueryClient' web/packages web/oss/src | while read -r f; do
  printf '%s\n' "--- $f"
  rg -n -A30 -B12 'getHostQueryClient|QueryClient' "$f" | head -n 180
done
printf '%s\n' '--- auth session state transitions ---'
rg -n -A18 -B18 'set\(sessionAtom|sessionAtom.*atom|session.*set|signOut\(\)|Session\.signOut|onAuth|auth.*listener' web/packages/agenta-auth web/packages/agenta-shared web/oss/src 2>/dev/null | head -n 260

Repository: Agenta-AI/agenta

Length of output: 50373


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- shared session files ---'
find web/packages/agenta-shared/src -maxdepth 3 -type f -print | sort | grep -E 'session|auth|query'
printf '%s\n' '--- setSessionAtom callers ---'
rg -n -A12 -B12 'setSessionAtom|sessionAtom' web/packages web/apps web/oss/src 2>/dev/null | grep -E 'setSessionAtom|sessionAtom|signOut|logout' | head -n 220
printf '%s\n' '--- hostQueryClient definitions ---'
rg -n -A35 -B15 'getHostQueryClient|hostQueryClient|setHostQueryClient|QueryClientProvider' web/packages/agenta-shared/src web/apps web/oss/src 2>/dev/null | head -n 260

Repository: Agenta-AI/agenta

Length of output: 32140


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- session initialization and logout sync ---'
cat -n web/oss/src/state/appState/atoms.ts | sed -n '1,90p'
cat -n web/oss/src/config/frontendConfig.ts | sed -n '120,165p'
cat -n web/oss/src/state/profile/UserListener.tsx | sed -n '1,180p'
cat -n web/oss/src/hooks/useSession.ts | sed -n '1,140p'
printf '%s\n' '--- query persistence configuration ---'
rg -n -A25 -B12 'persist|dehydrate|hydrate|queryClient\.clear|removeQueries|clear\(' web/oss/src web/packages/agenta-shared/src | head -n 260

Repository: Agenta-AI/agenta

Length of output: 36763


Sensitive Data Exposure (CWE-200): Exposure of Sensitive Information to an Unauthorized Actor

Reachability: External · Exploitability: Moderate

Partition or clear the skills query cache on every auth transition. sessionAtom is only boolean, so an UNAUTHORISED transition can disable the query without removing its project-scoped cache; a new user can then see the previous user's cached registry data.

…s, doc contracts

An explicit empty paths selection now imports nothing instead of everything;
a re-import persists the requested ref so refresh follows it; tarball
extraction gains member-count and uncompressed-size ceilings (the download
cap only bounded compressed bytes). Docs record the resolved contracts:
publish-based inline migration, the v1 removal of the sync toggle, the
meta-first provenance decision, and the skill URI identity.
Brings the phone-width work and the config-drawer changes that landed in the
release while this stack was open. Overlapping files were merged, not picked:
SkillFormView keeps the one-pane phone layout AND the resizable rail (the
dragged width rides a CSS variable so the breakpoint stays pure CSS), the
bridge providers keep the skills bridge alongside the new gateway-tools and
LLM-provider wiring, and the sdk keeps both resource accessors. The release
dropping the Form/JSON toggle is authoritative, so a skill row now opens
through the drawer's own jsonOnly instead of the view argument this branch
had added.
@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Website preview

Preview URL: https://pr-6604-agenta-website-preview.mahmoud-637.workers.dev

Built from 1f6e77ab4ba1e1fa33fb69931e4bfb1dc3964123. This comment updates in place on every push.

At 390px the [New skill] [search] [Archived skills] row ran 68px past the
viewport, so the page scrolled sideways. The row now wraps, with the search
taking its own full-width line below the phone breakpoint.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@agenta/skills-ui shipped with a test script but no test files, and vitest
exits 1 on an empty suite — so the package failed CI on every PR in this
stack. These cover the logic that decides what the gallery shows: registry
identity and version tags, source labelling, grouping by source, a detached
import returning to This project while keeping its provenance, an orphaned
source row, and the rail counts.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
services/runner declared its overrides and patched dependencies at the top
level, where pnpm does not read them, so a frozen install failed with
ERR_PNPM_LOCKFILE_CONFIG_MISMATCH against a lockfile that does carry them —
every runner CI job died at Install dependencies. The release had already
nested them under "pnpm"; merging it in kept our stale copy, so this takes
their fix.

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

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Railway Preview Environment

Preview URL https://gateway-pr-6604.up.railway.app/w
Project agenta-oss-clone-spike
Image tag pr-6604-8693124
Status Deployed
Railway logs Open logs
Workflow logs View workflow run
Updated at 2026-09-08T13:54:09.342Z

…ll list

readPiTurnTraceControl always emits skillsDropped, defaulting to an empty list
when the control file predates the field. The deep-equal assertion still
described the pre-field shape, so it failed the moment the runner suite could
actually run again.

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

The agents and skills toolbars packed the create button, the search and the
archived link into one row, in the reverse of the desktop order. The row then
overflowed or split badly: the agents search held a fixed 20rem, so below about
480px the archived link crossed the right padding, and the skills search claimed
a full-width line of its own below 640px, stranding the other two at opposite
ends of an otherwise empty row.

Both screens now split the controls across the two rows they already had. The
archived link joins the title row, since it is a destination rather than a
control on this list. The toolbar below carries the search and the create action
alone, on desktop's axis (TableShell): search left and growing, action right.
The skills title also picks up the 16px phone rung the other /m list screens use.

`NewSkillMenuButton` gains a `className` for its placement, and `NewAgentAction`
stops shrinking when a header row asks it to hug the right edge.

Measured in the browser at 375, 560 and 768: the search and the title start at
the left padding, the action and the archived link end on the right one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* docs(skills): meta-first provenance decision record and execution plan

* feat(api): meta-first skill provenance — tables out, meta._ag in (P2-A1..A5)

Removes the skill_sources/skill_source_links tables, their migration, DAO,
and DTOs (which also resolves the duplicate oss000000027 revision id the
release merge introduced). Import now stamps nested meta._ag.origin on the
artifact and flat immutable meta._ag.provenance on each imported revision;
every skills-side meta write goes through one merge helper that preserves
foreign keys. Detachment is derived from content hashes on read, never
stored. Per-skill POST /skills/{id}/updates/check (read-only) and
/updates/apply (base-revision guarded) replace the per-source refresh, and
the sync_enabled toggle is gone — every update is check-then-apply. Scan
markers, import idempotency, and registry origin attribution all derive
from the artifact metadata (plan-meta-provenance.md).

* feat(sdk): point the skill update ops at the per-skill check/apply endpoints

check_skill_updates and apply_skill_update now target
/skills/{skill_id}/updates/{check,apply} with the skill's workflow id as the
argument; the hardwired apply flag is gone because the read/write split is
in the endpoints themselves.

* fix(api): drop the removed sources field from the skills query response

* feat(api): backend-owned meta._ag namespace, guarded at the git DAO

Per the review requirement: generic client writes can neither replace nor
remove meta._ag, and cannot forge it on create or commit — edits preserve
the stored value, creates and commits strip an incoming one (a local-edit
commit never inherits provenance, which derived detachment depends on).
One pure guard applied at every DAO meta write point covers every git
entity and every route, legacy included; the skills import/apply services
are the only trusted writers (platform_meta=True through the service
layer).

* fix(api): the import's workflow create is a trusted platform write

Without platform_meta=True the DAO's _ag guard stripped the import's own
provenance stamp — imports listed under This project with no origin. The
stub test now asserts the trust flag, since stubs cannot see the strip.

* fix(api): reconcile a lost checkpoint write from revision provenance

Applying an update commits the revision and advances the artifact checkpoint
as two writes. Losing the second one used to strand the skill as detached
forever, since detachment compares the head against that checkpoint. The
head revision's provenance is immutable and records what sync wrote, so the
anchor now reconciles against it: a head whose own provenance vouches for
its content is sync-owned, while content no stamp vouches for still reads as
a local edit. Applied on the read paths, so the registry's detached flag
heals too.

* [5512] feat(frontend): meta-first skill provenance (Package 2, web) (#6620)

* feat(frontend): meta-first skill provenance — origin off the item, per-skill updates

The registry item now carries its own origin (from artifact meta), so the
sources block, registrySourcesAtom, and the source-row joins are gone:
sections group client-side by origin.repository, and provenance display
derives from the item everywhere (detail drawer, picker, detail host). The
per-repo Refresh becomes Check updates → per-skill check calls with an
Apply follow-up, and the import drawer loses the Keep in sync switch —
every update is an explicit apply now. Fern client regenerated for the
check/apply endpoints.

* fix(frontend): keep partial apply results when one update fails

Promise.all rejected on the first failure, so successful workflow ids stayed
in the pending queue and a second Apply click re-submitted them. allSettled
keeps every outcome: applied skills leave the queue, only the failures stay
for a retry, and the summary reports both.

* test(frontend): move the grouping tests onto origin-based sections

The registry item now carries its own origin, so the suite that covered
source-row joins covers origin grouping instead — same behaviours, new
shape: repository grouping, a detached import returning to This project with
its provenance intact, and the skill ids a section hands the update check.

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

* [5512] feat(api): /skills lifecycle facade (#6624)

* feat(api): /skills lifecycle facade — create, commit, log, archive, referenced-by

The frontend stops calling generic workflow routes for skill writes: the
facade validates every payload against the SkillTemplate contract, generates
the suffixed storage slug, stamps the skill flags and builtin URI on both
records, and turns a moved head into a revision_conflict envelope instead of
a clobber. /skills/usage becomes the directional
GET /skills/{skill_id}/referenced-by; the registry-wide count stays on the
listing.

* fix(api): typed lifecycle DTOs and a skill guard on the facade commit

The facade returned raw dicts, against the API convention that services
answer with typed DTOs; SkillCreated/SkillCommitted/SkillRevisionRow now
carry those shapes. The commit route also stamps skill flags and the skill
URI, so committing through it to an agent would have silently rewritten that
agent as a skill — the target must already be one.

* [5512] feat(frontend): skill lifecycle through the /skills facade (#6625)

* feat(frontend): skill lifecycle through the /skills facade

createSkillWorkflow, commitSkillRevision, fetchSkillRevisions, and
archive/unarchive now ride the facade — the server owns validation, slug
generation, and flag stamping, and the drawer's commit sends the rendered
head as base_revision_id so concurrent edits conflict instead of clobbering.
querySkillUsage reads the directional referenced-by route. The agent-config
writes (addSkillToAgents, publish-inline) stay on the workflow layer: they
commit agent revisions, not skill lifecycle.

* fix(frontend): read the referenced-by rows the API actually returns

The endpoint answers {count, referenced_by} but the local schema still
declared usage. Passthrough made that parse succeed with the field
undefined, so the drawer's USED BY list and the archive blast-radius
warning rendered empty against a non-empty response. The schema, the client
function, and the drawer now use the directional name end to end, and the
tests assert the rows survive the parse instead of only that it succeeds.

* fix(frontend): base the skill commit on the revision the edit started from

The drawer read `head.id` at save time, so a background refetch between
opening the editor and saving would hand the concurrency check a base the
author never saw — defeating the check it exists for. The base is captured
when editing starts. Response validation also moves to the shared
safeParseWithLogging helper, per the frontend API convention.

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants