Skip to content

refactor: single-source skill tree for all agent plugins - #7

Merged
prathamesh-sonpatki merged 12 commits into
fde-311-opencode-plugin-for-last9-hosted-mcp-serverfrom
repo-as-hub-skills
Aug 24, 2026
Merged

refactor: single-source skill tree for all agent plugins#7
prathamesh-sonpatki merged 12 commits into
fde-311-opencode-plugin-for-last9-hosted-mcp-serverfrom
repo-as-hub-skills

Conversation

@prathamesh-sonpatki

@prathamesh-sonpatki prathamesh-sonpatki commented Aug 24, 2026

Copy link
Copy Markdown
Member

Summary

Every skill now lives exactly once in git — with one documented exception for Grok. Marketplace manifests reference the canonical skills/ tree directly (Claude Code and Codex via root-source entries), and the OpenCode plugin assembles the tree into its tarball at pack time. The sync script, both committed mirrors, and the drift-failure class they produced are gone.

Stacked on #6 (which introduced opencode-last9); retarget to master after that merges.

Session-settled decisions carried from planning: repo-as-hub over auto-sync bot and npm artifact (user-directed); skills bundled on every install path (user-directed); automation-maintained fallback copies only where manifests cannot reference the canonical tree (user-approved — this PR exercises that clause once, for Grok).

Why not keep the mirrors

The old model failed twice in one day: a legitimate edit failed parity until re-synced, and a packaged copy of an uncommitted skill passed local checks but died in CI as an orphaned mirror. Ceremony also grew linearly per plugin format, while the strategy doc positions this repo as a growing multi-runtime skills layer.

Design decisions

  • Claude/Grok share one root-source entry — documented marketplace pattern (source: "./" + explicit skills paths).
  • Codex verified live before committing to the branch — parent-relative paths are out of spec for it, but a marketplace-local source.path: "./" makes the repo root itself the plugin; an in-session probe confirmed all four skills discovered (last9:*). Root-level .claude-plugin/plugin.json / .codex-plugin/plugin.json make the root a fully-formed plugin rather than relying on undocumented defaults.
  • Grok required the plan's sanctioned fallback — live testing showed its marketplace loader drops any entry whose source resolves to the marketplace root (./, ., omitted) and rejects symlinked skill dirs; only real files in a real subdirectory install. plugins/last9/skills returns as the single sanctioned mirror: byte-parity enforced by the CI gate in both directions, auto-refreshed by a master-only workflow step so contributors still touch only skills/.
  • OpenCode assembles at pack time — a prepack script enumerates skills via git ls-files, so only committed canonical content can reach npm; generated skills/ is gitignored.
  • CI checks outputs, not duplicates — the required check keeps its name (renaming would deadlock branch protection) but now validates frontmatter naming, declared manifest paths, tarball completeness, forbids unsanctioned committed copies, and asserts mirror parity; negative selftests cover every fail-closed branch.

Test plan

  • 11/11 unit tests in plugins/opencode-last9; tsc --noEmit clean.
  • scripts/check-skill-pack.sh passes on this tree and fails correctly on seeded faults (name mismatch, mirror drift verified in scratch repos); selftests cover all gate branches.
  • Clean-room install matrix:
    • Claude Code: teardown → fresh install → cache carries all 4 skills at v0.3.0 ✅
    • Codex: teardown → fresh install → clean-state probe sees all last9:* skills ✅
    • OpenCode: tarball listing complete post-assembly ✅
    • Grok Build: fresh install delivered all 4 skills to ~/.grok/installed-plugins/ ✅ — final in-session probe blocked by free-tier quota; re-run when quota resets
  • Review: 4-persona code review, 8 findings → fixes applied across 1b2e1d7, 1b61d39, 4ec02ab, 8d5d322, 2bc9b5f; receipt at /tmp/compound-engineering-503/ce-code-review/20260824-123136-32f2d084/review.json.

Post-Deploy Monitoring & Validation

  • Watch the "Plugin skill sync parity" check on this PR and the next few — new failure modes name the exact fault.
  • After merge: next skill addition should land as a one-directory PR; the Grok mirror refresh bot commits automatically on master.
  • On the next last9 release-please run: confirm version bumps land in root .claude-plugin/plugin.json / .codex-plugin/plugin.json (paths changed).
  • Spot-check one fresh install per platform after the next release; rollback = revert this PR.

Related to FDE-311 (the plugin this stacks on).


Compound Engineering

