Files
waggle-os/docs/ux-refactor/gap-cards/S19-skill-builder.md
Oleg Maslov 0c3e2ead3b
Some checks failed
Installer Smoke / installer-smoke (push) Has been cancelled
moving
2026-09-02 10:10:29 +02:00

243 lines
17 KiB
Markdown

# Gap Card — S19 · Skill Builder
> UX-refactor planning artifact. Execution model is LOCKED **in-place incremental refactor**
> of `apps/web` + targeted sidecar extensions. Mockups are directional (PRD §24);
> PRD acceptance criteria win. Every claim below is grounded in a cited file.
>
> Sources: PRD §12.6 + §16.8 (`docs/.../Waggle_OS_UX_Refactor_PRD.md`), Blueprint Screen 19
> (`_blueprint_extracted.txt:417-421`, state model `:464`), mockup
> `Waggle_OS_Handoff_Assets/screens_18_21_builders_and_marketplace.png`, backend-map
> `sections/03d` + `05g`, inventories under `docs/ux-refactor/_inventory/`.
---
## 1. Screen & purpose
**Skill Builder** — the *create/edit* surface for a reusable capability (Skill). PRD §12.6:
a stepper Builder ("Basic Info → Instructions → Inputs & Outputs → Tools & Data → Review & Create"),
with **test-run before publishing** and assignment to agents/workspaces/automations or direct use.
Blueprint Screen 19 (`_blueprint_extracted.txt:417-421`): *"Create reusable capability. Define
prompt, inputs/outputs, tools, memory access, test."* States: *Draft; test pass/fail; published;
archived.* Acceptance: *"Skills are inspectable and reusable by agents/automations"* (PRD §12.6
acceptance: *"a user can understand what a skill does, where it is used, and what access it has"*).
This is the **Builder half of the Intelligence layer's Skills surface** (S06 Skills Hub is the
browse/library half). PRD Roadmap puts both in Sprint 6 / Phase 3 (`PRD:344-351`, `:236-240`).
The mockup tile labelled "Skill Builder" shows a left form column (name/description/category +
instructions textarea) and a right column (tools picker + memory access + a Test panel + a primary
"Create Skill" action) — directional confirmation of the §12.6 stepper.
---
## 2. Required states (PRD / Blueprint)
PRD §14.1 global states (every screen) + Blueprint Skills row (`_blueprint_extracted.txt:464`):
*Loading, empty, populated, error; Installed, draft, custom, workspace, marketplace, update available;
recovery: Test, install, publish, archive, rollback.*
Builder-specific lifecycle (Blueprint Screen 19 `:417-421`): **Draft → test pass/fail → published →
archived**, with PRD global add-ons: **validation error**, **approval required** (skills that request
elevated tool/secret access), **offline/local-only**.
Stepper steps the Builder must implement (PRD §12.6 `:542`):
1. **Basic Info** — name, description, category.
2. **Instructions** — the prompt/steps body.
3. **Inputs & Outputs** — declared input params + expected output shape.
4. **Tools & Data** — tools/data + **memory access** (read scope).
5. **Review & Create** — summary + **Test run** + publish-to-scope (personal/workspace/team).
Per-state behaviours required:
- **Validation error** — name normalises to kebab-case; empty name/description/steps blocked
(backend already 400s on these — §5).
- **Test pass/fail** — inline Test panel showing the injected-prompt preview + parsed metadata.
- **Published** — confirmation + the new skill appears in the Hub (Installed list).
- **Approval required** — when declared tools include elevated/secret/code-exec permissions
(frontmatter `permissions` block exists — `skill-frontmatter.ts:40-48`).
---
## 3. Current state in repo
**Disposition: `create-new`** (frontend Builder), **`keep-promote`** (backend structured-create
contract). There is **no Skill Builder component anywhere in `apps/web/src`** — grep for
`SkillBuilder|Skill Builder|skill-builder` over `apps/web/src` returns **0 matches**. Skill creation
today is only available to the *agent* (the `create_skill` tool, `skill-creator.ts:8`), not to the
*user* via UI.
What exists today:
- `apps/web/src/components/os/apps/CapabilitiesApp.tsx` — "Skills & Apps". A **browse/install/test**
surface only. Tabs: `installed | starter | marketplace | tools | audit` (`:80`, `:358`). It lists
packs (`PackCard` `:190`), shows a detail drawer (`PackDetail` `:249`), installs
(`handleInstall`/`handleMarketplaceInstall` `:164,:174`), and **tests** an existing skill
(`handleTestSkill``POST /api/skills/test` `:88-102`). **It has NO create/edit form.** This is
the natural host to add a "+ Create Skill" entry point that launches the new Builder.
- `apps/web/src/lib/adapter.ts:652``createSkill({ name, description })` exists but is **lossy**:
it POSTs to `/api/skills/create` with only `{name,description}` (no `steps`/`tools`/`category`),
while the backend requires a non-empty `steps[]` (`skills.ts:434`). So the one adapter method that
targets the create route is under-specified and **has no caller** in `apps/web/src` (grep:
`createSkill` appears only at its definition). Needs rework to the full template shape.
- `apps/web/src/lib/types.ts:210``SkillPack` (`id/name/description/category/skills[]/installed/trust`).
This is a **pack/catalog** shape, NOT a skill-authoring shape (no instructions/inputs/outputs/tools).
- `packages/agent/src/skill-creator.ts:11``SkillTemplate` (`name, description, triggerPatterns[],
steps[], tools[], category`) + `generateSkillMarkdown()` (`:26`). This is the canonical authored-skill
model the backend writes from.
- `packages/agent/src/skill-frontmatter.ts:53` — `ParsedSkill`/`SkillFrontmatter` (name, description,
`scope` personal→workspace→team→enterprise `:27`, `permissions` block `:40-48`). This is the read/parse
side; the Builder's "memory access / permissions" + "scope" map onto this.
Conclusion: backend authoring contract is solid and reusable; the **frontend Builder UI is greenfield**,
and the **adapter method must be widened** to the full template.
---
## 4. Frontend work
**Create (new):**
- `apps/web/src/components/os/overlays/SkillBuilder.tsx` — a 5-step stepper modal/drawer. Place it in
`overlays/` (consistent with `CreateWorkspaceDialog.tsx`, `SpawnAgentDialog.tsx`). Reuse the DS
Builder-stepper pattern (PRD §19.1 lists "Builder stepper" as a core component — none exists yet, so
this is the **first stepper**; AgentBuilder S18 + AutomationBuilder S20 should share it — see §7).
- Step 1 Basic Info: `name`, `description`, `category` (select from the `SKILL_FAMILIES` family set
in `skills.ts:13-31`: writing/research/decision/planning/communication/code/creative — surface
these as the category options, not the narrower `SkillPack.category` 5-union).
- Step 2 Instructions: ordered `steps[]` editor (add/remove/reorder) + optional `triggerPatterns[]`
(maps directly to `SkillTemplate.steps`/`.triggerPatterns`).
- Step 3 Inputs & Outputs: `inputs[]` / `outputs[]` param rows. **Backend does not persist these
structured today** (§5) — render them into the markdown body for v1, or block on the §5 backend
extension.
- Step 4 Tools & Data: `tools[]` multiselect (source the tool catalog already enumerated in
`CapabilitiesApp.tsx:422-434` — extract that hardcoded list to a shared module to avoid a third
copy; the agent tool registry is `packages/agent/src/tools.ts`) + **memory access / scope**
(`SkillScope` personal/workspace/team — `skill-frontmatter.ts:27`) + a `permissions` toggle group
(`skill-frontmatter.ts:40-48`).
- Step 5 Review & Create: summary card + **Test** button (calls existing test route) + Create.
- Optional `apps/web/src/components/os/overlays/skill-builder/` substeps if the file exceeds ~400 LOC
(CLAUDE.md file-org rule).
**Rework:**
- `apps/web/src/lib/adapter.ts` — widen `createSkill` (`:652`) to
`createSkill(t: { name; description; steps: string[]; tools?: string[]; category?: string;
triggerPatterns?: string[] })` (full `SkillTemplate` shape, matching `skills.ts:424-430`). Add
`updateSkill(name, content)` → `PUT /api/skills/:name` (`skills.ts:506`) for edit, and reuse the
existing test call (CapabilitiesApp inlines `fetch('/api/skills/test')` `:91` — promote it to an
`adapter.testSkill(skillName, testInput?)` method so the Builder and CapabilitiesApp share one path).
- `apps/web/src/components/os/apps/CapabilitiesApp.tsx` — add a "+ Create Skill" button (header,
next to the grid/list toggle `:340`) that dispatches `waggle:open-app` or opens the Builder overlay
via `useOverlayState` (the overlay open/close registry — `hooks/useOverlayState.ts`, per frontend
inventory §c). Optionally render an edit affordance on installed custom skills.
**Wiring:**
- Register the overlay in `Desktop.tsx` (overlays block) + add a flag to `useOverlayState`.
- Ctrl+K "Create → Skill" command should open it (PRD §12.3 Create category; `GlobalSearch.tsx`
already loads skills `:129`).
**Props/state:** local stepper state object `{ step, name, description, category, steps[],
triggerPatterns[], inputs[], outputs[], tools[], scope, permissions, testResult, status }`. On
Create → `adapter.createSkill(template)`; on Test → `adapter.testSkill(name)`. No new global store;
optimistic insert into the Skills Hub list on success (CapabilitiesApp already refetches via
`getSkills`).
---
## 5. Backend work (PRD §16.8)
| PRD §16.8 endpoint | Status | Note / what to EXTEND vs NET-NEW | Substrate |
|---|---|---|---|
| `GET /api/skills` | **EXISTS** | `skills.ts:164` — list installed skills. Hub/Builder list source. | `~/.waggle/skills/*.md` files |
| `POST /api/skills` | **EXISTS** | `skills.ts:167` raw `{name,content}`; **`POST /api/skills/create` (`skills.ts:431`) is the structured one** the Builder should use (`{name,description,steps[],tools?,category?}` → `generateSkillMarkdown` + `redactSkillContent` + audit + hash). | skill files + `auditStore` + `skillHashStore` |
| `PATCH /api/skills/:id` | **PARTIAL** | Update is `PUT /api/skills/:name` keyed by **name**, method **PUT**, body `{content}` (`skills.ts:506-510`). PRD uses `PATCH`+`:id`. **EXTEND**: accept `PATCH` alias + name↔id mapping (skills have no numeric id — name *is* the id). | skill files |
| `POST /api/skills/:id/test` | **PARTIAL** | Test exists as **`POST /api/skills/test`** (body `{skillName, testInput?}`, `skills.ts:570-575`) — not a per-id path. **EXTEND**: add `:id`-path variant (or keep body-driven; Builder can call either). Returns injected-prompt preview + metadata. | skill files + starter dir |
| `POST /api/skills/:id/install` | **PARTIAL** | No unified per-skill install-by-id. Closest: `POST /api/skills/starter-pack/:id` (`skills.ts:161`), `POST /api/skills/capability-packs/:id` (`:163`), marketplace `POST /api/marketplace/install` (`marketplace.ts`, **Tier: PRO**, SecurityGate). **EXTEND**: add a dispatcher `/skills/:id/install` that routes by source — OR leave install to the Hub (S06) and have the *Builder* only create (create == installed, since authored skills write straight to `~/.waggle/skills/`). | skill files / marketplace |
**Net-new backend gap surfaced by the PRD stepper (NOT in §16.8, but needed for §12.6 Step 3):**
the structured-create contract (`SkillTemplate`, `skill-creator.ts:11`) has **no `inputs`/`outputs`
and no `memoryScopes` fields**. PRD §15 skill object (and Blueprint §551 "Skill … inputs, outputs,
requiredTools, requiredMemoryScopes") expects them. Two options, both in-place:
- **v1 (no migration, recommended):** Builder serialises inputs/outputs/memory-scope into the
markdown body via an extended `generateSkillMarkdown` (add optional `inputs?`/`outputs?`/`scope?` to
`SkillTemplate`); they round-trip as `## Inputs` / `## Outputs` / frontmatter `scope:` sections.
`parseSkillFrontmatter` already reads `scope` (`skill-frontmatter.ts:27`) and `permissions`.
- **v2 (later):** promote to structured frontmatter if inputs/outputs become a query/filter axis.
**No `.mind` migration.** Skills are **filesystem markdown** (`~/.waggle/skills/*.md`), not SQLite —
confirmed `skills.ts:46-49`. The only DB touch is the **install-audit** record on create
(`skills.ts:476`, writes to `.mind` install_audit) + the skill-hash store. The audit `record()` call
already passes `riskLevel:'low'` so it sidesteps the latent `risk_level` CHECK drift noted in
substrate-types §(d) — no new exposure.
**Security:** create/update already pass content through `redactSkillContent()` (strips
secrets+user-paths, `skills.ts:464,:521`) and guard path-traversal in the name (`:447`). The Builder
must NOT bypass these — always go through `/api/skills/create` / `PUT /api/skills/:name`, never write
files another way. Elevated `permissions` (codeExecution/secrets/network) in a skill should trigger
the §2 "approval required" UX (PRD §12.6 / §17.3 elevated-action approval) — currently no approval
gate fires on skill authoring; flag as an open item (§9).
---
## 6. Shared types needed (PRD §15 vs `lib/types.ts`)
PRD §15.2 declares `ExtensionType = 'skill' | ...` — **MISSING** in `apps/web/src/lib/types.ts`
(substrate-types §e). For S19 specifically:
- **Add `SkillTemplate` (authoring) FE type** mirroring `packages/agent/src/skill-creator.ts:11`
(currently the FE only has the catalog-shaped `SkillPack` `types.ts:210`). Fields:
`name, description, category, steps[], triggerPatterns[], tools[]` + PRD additions
`inputs[]`, `outputs[]`, `scope` (`SkillScope`), `permissions`. Keep `SkillPack` (catalog) distinct
from `SkillTemplate` (authoring) — they are different objects.
- **Reuse `SkillScope`** (`personal|workspace|team|enterprise`) from `skill-frontmatter.ts:27` rather
than redeclaring (export it through the FE if needed; PRD §15.2 `Scope` is the broader
personal/workspace/team/organization — reconcile: skills use the `enterprise` variant).
- `SkillPack.category` is a narrow 5-union (`types.ts:213`) that does **not** match the backend
`SKILL_FAMILIES` 7-set (`skills.ts:13`). Widen the FE category union to the family set so the Builder
category picker and the Hub badges agree.
---
## 7. Dependencies (screens/phases first)
- **Phase/Sprint:** PRD Sprint 6 / Phase 3 — Intelligence layer (`PRD:344-351`, `:236-240`).
- **Hard prereq — DS Builder Stepper component** (PRD §19.1). None exists. S19 is the cheapest place
to introduce it, but **S18 Agent Builder** and **S20 Automation Builder** share the exact same
stepper pattern — build the stepper as a reusable primitive (`components/ui/` or
`components/os/overlays/builder-stepper/`) so all three Builders consume it.
- **Sibling — S06 Skills Hub** (the browse/library half, hosted today by `CapabilitiesApp.tsx`).
S19 launches *from* the Hub and writes *into* it (created skill → Installed list). They should ship
together or S06 first.
- **Soft — Command Center (Ctrl+K) (S03)** for the "Create → Skill" entry (PRD §12.3); not blocking.
- **Soft — Agent Builder (S18) / Automation Builder (S20)** consume skills via `skillIds`
(PRD §15.5) — they depend on skills *existing*, not on the Builder UI. Skill→agent assignment is a
downstream wiring concern.
---
## 8. Effort: **M**
Backend is mostly **EXISTS/PARTIAL** (structured-create, test, update all present — only thin aliases
`PATCH`/`:id`-path + an optional `inputs/outputs/scope` extension to `generateSkillMarkdown`, no DB
migration). The frontend is a net-new 5-step stepper + adapter widening + a reusable stepper primitive
shared with S18/S20 — real but bounded UI work. Not S (greenfield component + 3 endpoint extensions);
not L (no new substrate, no migration, backend authoring path already proven via the `create_skill`
tool).
---
## 9. Open questions
1. **Install vs create semantics.** Authored skills write straight to `~/.waggle/skills/` (create ==
installed). Does PRD `POST /api/skills/:id/install` (§16.8) apply to the *Builder* at all, or only
to the *Hub/Marketplace* install flow? Recommend: Builder = create-only; install-by-id lives in S06.
2. **Inputs/Outputs persistence.** v1 markdown-body serialisation vs structured frontmatter — confirm
whether inputs/outputs need to be *queryable* (drives the v1-vs-v2 backend choice in §5).
3. **Approval gate on elevated skills.** Should authoring a skill that declares
`permissions: {codeExecution|secrets|network}` trigger the PRD §17.3 approval prompt at *create*
time, or only at *run* time? No gate fires today on authoring.
4. **Scope vocabulary reconciliation.** `SkillScope` uses `enterprise`; PRD §15.2 `Scope` uses
`organization`. Pick one for the Builder's publish-to-scope picker.
5. **Test-run fidelity.** `POST /api/skills/test` returns only the *injected-prompt preview* + parsed
metadata (`skills.ts:570`), it does **not** execute the skill against an LLM. Is preview-only
sufficient for the PRD "test pass/fail" state, or is a live dry-run expected? (Live run would be
net-new and lean on `/api/agent/run`.)
6. **Category source of truth.** Reconcile `SkillPack.category` (5-union) vs `SKILL_FAMILIES` (7-set)
vs `SkillTemplate.category` (free string) — which is canonical for the picker?