Skip to content

feat(skills): foundation + web-search pilot + market-monitor template (#543)#552

Open
clemenshelm wants to merge 2 commits into
mainfrom
feat/skill-layer-foundation
Open

feat(skills): foundation + web-search pilot + market-monitor template (#543)#552
clemenshelm wants to merge 2 commits into
mainfrom
feat/skill-layer-foundation

Conversation

@clemenshelm

Copy link
Copy Markdown
Contributor

Summary

  • v1 of the Pinchy Skill Layer (master tracking issue RFC/Tracking: Pinchy Skill Layer (consolidates #354/#126/#372) #543). Adopts OpenClaw 2026.6.x's native skill mechanics — SKILL.md with YAML frontmatter, six-tier loading precedence, per-agent allowlist via agents.list[].skills — as Pinchy's composition unit for agent capabilities.
  • First pinchy-web template: Market & News Monitor, lands in the existing marketing-web category. Resolves the gap that triggered this whole initiative — pinchy-web was the last external plugin without templates.
  • Foundation only: existing 33 templates unchanged, defaultSkills field is additive, behavior preserved.

What changes

  • Schema: agents.skills jsonb column (migration 0040). Default []. Existing rows seeded with [].
  • Skills foundation:
    • KNOWN_SKILLS const + drift-guard test (pairs the const list with on-disk SKILL.md files — same convention as KNOWN_PINCHY_PLUGINS).
    • First-party skill web-search with workflow guidelines for pinchy_web_search / pinchy_web_fetch. Source-grounded, recency-aware, no-fabrication invariants in the body.
    • writeWorkspaceSkill(agentId, skillId, content) materializes SKILL.md to <workspace>/skills/<id>/SKILL.md — the highest-precedence skill tier in OC's loader.
  • Config emission: regenerateOpenClawConfig writes agents.list[].skills for every agent, always, including empty. Verified in Docker smoke-test against OC 2026.6.5: empty allowlist correctly excludes all 58 bundled OC desktop skills (1password, apple-notes, blucli, bear-notes, ...) — the right default for enterprise agents.
  • Template schema: AgentTemplate.defaultSkills?: string[] added additively. Drift-guard ensures every entry is a KNOWN_SKILLS member.
  • Pilot template market-monitor ("Market & News Monitor") in marketing-web, plugin pinchy-web, defaultSkills: ["web-search"]. Persona-only AGENTS.md; workflow lives in the skill so it can be reused by Phase 2 hybrids (Lead Researcher, Competitive Intelligence, Outreach Drafter).
  • UI surface: requiresWeb access badge ("Web · Public search") and permission preview ("Search the public web / Fetch public web pages / Cannot access your private data") for pinchy-web templates. Plumbed through /api/templates and new-agent-form.tsx.
  • Audit: agent.created detail now snapshots skills alongside the existing templateId / modelSelection.
  • Docs: new guide guides/create-market-monitor-agent.mdx, linked in sidebar.

Why this shape

Three closed RFCs (#354 Template Contract, #126 MCP Tool Profiles, #372 Agent-Builder-Agent) are consolidated into master #543 because they all describe the same architecture: composable agent capabilities. OpenClaw 2026.6.5 (bundled in Pinchy v0.6.0) implements the AgentSkills.io spec natively — composer, loader, allowlist, gating, Skill Workshop, ClawHub marketplace. Pinchy gets the mechanics for free; this PR just writes the files and emits the allowlist.

The smoke-test against OC 2026.6.5 confirmed three load-bearing properties:

  • Per-agent workspaces isolate skills (Agent A sees skill-a only, Agent B sees skill-b only)
  • Allowlist enforcement is hard — skills: [] puts all 58 bundled skills into "excluded"
  • <workspace>/skills/<id>/SKILL.md is the correct path (openclaw-workspace source in openclaw skills list)

What's next (out of scope for this PR)

Phase 1 sub-issues already filed and tagged priority:P1:

Phase 2 (hybrid templates: Lead Researcher = Web + Odoo etc.), Phase 3 (generated AGENTS.md, supersedes #354), and Phase 4 (MCP UI supersedes #126, ClawHub integration, Skill Workshop UI supersedes #372) are documented in master #543 — sub-issues filed when those phases start.

Test plan

  • pnpm test — full unit + integration suite green (6225 passed, 0 failed)
  • pnpm test:db — Drizzle migrations apply cleanly (115 passed)
  • pnpm test:scripts — 126 passed
  • pnpm tsc --noEmit — clean
  • pnpm lint — 0 errors (warnings are pre-existing)
  • cd docs && pnpm build — Astro build clean
  • Docker smoke-test against ghcr.io/heypinchy/pinchy-openclaw:v0.6.0 — verified <workspace>/skills/<id>/SKILL.md loads, per-agent isolation, empty-allowlist semantics
  • Reviewer: try the Market & News Monitor template end-to-end in dev compose (docker compose -f docker-compose.yml -f docker-compose.dev.yml up --build), confirm the agent picks up the skill and cites sources
  • Reviewer: confirm existing templates still create agents with skills: [] (no regression)

Tracking

Closes #354 #126 #372. Tracked in #543.

clemenshelm pushed a commit that referenced this pull request Jun 18, 2026
Addressing the independent code-review pass on #552:

* Soft-deleted agents no longer get SKILL.md materialized into their
  stale workspaces. The pre-existing config-emission pattern still
  builds entries for `deletedAt` agents (broader to untangle), but the
  new skill-write step short-circuits on them so the workspaces don't
  accumulate dead files.
* `getSkillBody()` now memoizes the SKILL.md body in a process-local
  cache. With 50 agents on the same skill, this drops the regenerate
  from 50 redundant disk reads to one. First-party skill bodies are
  bundled with the build and never change at runtime, so the cache has
  no invalidation semantics to worry about.
* Skill materialization moved out of the `agentsList.map(...)` callback
  into a dedicated post-pass — `.map` shouldn't have side effects, and
  the second pass is the natural place for the `deletedAt` guard.
* `assertValidSkillId` tightened to `/^[a-z][a-z0-9-]*$/` so digit-first
  ids ("99-bottles") and uppercase ids ("WebSearch") are rejected. The
  digit case was accepted previously even though it breaks the
  AgentSkills.io convention this layer follows. Two new test cases.
* Drift guard broadened: `agent-templates-web.test.ts` now iterates
  every `AGENT_TEMPLATES` entry and asserts that any `defaultSkills`
  member is in `KNOWN_SKILLS`. Catches a template-skill mismatch at
  test time rather than at the runtime guard in `regenerateOpenClawConfig`
  (which only fires when a user instantiates the template).
* `?? []` defense-in-depth on `agent.skills` documented inline — the
  column is `notNull().default('[]')`, so the null path is unreachable
  post-migration; the fallback is there to keep an in-flight regenerate
  race quiet rather than throwing an opaque TypeError.
* Self-contradicting comment in `web-agents.ts` removed ("No
  allowedTools listed here" sat above `allowedTools: [...]`).
* `web-search` SKILL.md: Safety section moved ahead of Output format so
  the model attends to it earlier in the prompt.
* Doc examples in `create-market-monitor-agent.mdx` rephrased to be
  evergreen ("this quarter", "in our industry") instead of bound to
  specific 2026 events.
clemenshelm pushed a commit that referenced this pull request Jun 19, 2026
Addressing the independent code-review pass on #552:

* Soft-deleted agents no longer get SKILL.md materialized into their
  stale workspaces. The pre-existing config-emission pattern still
  builds entries for `deletedAt` agents (broader to untangle), but the
  new skill-write step short-circuits on them so the workspaces don't
  accumulate dead files.
* `getSkillBody()` now memoizes the SKILL.md body in a process-local
  cache. With 50 agents on the same skill, this drops the regenerate
  from 50 redundant disk reads to one. First-party skill bodies are
  bundled with the build and never change at runtime, so the cache has
  no invalidation semantics to worry about.
* Skill materialization moved out of the `agentsList.map(...)` callback
  into a dedicated post-pass — `.map` shouldn't have side effects, and
  the second pass is the natural place for the `deletedAt` guard.
* `assertValidSkillId` tightened to `/^[a-z][a-z0-9-]*$/` so digit-first
  ids ("99-bottles") and uppercase ids ("WebSearch") are rejected. The
  digit case was accepted previously even though it breaks the
  AgentSkills.io convention this layer follows. Two new test cases.
* Drift guard broadened: `agent-templates-web.test.ts` now iterates
  every `AGENT_TEMPLATES` entry and asserts that any `defaultSkills`
  member is in `KNOWN_SKILLS`. Catches a template-skill mismatch at
  test time rather than at the runtime guard in `regenerateOpenClawConfig`
  (which only fires when a user instantiates the template).
* `?? []` defense-in-depth on `agent.skills` documented inline — the
  column is `notNull().default('[]')`, so the null path is unreachable
  post-migration; the fallback is there to keep an in-flight regenerate
  race quiet rather than throwing an opaque TypeError.
* Self-contradicting comment in `web-agents.ts` removed ("No
  allowedTools listed here" sat above `allowedTools: [...]`).
* `web-search` SKILL.md: Safety section moved ahead of Output format so
  the model attends to it earlier in the prompt.
* Doc examples in `create-market-monitor-agent.mdx` rephrased to be
  evergreen ("this quarter", "in our industry") instead of bound to
  specific 2026 events.
@clemenshelm clemenshelm force-pushed the feat/skill-layer-foundation branch 2 times, most recently from 3b3ee65 to 2ef0662 Compare June 19, 2026 07:26
clemenshelm pushed a commit that referenced this pull request Jun 19, 2026
Addressing the independent code-review pass on #552:

* Soft-deleted agents no longer get SKILL.md materialized into their
  stale workspaces. The pre-existing config-emission pattern still
  builds entries for `deletedAt` agents (broader to untangle), but the
  new skill-write step short-circuits on them so the workspaces don't
  accumulate dead files.
* `getSkillBody()` now memoizes the SKILL.md body in a process-local
  cache. With 50 agents on the same skill, this drops the regenerate
  from 50 redundant disk reads to one. First-party skill bodies are
  bundled with the build and never change at runtime, so the cache has
  no invalidation semantics to worry about.
* Skill materialization moved out of the `agentsList.map(...)` callback
  into a dedicated post-pass — `.map` shouldn't have side effects, and
  the second pass is the natural place for the `deletedAt` guard.
* `assertValidSkillId` tightened to `/^[a-z][a-z0-9-]*$/` so digit-first
  ids ("99-bottles") and uppercase ids ("WebSearch") are rejected. The
  digit case was accepted previously even though it breaks the
  AgentSkills.io convention this layer follows. Two new test cases.
* Drift guard broadened: `agent-templates-web.test.ts` now iterates
  every `AGENT_TEMPLATES` entry and asserts that any `defaultSkills`
  member is in `KNOWN_SKILLS`. Catches a template-skill mismatch at
  test time rather than at the runtime guard in `regenerateOpenClawConfig`
  (which only fires when a user instantiates the template).
* `?? []` defense-in-depth on `agent.skills` documented inline — the
  column is `notNull().default('[]')`, so the null path is unreachable
  post-migration; the fallback is there to keep an in-flight regenerate
  race quiet rather than throwing an opaque TypeError.
* Self-contradicting comment in `web-agents.ts` removed ("No
  allowedTools listed here" sat above `allowedTools: [...]`).
* `web-search` SKILL.md: Safety section moved ahead of Output format so
  the model attends to it earlier in the prompt.
* Doc examples in `create-market-monitor-agent.mdx` rephrased to be
  evergreen ("this quarter", "in our industry") instead of bound to
  specific 2026 events.
Clemens Helm added 2 commits June 19, 2026 09:30
…#543)

Adopts OpenClaw 2026.6.x's native skill mechanics — SKILL.md, six-tier
loading precedence, per-agent `agents.list[].skills` allowlist — as
Pinchy's composition unit for agent capabilities. v1 reference impl per
master tracking issue #543.

* Add `agents.skills jsonb` column with a Drizzle migration. Empty
  default; existing rows untouched.
* New `KNOWN_SKILLS` const with drift-guard test enforcing on-disk
  truth (workspace `<id>/SKILL.md` ↔ list).
* New first-party skill `web-search` with workflow guidelines for
  `pinchy_web_search` / `pinchy_web_fetch` (source-grounded, recency-
  aware, no-fabrication invariants).
* `writeWorkspaceSkill` materializes SKILL.md to
  `<workspace>/skills/<id>/SKILL.md` so OC's workspace-tier loader
  picks it up.
* `regenerateOpenClawConfig` emits `agents.list[].skills` for every
  agent — always, even empty. Verified in Docker smoke-test against OC
  2026.6.5: `skills: []` excludes all 58 bundled OC desktop skills
  (1password, apple-notes, ...) that are irrelevant for enterprise
  agents.
* `AgentTemplate.defaultSkills?: string[]` added additively. Existing
  33 templates unchanged; new agents inherit the template's allowlist
  at create.
* Pilot template `market-monitor` ("Market & News Monitor") in the
  marketing-web category, pinchy-web plugin, defaultSkills:
  ["web-search"]. Persona-only AGENTS.md; workflow lives in the skill.
* `requiresWeb` access badge + permission preview for `pinchy-web`
  templates; templates API + new-agent-form propagate the flag.
* Audit hook snapshots `skills` in `agent.created` detail.
* Docs: `guides/create-market-monitor-agent.mdx`.

Closes #354 #126 #372 (consolidated into master #543). Phase 1
migration sub-issues #544 #545 #546.
Addressing the independent code-review pass on #552:

* Soft-deleted agents no longer get SKILL.md materialized into their
  stale workspaces. The pre-existing config-emission pattern still
  builds entries for `deletedAt` agents (broader to untangle), but the
  new skill-write step short-circuits on them so the workspaces don't
  accumulate dead files.
* `getSkillBody()` now memoizes the SKILL.md body in a process-local
  cache. With 50 agents on the same skill, this drops the regenerate
  from 50 redundant disk reads to one. First-party skill bodies are
  bundled with the build and never change at runtime, so the cache has
  no invalidation semantics to worry about.
* Skill materialization moved out of the `agentsList.map(...)` callback
  into a dedicated post-pass — `.map` shouldn't have side effects, and
  the second pass is the natural place for the `deletedAt` guard.
* `assertValidSkillId` tightened to `/^[a-z][a-z0-9-]*$/` so digit-first
  ids ("99-bottles") and uppercase ids ("WebSearch") are rejected. The
  digit case was accepted previously even though it breaks the
  AgentSkills.io convention this layer follows. Two new test cases.
* Drift guard broadened: `agent-templates-web.test.ts` now iterates
  every `AGENT_TEMPLATES` entry and asserts that any `defaultSkills`
  member is in `KNOWN_SKILLS`. Catches a template-skill mismatch at
  test time rather than at the runtime guard in `regenerateOpenClawConfig`
  (which only fires when a user instantiates the template).
* `?? []` defense-in-depth on `agent.skills` documented inline — the
  column is `notNull().default('[]')`, so the null path is unreachable
  post-migration; the fallback is there to keep an in-flight regenerate
  race quiet rather than throwing an opaque TypeError.
* Self-contradicting comment in `web-agents.ts` removed ("No
  allowedTools listed here" sat above `allowedTools: [...]`).
* `web-search` SKILL.md: Safety section moved ahead of Output format so
  the model attends to it earlier in the prompt.
* Doc examples in `create-market-monitor-agent.mdx` rephrased to be
  evergreen ("this quarter", "in our industry") instead of bound to
  specific 2026 events.
@clemenshelm clemenshelm force-pushed the feat/skill-layer-foundation branch from 2ef0662 to 06d916d Compare June 19, 2026 07:31
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.

RFC: formal Template Contract — schema + tools + permissions in one declaration

1 participant