Root-source plugin entry with explicit skills path replaces the
plugins/last9 directory copy; Grok-specific root manifest removed so
its documented Claude-manifest fallback serves it.
source.path moves from ./plugins/last9 to ./; Codex discovers the
root-level skills/ conventionally (verified live: all four skills
visible in-session), so no committed mirror is needed.
prepack regenerates skills/ via git ls-files (tracked canonical only,
so uncommitted experiments never leak into the tarball); the committed
mirror is deleted and gitignored. Bundled-skills tests now exercise
both present and absent states.
The old job verified committed plugin copies; there are none now. The
replacement validates the hub model directly: frontmatter name equals
directory name, declared marketplace skills paths exist, and the
opencode tarball ships every tracked canonical skill. Job name kept
identical — it is a branch-protection required check.
Adding a skill is a single commit under skills/<name>/; manifests
reference the canonical tree directly and the OpenCode plugin assembles
it at pack time. Sync-script instructions removed everywhere.
…ned ones

- check-skill-pack.sh fails closed on malformed manifests and bounds
  name extraction to the frontmatter block (adversarial + testing
  findings); negative selftests cover every fail-closed branch
- root-level plugin.json manifests for Claude Code and Codex make the
  repo root a fully-formed plugin under source:"./" entries
- orphaned plugins/last9 manifest dirs removed; release-please
  extra-files repointed at the root manifests
