# Plugin development lifecycle

**Maintainer notes.** The workflow a change to `plugin/` follows, from the first edit to
reaching a connector builder.

## 1. Make the change

Decide placement with [plugin-layout.md](plugin-layout.md), then bump the plugin version in
the same commit as the change itself.

| File | Version of | Bumped by |
|---|---|---|
| `plugin/.claude-plugin/plugin.json` | The plugin. **Authoritative** | You |
| `plugin/package.json` | Nothing else. Keep it equal to `plugin.json` | You |
| `.claude-plugin/marketplace.json` | The marketplace listing | You, keep it equal |
| `package.json` (repo root) | The `@stackone/agent-harness` npm package, which is a different number | release-please, from the commit types on main |

## 2. Verify locally

From the repo root:

```bash
pnpm lint:fix
pnpm lint
pnpm typecheck
pnpm test
```

That covers everything already under `test/`. What it cannot cover depends on what you
touched:

| If you changed | Do this as well |
|---|---|
| A gate check | Add a spec that calls your check twice, once with a connector that breaks the rule and once with one that satisfies it. `test/gates/health-checks.spec.ts` is the pattern: import `checks` from the gate script, find yours by id, build the input inline |
| Prose inside a `SKILL.md` | Nothing here can see it. Use the benchmark in stage 4 |
| A skill's name, or added a skill | Register it in `skills[]` in `plugin/.claude-plugin/plugin.json`, or the loader will not find it |
| Anything under `skills/`, `commands/`, `agents/`, `interactive/` or `connector-guide.md` | Install your checkout into a scratch project and look at what landed: `stackone agent sync --plugin-dir <this checkout>/plugin` |

Write the failing assertion first. A check only ever seen to pass is indistinguishable
from one that never fires.

## 3. Open the PR

Open a PR as per usual repo guidelines and rules.

## 4. Run the benchmark

Skill content is prose the model acts on, so its regressions never appear in unit tests.
Two PR labels drive the [build benchmark](../../../docs/02-benchmark-pr-check.md), which is
also where the tolerances and current costs live:

| Label | Runs | Use it |
|---|---|---|
| `run-benchmark-lite` | A curated subset | While iterating, to catch obvious breakage cheaply. Scores are not promoted |
| `run-benchmark` | Every prompt | Run before merging. This is the gate |

On a same-repo PR the full run commits its scores to the PR branch, so merging promotes them
and they become the baseline the next PR is measured against. That commit step does not run
for a fork PR, because the bot cannot push to the fork, so after merging one you have to run
the Benchmark Baseline workflow by hand to refresh main. Details in
[02-benchmark-pr-check.md](../../../docs/02-benchmark-pr-check.md).

Script and gate changes usually need neither label, since `test/gates/` already covers
their behaviour. Reach for the benchmark when you have changed what the model reads.

## 5. Review

For the reviewer, in the order things get missed:

- Does a gate change have a test that fails in the failing direction, or only one that
  passes?
- Is the version bumped, and equal across the three plugin files?
- If skill prose moved, is there a green full `run-benchmark`, not just a lite run?
- Does the placement follow [plugin-layout.md](plugin-layout.md)?

Skill changes also arrive without anyone starting them, from the daily Falcon Knowledge
Sync workflow. Those PRs need four further checks, listed in
[docs/03-falcon-knowledge-sync.md](../../../docs/03-falcon-knowledge-sync.md).

## 6. Merge

Merging is not delivery. See [@stackone/cli](https://github.com/StackOneHQ/cli) for how
agent-harness releases are deployed.
