From b6353621716b53e36e5b871948c559122344f8ee Mon Sep 17 00:00:00 2001 From: haylan Date: Wed, 14 Oct 2026 11:36:56 +0200 Subject: [PATCH] docs: phase 0 analysis - architecture, plugin api requirements, sync and release design --- .opencode/state/decisions.md | 57 +++++++++ .opencode/state/status.md | 40 +++++++ .opencode/state/upstream.json | 8 ++ AGENTS.md | 58 +++++++++ TODO.md | 60 ++++++++++ docs/architecture.md | 220 ++++++++++++++++++++++++++++++++++ 6 files changed, 443 insertions(+) create mode 100644 .opencode/state/decisions.md create mode 100644 .opencode/state/status.md create mode 100644 .opencode/state/upstream.json create mode 100644 AGENTS.md create mode 100644 TODO.md create mode 100644 docs/architecture.md diff --git a/.opencode/state/decisions.md b/.opencode/state/decisions.md new file mode 100644 index 0000000..8ee6890 --- /dev/null +++ b/.opencode/state/decisions.md @@ -0,0 +1,57 @@ +# Decision log + +Binding decisions. Re-litigate only with new evidence; append, never rewrite history. + +## D1 — 2026-10-14 — Skills ship verbatim, agents carry the OpenCode wiring + +Upstream SKILL.md files are served as faithfully as possible (OpenCode v2 natively honors `name`, +`description`, `disable-model-invocation`, supporting files via path resolution). OpenCode-specific +behavior (worktrees, subagent dispatch, permission enforcement, phase gating) lives in generated +**agent** files, not in patched skill text. This matches the proven `mcdays94/pocock-agents` design +and the mission's "upstream = methodology truth" principle. + +## D2 — 2026-10-14 — Distribution: plugin injects skills; `init` bin copies agents + +V2 `SkillEditor` supports `add()`; `AgentEditor` does **not** (verified against +`anomalyco/opencode@dev` docs/source 2026-10-14). Therefore: + +- `setup(ctx)` injects every vendored skill via `ctx.skill.transform` reading bundled files + (zero-config once the package is in `plugins`). +- `bin/opencode-pocock init [-g]` copies generated `agents/*.md` into `.opencode/agents/` + (or `~/.config/opencode/agents/`). Plugin setup warns (one line) when the agents are missing or + their stamped version != package version. No silent writes to user repos. + +## D3 — 2026-10-14 — Release flow copied from fastcrw-opencode-websearch + +CHANGELOG.md `## [x.y.z]` heading = release source of truth. `scripts/release.ts` publishes to the +Gitea npm registry via HTTP API (packument PUT + base64 tarball, no `npm publish`, no `.npmrc`), +ensures tag `v`, links package to repo. Idempotent. Credentials only from +`secrets.PACKAGE_TOKEN`. package.json keeps `0.0.0-dev`; version is stamped in the staged copy only. + +## D4 — 2026-10-14 — Nightly sync stops on no-change, minor-bumps on upstream change + +Nightly workflow: sync → if upstream commit unchanged, exit 0 immediately. On change: update +snapshot, regenerate, format/typecheck/test/build, auto-prepend changelog entry with new minor +version + upstream short-SHA, release, commit generated changes back to main. Major bumps are never +automatic. Generated commit-back can't retrigger nightly (schedule/dispatch only). + +## D5 — 2026-10-14 — Agent hierarchy stays minimal: orchestrator + worker (+ candidates) + +No "giant Matt Pocock agent" and no 12-agent hierarchy for its own sake. Matt's phases +(grill → spec → tickets → build → review → retro) run in one primary conversation; only the build +phase fans out. Core agents: `pocock` (primary orchestrator), `pocock-worker` (subagent, worktree- +isolated, TDD). Candidates evaluated in Phase 2: `pocock-reviewer` (read-only review fanout — +likely yes), `pocock-researcher` (likely reject: built-in explore/general suffice). Every extra +agent must prove value against the classification matrix or be documented as rejected. + +## D6 — 2026-10-14 — in-progress and deprecated upstream skills are excluded + +`skills/in-progress/*` and `skills/deprecated/*` are not vendored into the package (unstable, +upstream itself gates them). `claude-handoff` is Claude-specific → not-convertible. Exclusions are +recorded in `.opencode/state/classification.json` so re-inclusion is a data change, not a redesign. + +## D7 — 2026-10-14 — git-guardrails-claude-code becomes a real plugin feature + +The only skill needing executable integration: dangerous-git blocking. Implemented as an opt-in +plugin hook (`ctx.tool.hook("execute.before")` on shell, options `{ guardrails: true }`, default +off) inside our plugin; the skill itself stays vendored verbatim for reference. diff --git a/.opencode/state/status.md b/.opencode/state/status.md new file mode 100644 index 0000000..6858394 --- /dev/null +++ b/.opencode/state/status.md @@ -0,0 +1,40 @@ +# Project status + +Updated: 2026-10-14 (phase 0 — analysis) + +## Current phase + +Phase 0 (analysis) — nearly complete. Phase 1 (plugin package) not started. + +## Done + +- Repo bootstrapped (was empty; Gitea remote `haylan/mattpocock-skills-opencode` configured, no commits yet). +- Studied reference repos (cloned to temp, not vendored): + - `fastcrw-opencode-websearch` — V2 plugin shape, Gitea release script/workflows, test style + (`node --test` on .ts), prettier/tsconfig conventions. + - `mcdays94/pocock-agents` — proven orchestrator/worker agent pair (V1-era, tracks upstream v1.3 + `d81f3a1`), drift-check script, Docker smoke test. + - `mattpocock/skills` @ `49dd158` (2026-10-09, v1.3.1) — full inventory: engineering 20, + productivity 7, misc 4, in-progress 7 (excluded), deprecated 0. +- Verified OpenCode V2 API surface against docs + `anomalyco/opencode@dev` source (see AGENTS.md + "Key V2 facts"). +- Wrote: AGENTS.md, state files, TODO.md, docs/architecture.md, docs/skill-mapping.md. + +## Known gaps / risks + +- Unverified at runtime: exact `Skill.Info` field set accepted by `editor.add` (docs show + id/name/description/path/content + autoinvoke; confirm against installed `@opencode/plugin` + typings in Phase 1). Same for `permissions` array frontmatter in agent .md — verify with a real + `opencode serve` smoke. +- Background subagents: V1 needed `OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true`; V2 status + unknown — orchestrator must work in batch mode either way. +- `request.body` overlays are accepted but not sent by the V2 runner yet — don't rely on + temperature tuning. +- Nightly commit-back to main needs a Gitea token with repo write scope (same `PACKAGE_TOKEN` + pattern as fastcrw, which used it for tags + package link). + +## Next steps (exact) + +1. Finish docs/skill-mapping.md classification matrix (subagent analysis of every SKILL.md). +2. Commit phase-0 docs: `docs: phase 0 analysis and migration plan`. +3. Start TODO.md Phase 1.1: scaffold package (package.json, tsconfig, prettier, src/index.ts stub). diff --git a/.opencode/state/upstream.json b/.opencode/state/upstream.json new file mode 100644 index 0000000..b80b6cf --- /dev/null +++ b/.opencode/state/upstream.json @@ -0,0 +1,8 @@ +{ + "repo": "https://github.com/mattpocock/skills", + "commit": "49dd158d1076134a641b33efb035946536778336", + "commit_date": "2026-10-09T11:56:49+01:00", + "upstream_version": "1.3.1", + "synced_at": null, + "note": "HEAD observed during phase-0 analysis; upstream/ snapshot not vendored yet" +} diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..be94679 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,58 @@ +# mattpocock-skills-opencode + +OpenCode v2 integration for the [Matt Pocock skills](https://github.com/mattpocock/skills) ecosystem. +Upstream is the source of truth for the methodology; OpenCode v2 is the source of truth for execution +architecture. We translate, never copy Claude Code's plugin architecture. + +## Session start protocol (long-running project) + +1. `git status` + current branch — never destroy uncommitted work. +2. Read `TODO.md` — the persistent task list. +3. Read `.opencode/state/status.md` — current phase and last completed work. +4. Read `.opencode/state/decisions.md` — binding decisions; don't re-litigate them without new evidence. +5. Read `.opencode/state/upstream.json` — last synchronized upstream revision. +6. Pick the highest-value unfinished task from `TODO.md` and continue. + +Before context runs low: update `status.md` + `TODO.md`, record failures and exact next steps, commit +safe intermediate work. + +## Layout + +- `upstream/` — pristine vendored snapshot of mattpocock/skills (`skills/` subtree). **Never hand-edit.** + Updated only by `scripts/sync-upstream.ts`. +- `skills/` — generated OpenCode skill tree + `manifest.json` (output of `scripts/generate.ts`). Hand-edit + only the overlay inputs under `overlay/`, never the output. +- `agents/` — generated OpenCode v2 agent markdown files. Edit templates in `agent-templates/`, then regenerate. +- `.opencode/state/` — machine- and human-readable project state (upstream revision, classification, status). +- `docs/architecture.md` — the design. `docs/skill-mapping.md` — per-skill classification record (generated + from `.opencode/state/classification.json`). +- `scripts/` — sync, generate, release (Gitea npm registry publish without `npm publish`), smoke. +- `test/` — `node --test` on TypeScript sources directly (Node ≥ 22.18 type stripping, like the reference repo). + +## Conventions + +- TypeScript ESM, `type: module`, prettier (`semi: false`, `printWidth: 100`), `tsc --noEmit` gate. +- Generated agent frontmatter uses **V2-native fields only**: `mode`, `description`, `model` + (`provider/model#variant`), `permissions` (array of `{action, resource, effect}`), `steps`, `hidden`, + `request` (sparingly — body overlays are not sent by the runner yet). Never `prompt`, `permission`, + `tools`, `disable`, `maxSteps`, `temperature` at top level. +- Commits: `sync:`, `feat:`, `fix:`, `test:`, `chore:`, `docs:` — logically separated, conventional style. +- Versioning: semver; `CHANGELOG.md` `## [x.y.z]` headings are the release source of truth + (fastcrw pattern). Upstream revision tracked separately in state + changelog entries. +- Registry credentials only from Gitea Action secrets (`PACKAGE_TOKEN`). Never commit them. + +## Key V2 facts this repo relies on (verified 2026-10-14 against opencode docs/source) + +- Plugins: `Plugin.define({ id, setup(ctx) })` from `@opencode/plugin`; config key `plugins`; + npm package needs `type: module` + `exports["."]`. +- `ctx.skill.transform(editor)` **can add** skills programmatically (id, name, description, path, + content, autoinvoke, slash). `ctx.agent.transform(editor)` **cannot add** agents — only + list/get/update/remove/default. Hence: skills ship via plugin injection, agents via `init` copy. +- Skills frontmatter honored: `name`, `description`, `slash`, `disable-model-invocation` / + `metadata.opencode/autoinvoke` (hidden from model, still loadable by id/slash). +- Agent files: `.opencode/agents/.md` (project), `~/.config/opencode/agents/` (global); + frontmatter = agent config, body = system prompt. +- Permission rules are ordered, last match wins; per-agent skill restrictions via + `{action: "skill", resource: "", effect: ...}`. Shell action is `shell` in V2. +- Agent `steps` replaces `maxSteps`; `temperature`/`top_p` go under `request.body` (not yet sent by + the runner — avoid). diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..e0c726f --- /dev/null +++ b/TODO.md @@ -0,0 +1,60 @@ +# TODO + +Persistent task list. Check off + note outcome; move done items to the bottom log with date. +State: `.opencode/state/status.md`. Design: `docs/architecture.md`. + +## Phase 0 — analysis + +- [x] Inspect repo, fastcrw reference, pocock-agents, upstream skills, OpenCode v2 plugin + agent docs +- [x] Architecture (docs/architecture.md §1) +- [x] Plugin API requirements (§3) +- [x] Agent architecture (§4) +- [x] Sync strategy (§5) +- [x] Gitea Actions design (§6) +- [x] Testing strategy (§7) +- [x] Phased plan (§9) +- [ ] Skill inventory + classification matrix → docs/skill-mapping.md (subagent analysis in flight) +- [ ] Commit phase-0 docs (`docs: phase 0 analysis and migration plan`) + +## Phase 1 — plugin package + +- [ ] 1.1 scaffold package (package.json name @haylan/opencode-pocock — CONFIRM scope with repo + owner, tsconfig, prettier, .gitignore, MIT LICENSE + upstream attribution, README stub) +- [ ] 1.2 scripts/sync-upstream.ts + vendor upstream/ @49dd158 + state update +- [ ] 1.3 scripts/generate.ts: manifest.json + skills/ output + unit tests (parser, manifest) +- [ ] 1.4 src/index.ts plugin: skill injection, options validation, agent-staleness warning + + fake-ctx integration tests (verify Skill.Info fields against installed @opencode/plugin typings!) +- [ ] 1.5 bin init (copy agents, -g global, no silent overwrite) + tests +- [ ] 1.6 agent-templates/: port pocock + pocock-worker from pocock-agents to V2-native frontmatter + (permissions array, shell action, no legacy keys); generate agents/*.md; drift tests +- [ ] 1.7 scripts/release.ts + version.ts adapted from fastcrw + unit tests +- [ ] 1.8 .gitea/workflows/: check.yml, nightly.yml, smoke (dispatch); smoke.ts against opencode serve +- [ ] 1.9 local runtime verification (serve + HTTP API: plugin ok, skills visible, autoinvoke flags, + agents after init, worker denies hold) → docs/runtime-checks.md +- [ ] 1.10 manual release 0.1.0 to Gitea registry; verify install-from-registry by package name +- [ ] ask repo admin to add Gitea secret PACKAGE_TOKEN (write:package + write:repository) + +## Phase 2 — conversion + +- [ ] 2.1 deep per-skill analysis → .opencode/state/classification.json + docs/skill-mapping.md + (classification.json becomes generator input; mapping doc generated from it) +- [ ] 2.2 guardrails plugin hook (opt-in, tool.hook execute.before on shell) + tests +- [ ] 2.3 pocock-reviewer: implement or document rejection in decisions.md +- [ ] 2.4 orchestrator V2 flow wiring (background-subagent status in V2? batch fallback; worktree + permission guidance; slash UX for user-invoked skills) +- [ ] 2.5 per-skill regression checks + manual orchestration smoke (token-spending, documented) +- [ ] 2.6 README usage guide + finalize skill-mapping +- [ ] 2.7 one week of green nightlies → declare stable + +## Open questions + +- Exact `Skill.Info`/editor.add field set — check `@opencode/plugin` typings at 1.4. +- V2 background subagents: still env-gated? Affects orchestrator dispatch loop wording (1.6). +- Does `slash` need explicit `true` in editor.add to expose `/name` commands? (1.4 runtime check.) +- Package name/scope confirmation (1.1). +- Does nightly runner have network access to github.com for upstream fetch? (1.8 — fallback: mirror + upstream to Gitea and sync from the mirror.) + +## Done log + +- 2026-10-14: phase-0 analysis docs written (AGENTS.md, state/, docs/architecture.md, TODO.md). diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..412f407 --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,220 @@ +# Architecture — Matt Pocock Skills → OpenCode v2 + +Phase-0 analysis, 2026-10-14. Sources: `mattpocock/skills@49dd158` (v1.3.1), +`fastcrw-opencode-websearch` (working V2 plugin + Gitea release pipeline), `mcdays94/pocock-agents` +(proven V1 orchestrator/worker pair, tracks upstream v1.3), OpenCode v2 docs + `anomalyco/opencode@dev` +source (verified via Context7). Per-skill classification: `docs/skill-mapping.md`. + +## 1. Proposed architecture + +One npm package, three delivery channels, one generator pipeline: + +``` +mattpocock/skills (upstream, git) + │ scripts/sync-upstream.ts (nightly) + ▼ +upstream/ pristine snapshot (skills/ subtree + UPSTREAM.json) — never hand-edited + │ scripts/generate.ts (inputs: upstream/ + .opencode/state/classification.json + agent-templates/ + overlay/) + ▼ +skills/ + manifest.json OpenCode skill tree (verbatim content, stripped of other-harness files) +agents/*.md V2-native agent files (orchestrator, worker, ...) + │ + ▼ +package @haylan/opencode-pocock (name provisional) + ├── dist/index.js plugin: injects skills via ctx.skill.transform; optional guardrails hook; + │ warns when agents are missing/stale + ├── bin init copies agents/*.md → .opencode/agents/ (or ~/.config/opencode/agents/ with -g) + └── files: dist, skills, agents, manifest +``` + +Why this split (decision D2): V2 `SkillEditor.add()` exists, `AgentEditor.add()` does not. Skills can +be injected at runtime by the plugin with zero user file management; agents must exist as markdown +files in a discovered directory, so a tiny `init` copy step is unavoidable. The plugin detects +missing/version-mismatched agents and prints the one-line fix instead of failing or silently +writing into the user's repo. + +User-facing install: + +```jsonc +// opencode.json +{ "plugins": ["@haylan/opencode-pocock"] } +``` + +```bash +npx @haylan/opencode-pocock init # once per project (or -g for global agents) +``` + +Fidelity rule (D1): skill text ships verbatim. V2 natively honors `disable-model-invocation` +(user-invoked skills stay hidden from auto-selection but loadable via `/name` or by id), skill +`name`/`description`, and supporting-file paths relative to SKILL.md (we keep each skill's whole +directory, so `DEEPENING.md`, `template.sh`, etc. resolve). OpenCode-specific wiring lives only in +generated agents. If a skill ever needs a textual deviation, it goes through a tracked `overlay/` +patch input — never a hand-edit of `upstream/` or `skills/`. + +Generator scope (deliberately small): + +- strip non-OpenCode harness files (`agents/openai.yaml`, Claude plugin metadata) from the output tree +- parse each SKILL.md frontmatter → `manifest.json` entry: `{ id, name, description, autoinvoke, + slash, path, files[] }`; `autoinvoke=false` when upstream sets `disable-model-invocation: true` +- render `agents/*.md` from templates + `classification.json` (skill allow/deny lists, version stamp) +- regenerate `docs/skill-mapping.md` from `classification.json` (docs never drift from data) + +## 2. Skill inventory (summary) + +38 active skills at `49dd158`: engineering 20, productivity 7, misc 4, in-progress 7 (excluded per +D6), deprecated 0. Full table with classification + rationale: `docs/skill-mapping.md`. + +## 3. Plugin API requirements + +Verified against V2 (`@opencode/plugin` ^2.x, `Plugin.define`): + +| Requirement | V2 mechanism | +| --- | --- | +| Stable identity | `Plugin.define({ id: "haylan.pocock", setup })` | +| Inject all skills | `await ctx.skill.transform(editor => editor.add({ id, name, description, path, content, autoinvoke, slash }))` per manifest entry; `path` = absolute SKILL.md location inside the installed package so supporting-file references resolve | +| Options | `ctx.options` — validate and fail loudly (fastcrw pattern) | +| Dangerous-git guardrails (D7) | `await ctx.tool.hook("execute.before", ...)` on shell tool; opt-in `{ guardrails: true }` | +| Agent staleness hint | `await ctx.agent.list()` in setup; one-line console warning if `pocock`/`pocock-worker` missing or stamped version ≠ package version | +| Packaging | `type: module`, `exports["."]`, dependency `@opencode/plugin`; installed via bun by OpenCode at startup | + +Not needed: custom tools, MCP, websearch, auth. Keep the plugin surface minimal. + +## 4. Agent architecture + +Hierarchy (D5) — two core agents, one likely addition, rest rejected unless the matrix proves value: + +| Agent | Mode | Role | Key V2 config | +| --- | --- | --- | --- | +| `pocock` | primary | Orchestrator: routes via `ask-matt`, walks grill→spec→tickets→build→review→retro, gates user-invoked skills behind explicit proposals, runs the `implement-spec` dispatch loop (integration branch + one worktree per ticket + frontier scheduling from its own merge record) | `permissions`: edit/shell allow; shell deny for destructive git on the main checkout; `skill` allow `*`; task allow | +| `pocock-worker` | subagent | Builds one ticket test-first inside its prepared worktree, merges integration tip into its branch, reports; never pushes/stashes/worktrees/PRs; asks nothing | `permissions`: skill deny `*` + allow-list (tdd, codebase-design, diagnosing-bugs, stack skills); shell deny push/stash/reset --hard/clean/worktree/switch, `gh pr`/`gh issue close`; edit allow | +| `pocock-reviewer` (candidate) | subagent | Read-only `code-review` fan-out over the integration branch; findings go back to the orchestrator, fixes run in a fresh worker | `permissions`: edit deny; shell allow git read commands only; skill allow code-review only | +| `pocock-researcher` (likely reject) | subagent | Exploration notes | Built-in `explore`/`general` subagents already do this; a branded one adds drift risk, no capability | + +Design requirements coverage (per mission): purpose/scope/IO contract in each agent's system body; +skills via `action: skill` permission patterns; primary vs subagent via `mode`; spawn rights — only +`pocock` gets `task`; model — left to user default (pocock-agents pinned opus; we ship without a +`model` line and document how to pin, since provider availability varies); context/duration — phases +1–4 stay in one window (~150k guidance from `ask-matt` PHASE-BOUNDARIES), workers get bounded +`steps` only if testing shows runaway loops; git/worktrees — orchestrator-owned `/tmp/pocock-workers` +convention (documented, `external_directory` permission guidance in README); failure modes — worker +reports instead of repairing bad worktrees, orchestrator never merges failed workers; validation — +smoke + drift tests below. + +Explicitly rejected: one giant agent containing all skills; a 12-agent hierarchy mirroring phase +names (phases are conversation steps, not roles — splitting them shreds the shared context the +methodology depends on, e.g. grilling facts feeding `to-spec`). + +## 5. Synchronization strategy + +- `upstream/` is vendored by script only: shallow-clone/fetch `mattpocock/skills`, resolve `HEAD`, + copy the `skills/` subtree, write `upstream/UPSTREAM.json` `{ repo, commit, commit_date, version }` + and mirror it to `.opencode/state/upstream.json`. +- Diff old vs new snapshot → added/removed/changed skill list → written into the changelog entry and + `status.md`; changed skills are matched against `classification.json` to flag affected agents + (regeneration is full but cheap and deterministic; "regenerate only what is necessary" is satisfied + by determinism — unchanged inputs produce byte-identical outputs, so the git diff *is* the + affected-set report). +- New upstream skills appear in the manifest automatically but land unclassified: + `classification.json` defaults them to `skill` + `status: "needs-review"`, and the drift test fails + until a human confirms the classification. Upstream renames/removals fail the drift test when a + generated agent still references the old name (pocock-agents `check-skills.sh` pattern, ported to TS, + run against the vendored snapshot — no network in tests). +- Locally maintained artifacts (`agent-templates/`, `overlay/`, `src/`) are never touched by sync. + +## 6. Gitea Actions design + +`.gitea/workflows/check.yml` — on PR + push to main: `npm ci` → `npm run check` (prettier + tsc) → +`npm test` → `npm run build`. (fastcrw pattern.) + +`.gitea/workflows/nightly.yml` — `schedule: cron` nightly + `workflow_dispatch`, `if: gitea.ref == +'refs/heads/main'`, node 24: + +1. checkout, `npm ci` +2. `node scripts/sync-upstream.ts` — no change → log + exit 0 (success) +3. `node scripts/generate.ts` +4. `npm run format` → `npm run check` → `npm test` (unit + integration + drift/regression) → + `npm run build` — any failure aborts before publish; drift-test failure for unclassified new + skills aborts too (human review required; publish waits for the next green run) +5. `node scripts/version.ts` — prepend `## [] - ` + upstream sync entry to + CHANGELOG.md unless this exact upstream SHA was already released (state check → idempotent) +6. `node scripts/release.ts` — fastcrw release script adapted: publish packument+tarball to + `${SERVER_URL}/api/packages/${owner}/npm` with `Bearer ${PACKAGE_TOKEN}`, skip if version exists, + ensure tag `v` at `gitea.sha`, link package to repo +7. commit-back: `sync: update mattpocock skills to [skip ci]` (upstream/, skills/, + agents/, docs/skill-mapping.md, state, CHANGELOG) pushed to main via token-embedded URL; + job fails loudly if push is rejected (no force-push, ever) + +Secrets: only `secrets.PACKAGE_TOKEN` (write:package + write:repository). Nothing else, nothing +committed. Release/tag = publish step 6; no separate release job. + +Smoke workflow (`workflow_dispatch` only): `scripts/smoke.ts` boots `opencode serve` in a fixture +project whose config points at the local package path, polls `GET /api/plugin` (status ok), +`GET /api/skill` (contains `tdd`, `to-spec` hidden-from-model flag), `GET /api/agent` (contains +`pocock`, `pocock-worker`). No model calls, no tokens spent. Optionally a model-driven acceptance +run (pocock-agents Docker pattern) stays manual-only. + +## 7. Testing strategy + +`node --test` directly on `.ts` (Node ≥ 22.18 type stripping; fastcrw precedent — zero test deps). + +- **Unit**: frontmatter parser (name/description/disable-model-invocation/slash extraction incl. + quoted YAML values); manifest generation (fixture skill tree → expected manifest); classifier + (classification.json schema: every snapshot skill classified exactly once, enum values valid, + in-progress/deprecated excluded); agent generation (frontmatter keys ⊆ V2 allowlist — fails on any + legacy key like `prompt`/`permission`/`maxSteps`; permission arrays well-formed; worker skill + allow-list ∩ user-invoked skills = ∅); version bump calc; release helpers (`latestVersion`, + `publishBody` shasum/integrity, `isPublished`, `ensureTag` with fake fetch — fastcrw's tests + already prove this shape). +- **Integration**: plugin `setup` against a fake ctx (fastcrw's fake-editor pattern) → every manifest + skill added once, ids unique, `path` points at an existing file inside the package, user-invoked + skills get `autoinvoke: false`; bad options throw and register nothing; `init` bin copies agents to + a temp dir, `-g` to a fake home, refuses to overwrite modified files without `--force`; guardrails + hook denies `git push --force`, allows `git status`; drift/regression suite (agents reference only + skills present in the snapshot; every agent-referenced skill is in the manifest). +- **Smoke**: dispatch-only workflow above; representative execution (one worker ticket build) stays + manual/local, documented in `docs/runtime-checks.md` style. + +## 8. Versioning + +Semver; CHANGELOG is the source of truth (D3/D4). Package version in `package.json` stays +`0.0.0-dev`; release stamps the staged copy. Upstream tracking is orthogonal: +`.opencode/state/upstream.json` + changelog entries naming `mattpocock/skills@` (from → +to) + skill add/remove/change list. Upstream content change ⇒ minor bump (automated); generator/ +plugin/agent behavior change ⇒ human-written changelog entry decides the bump; major never automatic. + +## 9. Phased implementation plan + +**Phase 1 — plugin package (quality gate: install → load → skills work → publish → nightly green)** + +1.1 scaffold: package.json (`@haylan/opencode-pocock`, type module, exports, bin, files), tsconfig, +prettier, `.gitignore`, LICENSE (MIT — matches upstream; include upstream NOTICE/attribution), README +1.2 vendor snapshot: sync-upstream.ts + first `upstream/` commit + state files +1.3 generator: manifest + skills tree + tests +1.4 plugin: src/index.ts (skill injection, options validation, agent-staleness warning) + fake-ctx tests +1.5 init bin + tests +1.6 agents v1: hand-port pocock + pocock-worker to V2-native frontmatter as `agent-templates/`, +generate `agents/*.md`, drift tests +1.7 release.ts + version.ts (adapted from fastcrw) + unit tests +1.8 workflows: check.yml, nightly.yml (secrets documented for the repo admin), smoke.ts + dispatch workflow +1.9 local runtime verification: `opencode serve` against a fixture project — skills visible, +`/tdd` loads, agents appear after init, worker permission denies hold (`opencode debug`-style checks +or HTTP API); record results in docs/runtime-checks.md +1.10 first manual release `0.1.0` to Gitea registry; verify `plugins: ["@haylan/opencode-pocock"]` +installs from the registry (the one thing fastcrw left unverified — do it here) + +**Phase 2 — classification-driven conversion (quality gate: every skill classified, conversions +tested, rejections documented, sync doesn't break agents)** + +2.1 per-skill deep analysis (Step 1–3 of the conversion process) → fill `classification.json` + +`docs/skill-mapping.md` rationale/dependencies +2.2 implement `plugin` classification: guardrails hook (D7) + tests +2.3 evaluate `pocock-reviewer`; implement or document rejection +2.4 wire orchestrator flow to V2 realities (background subagents? batch fallback; worktree +permissions guidance; slash-command UX for user-invoked skills) +2.5 regression suite per converted skill; orchestration smoke (manual, token-spending, documented) +2.6 docs: README usage guide, skill-mapping complete, runtime-checks updated +2.7 let nightly run a full week; fix fallout; declare stable + +## 10. Persistent TODO + +Lives in `TODO.md` at the repo root (that file is the checklist; this section is the pointer).