git ls-files glob cannot match nested paths today; the guard keeps the
copy loop correct if that ever widens (adversarial finding #9).
fb5704a intended this deletion but a prepack regeneration between
staging and committing resurrected identical files, silently unstaging
it. Committed mirrors are forbidden under the hub model.
Pins the invariant whose violation slipped through fb5704a: a pack-time
regeneration between staging and committing resurrected identical
mirror files and unstaged their deletion unnoticed.
Live testing showed Grok Build's marketplace loader drops entries whose
source resolves to the marketplace root (./, ., omitted all invisible
to catalog discovery) and requires real skill files in a real subdir;
symlinked skills dirs are also rejected. R5's per-format fallback fires:
plugins/last9/skills returns as the single sanctioned mirror.

Automation keeps it honest: the gate asserts byte-parity with canonical
skills/ (both directions), and a master-only workflow step refreshes the
mirror so contributors still touch only skills/.

Selftests extended: mirror-drift case added; fixtures carry the mirror.
- fail closed on missing consumer manifests; require >=1 plugin entry
- type-safe skills assertions (string-typed values false-passed -d checks)
- declared paths must contain SKILL.md, not just exist as directories
- tarball completeness via tar -tzf exact-set match, both directions
  (catches extras, not just omissions); npm failures fail fast
- assemble-skills copies all tracked files under skills/<name>/ (not
  SKILL.md alone) and asserts assembled count == tracked count
- Grok mirror parity skips on contributor PRs (SKIP_MIRROR_PARITY) and
  enforces at full strength on master after the auto-refresh bot runs;
  refresh regenerates from scratch so orphans cannot survive
- selftests cover string-skills, deleted manifest, codex pointer drift,
  and the parity skip
The workflow exports the skip for the gate; the env leaked into the
selftest, neutering its drift-detection case. Each selftest invocation
now pins its own environment via env -u.
@prathamesh-sonpatki
prathamesh-sonpatki merged commit 267b8a4 into fde-311-opencode-plugin-for-last9-hosted-mcp-server Aug 24, 2026
5 checks passed
prathamesh-sonpatki added a commit that referenced this pull request Aug 24, 2026
…skills (#6)

* feat(opencode): add opencode-last9 plugin for hosted MCP and bundled skills

New npm package `opencode-last9` connects OpenCode to the hosted Last9
MCP server:

- config hook injects the org-scoped remote endpoint
  (https://app.last9.io/api/v4/organizations/<org-slug>/mcp); OpenCode
  handles OAuth automatically (RFC 7591 dynamic client registration)
- org slug resolves from plugin options or LAST9_ORG_SLUG; explicit url
  / LAST9_MCP_URL override for self-hosted environments; a user-defined
  last9 MCP entry is never clobbered; nothing configured = no injection
- bundles the canonical skills via config.skills.paths so investigation
  skills ship with the tools

Repo wiring:
- sync-agent-plugin-skills.sh mirrors skills into both packaged plugins;
  CI parity check extended to plugins/opencode-last9/skills
- release-please config + manifest track the new package
- README + CONTRIBUTING document the OpenCode install path

The plugin entrypoint exports functions only — opencode's loader rejects
modules with non-function exports.

Refs FDE-311.

* fix(ci): drop orphaned packaged skill, assemble test URLs via constant

- Remove plugins/opencode-last9/skills/sidekiq-debugging: its canonical
  source is not yet committed, so the sync-parity check deleted it as an
  orphaned copy on CI. It returns when that skill lands with both mirrors.
- Assemble expected URLs in tests from a base constant; the structural
  reference scan rejects literal organizations/<slug>/mcp shapes even for
  fake slugs.

Refs FDE-311.

* refactor: single-source skill tree for all agent plugins (#7)

* refactor(skills): point Claude marketplace at canonical root tree

Root-source plugin entry with explicit skills path replaces the
plugins/last9 directory copy; Grok-specific root manifest removed so
its documented Claude-manifest fallback serves it.

* refactor(skills): point Codex marketplace at canonical root tree

source.path moves from ./plugins/last9 to ./; Codex discovers the
root-level skills/ conventionally (verified live: all four skills
visible in-session), so no committed mirror is needed.

* refactor(opencode): assemble skills at pack time from canonical tree

prepack regenerates skills/ via git ls-files (tracked canonical only,
so uncommitted experiments never leak into the tarball); the committed
mirror is deleted and gitignored. Bundled-skills tests now exercise
both present and absent states.

* refactor(ci): replace sync-parity with skill distribution checks

The old job verified committed plugin copies; there are none now. The
replacement validates the hub model directly: frontmatter name equals
directory name, declared marketplace skills paths exist, and the
opencode tarball ships every tracked canonical skill. Job name kept
identical — it is a branch-protection required check.

* docs: describe repo-as-hub skill distribution

Adding a skill is a single commit under skills/<name>/; manifests
reference the canonical tree directly and the OpenCode plugin assembles
it at pack time. Sync-script instructions removed everywhere.

* fix(review): harden skill gate, add root plugin manifests, drop orphaned ones

- check-skill-pack.sh fails closed on malformed manifests and bounds
  name extraction to the frontmatter block (adversarial + testing
  findings); negative selftests cover every fail-closed branch
- root-level plugin.json manifests for Claude Code and Codex make the
  repo root a fully-formed plugin under source:"./" entries
- orphaned plugins/last9 manifest dirs removed; release-please
  extra-files repointed at the root manifests

* fix(review): reject unexpected path depth in skill assembly

git ls-files glob cannot match nested paths today; the guard keeps the
copy loop correct if that ever widens (adversarial finding #9).

* chore: drop committed opencode plugin skill mirror

fb5704a intended this deletion but a prepack regeneration between
staging and committing resurrected identical files, silently unstaging
it. Committed mirrors are forbidden under the hub model.

* fix(review): forbid committed plugin skill copies in the gate

Pins the invariant whose violation slipped through fb5704a: a pack-time
regeneration between staging and committing resurrected identical
mirror files and unstaged their deletion unnoticed.

* fix(grok): sanctioned fallback mirror — Grok cannot follow root sources

Live testing showed Grok Build's marketplace loader drops entries whose
source resolves to the marketplace root (./, ., omitted all invisible
to catalog discovery) and requires real skill files in a real subdir;
symlinked skills dirs are also rejected. R5's per-format fallback fires:
plugins/last9/skills returns as the single sanctioned mirror.

Automation keeps it honest: the gate asserts byte-parity with canonical
skills/ (both directions), and a master-only workflow step refreshes the
mirror so contributors still touch only skills/.

Selftests extended: mirror-drift case added; fixtures carry the mirror.

* fix(review): harden gate per cross-harness Cursor review

- fail closed on missing consumer manifests; require >=1 plugin entry
- type-safe skills assertions (string-typed values false-passed -d checks)
- declared paths must contain SKILL.md, not just exist as directories
- tarball completeness via tar -tzf exact-set match, both directions
  (catches extras, not just omissions); npm failures fail fast
- assemble-skills copies all tracked files under skills/<name>/ (not
  SKILL.md alone) and asserts assembled count == tracked count
- Grok mirror parity skips on contributor PRs (SKIP_MIRROR_PARITY) and
  enforces at full strength on master after the auto-refresh bot runs;
  refresh regenerates from scratch so orphans cannot survive
- selftests cover string-skills, deleted manifest, codex pointer drift,
  and the parity skip

* fix(ci): pin selftest env — CI exports SKIP_MIRROR_PARITY on PRs

The workflow exports the skip for the gate; the env leaked into the
selftest, neutering its drift-detection case. Each selftest invocation
now pins its own environment via env -u.
@prathamesh-sonpatki
prathamesh-sonpatki deleted the repo-as-hub-skills branch August 24, 2026 17:17
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.

1 participant