Commit 8a768b6
authored
feat(ui): Skills management surface — unhide and rebuild the Skills tab (ent#235) (#1877)
* feat(ui): Skills management surface — unhide and rebuild the Skills tab (ent#235)
The skills machinery shipped across three planes (#182 distribute/place/expose,
#183 package injection with a per-skill result contract) and nothing rendered
any of it. The Agent Detail Skills tab was excluded from `visibleTabs` per
requirements §22.2 ("component preserved for potential admin-only access"),
assignment was REST/MCP-only (§21.3), and #183's statuses and named warnings had
no consumer at all. A user could not browse the library, see what an agent had,
or assign anything.
What lands:
* **Tab unhidden** for owners/admins on non-system agents, matching the other
management tabs. `OverflowTabs` absorbs it.
* **`stores/skills.js`** — a domain store (Invariant #6). The old panel called
`axios` directly with a hand-built auth header, silently bypassing the shared
client every other call relies on; everything now goes through `api`
(Invariant #7).
* **Library browse** with the §21.6 contract surfaced: description, automation,
`user_invocable`, declared `requires` (binaries/packages/env), multi-file file
count, size, and the git tree SHA as version. Dependencies are shown BEFORE
assignment, because they are what later becomes a `missing_binary:*` warning.
* **Assignment** with bulk save through the existing `PUT .../skills`, plus a
dirty/reset affordance so a half-made selection is recoverable.
* **Honest injection status.** This is the load-bearing part. #183 reports
`injected | unchanged | fallback | failed` with named warnings; the panel
renders the verdict per skill and translates the tokens into what they mean
for THIS agent ("`jq` is not installed in this agent — the skill may not
run"). `fallback` renders as "partial" in amber, never a green tick — an
explicit AC. Injection results are kept separate from assignment in the store
precisely so a durable assignment cannot be painted with a stale success.
* **Manual sync** (`force=True` repair action) with in-flight state, and a 409
from `SkillInjectionBusy` reported as "already running" rather than a generic
failure.
* **No dead empty states** — the store computes one discriminator
(`library_unconfigured` / `library_empty` / `none_assigned`) so the panel
cannot invent a fourth. Unconfigured routes an admin to Settings and tells a
non-admin to ask one.
* **Stopped agent** renders persisted assignment state with Sync disabled and
the reason in the tooltip, rather than offering an action that would fail.
Verified against the live instance: tab appears, 3-skill library renders with
contract fields, bulk assign persists, agent started, "Sync now" returns
`{haiku: injected/2 files, word-count: injected/2 files}` and the badges +
last-sync line render from that response.
Gating confirmed OSS-core with the issue author before building: every file here
is already public, the endpoints are ungated, and the paid piece (skill_runner,
ent#139) plus exposure curation (#178) are both explicitly out of scope.
Related to trinity-enterprise#235
* fix(ui): dead Settings link + error swallowed as an empty library (ent#235 review)
Self-review of #1877 found two defects, both in the "no dead empty states" AC
this panel exists to satisfy.
1) The "Configure the library" CTA linked to `/settings?tab=skills`. There is no
such tab — the Skills Library config lives under Settings → **agents**
(`Settings.vue`, `v-if="activeTab === 'agents'"`). So the one call-to-action
offered to an admin staring at an unconfigured library went nowhere. I also
asserted in the PR body that the Settings panel already reported sync status
/ last-synced / skill count without checking; it does report all three — but
I had the tab wrong, which is what checking would have caught.
2) `api.get('/api/skills/library').catch(() => ({ data: [] }))` swallowed every
error, not just the unconfigured case: a 500, a timeout or an auth failure
all rendered as "the library is configured but has no skills yet" — a
confident, wrong empty state that points the operator at the wrong problem.
The list is now fetched only when `status.configured` is true, so the known
empty state comes from the status read and any other failure surfaces as one.
Related to trinity-enterprise#2351 parent dc4f490 commit 8a768b6
3 files changed
Lines changed: 396 additions & 282 deletions
0 commit comments