amplifier-opencode-release

$npx mdskill add microsoft/amplifier-app-opencode/amplifier-opencode-release

$ARGUMENTS

SKILL.md

.github/skills/amplifier-opencode-releaseView on GitHub ↗
---
name: amplifier-opencode-release
description: "Release amplifier-app-opencode: bound the commit range, sweep the diff for anything that should not ship, bump both version declarations in agreement, write the changelog, confirm the agent-version floor decision, run the local gate, and open the release PR. Merging to main IS the release; there is no tag or publish step."
disable-model-invocation: true
user-invocable: true
---

# Release amplifier-app-opencode

## Release

$ARGUMENTS

If that is empty, do not ask what to release yet. Run Phase 0 first and
propose a scope from the evidence, then confirm it with the user.

## The Method

A release is a public statement. Everything in the diff becomes permanent,
readable by anyone, and attributed to the project. So the work is ordered:
establish what actually changed, remove what should never have been
committed, then version it, describe it, confirm the upstream floor
decision, verify it, and propose it.

Users install `amplifier-opencode` straight from git (`install.sh`,
`uv tool install --from git+...`, or `amplifier-opencode update`), so
**merging the release PR to main is the release**. There is no second,
later step that makes it real, and no gate after the merge to catch what
was missed before it.

Two rules override everything below.

**This skill never decides the agent-version floor.** `MIN_AGENT_VERSION` in
`src/amplifier_app_opencode/prereqs.py` is a requirement statement owned by
`amplifier-agent`'s own release process. This skill confirms whether that
process delivered a floor change and folds it in if so; it never bumps the
floor on its own initiative just because a newer amplifier-agent exists.

**The release completes when the PR merges.** There is no later step.

Work through the phases in order. Use the todo tool to track them.

---

## Phase 0: Scope

You cannot sweep or version a release you have not bounded. Establish the
range first.

```bash
git fetch origin
git log --oneline origin/main -5
git merge-base origin/main HEAD
```

This repo has no release tags, so the range is whatever landed on `main`
since the previous release commit. If a prior
release PR is identifiable in history (its `chore: cut ... X.Y.Z` commit),
diff from there:

```bash
git log --oneline --grep='^chore: cut amplifier-opencode' -5
git diff <previous-release-commit>..origin/main --stat
```

If nothing since the last release-adjacent commit is otherwise findable,
diff from the last version bump in `pyproject.toml`:

```bash
git log -p -- pyproject.toml | grep -m1 -A2 '^-version'
```

State the scope back to the user before proceeding: which commits, what
changed at a glance, and the version you propose moving to. Get agreement.
A release nobody agreed the shape of is not ready to have its version set.

---

## Phase 1: Sweep

This is the phase that justifies the skill existing. Everything in the
range becomes public. Read the actual diff, not the file list.

```bash
git diff <range-start>..origin/main
```

For a large range, walk it file by file rather than dumping it all at once.

### Files that should not be in the repo

`AGENTS.md` is explicit that design docs are transient working artifacts,
not repo content. Hunt for:

```
plan / PLAN / *-plan.md / implementation-plan.md / phase-*.md
.ai_working/  scratch/  tmp/  WIP*
*.log  *.tmp  *.bak  *.orig  *.rej  .DS_Store
```

If a plan file exists and the user still wants it, it moves outside the
repo, into `.ai_working/` or wherever they keep working notes. It does not
ship.

`docs/ISSUES.md` is durable and must NOT be swept -- it is intentional,
checked-in tracked-gap content, not scratch.

### Comments and code that leak the process

The tell is a comment that only makes sense to someone who watched the work
happen. Grep the diff, then read the hits in context:

```bash
git diff <range-start>..origin/main \
  | grep -nE '^\+' \
  | grep -inE 'TODO|FIXME|XXX|HACK|phase [0-9]|per the plan|as discussed|step [0-9]+ of|for now|temporar|placeholder|remove (this|before)|/home/|/Users/|localhost:[0-9]|ngrok|session[_-]?id'
```

Remove or rewrite:

- References to plan phases or the sequence work was done in
- "For now", "temporary", "will fix later" without an issue behind it
- Commented-out code left as a fallback
- Absolute host paths, personal directories, machine names
- Anything resembling a key, token, or credential. If one is found, stop.
  It needs rotation, not deletion.

### Two invariant checks specific to this repo

**No new stdout writes that would break a scripted caller.**
`onboarding.py`'s secret-redaction path is the highest-risk regression
surface in the whole codebase, because a credential that reaches stdout in
plaintext is exposed the moment it ships and cannot be un-shipped (see
`docs/ISSUES.md`'s risk call-out): any diff touching `_auth_set` or the
onboarding wizard needs a specific check that a provider credential can
never reach stdout in plaintext, on a timeout or a non-zero exit.

```bash
git diff <range-start>..origin/main -- src/amplifier_app_opencode/onboarding.py
```

**Generated-file ownership manifests.** If the diff touches the skills or
modes bridge (`.amplifier-generated-commands.json`,
`.amplifier-generated-agents.json` handling), confirm the reconciliation
logic still never overwrites an unrecorded (user-owned) file -- this is
AGENTS.md invariant 4, and a regression here silently deletes or clobbers a
user's own config.

### Report before you change

Show the user what you found and what you propose to do about each item.
Do not silently delete things from someone else's commits.

---

## Phase 2: Versions

Check whether the bump already happened. The version in the manifest is the
target of the next release, so it may already be ahead of the last shipped
version.

```bash
grep -m1 '^version' pyproject.toml
grep -m1 '__version__' src/amplifier_app_opencode/__init__.py
```

**Both must agree, and nothing enforces that on its own.** `AGENTS.md`
invariant 2 states this plainly: these are two independent hand-edited
strings. `--version` reports `__init__.py`'s `__version__`; the installer
and `update` report `pyproject.toml`'s version via package metadata; `doctor`
reports neither. A past release (0.2.0) shipped these out of sync --
`__version__` was left at `0.1.3` while `pyproject.toml` moved to `0.2.0` --
and under-reported the installed version until 0.3.0 fixed it (see
`CHANGELOG.md`). `docs/ISSUES.md` ISSUE-002 tracks that this failure mode is
still reachable; there is no guard against it recurring. Treat both edits as
one atomic step, not two, and diff both files in the same review pass
before moving on:

```bash
git diff -- pyproject.toml src/amplifier_app_opencode/__init__.py
```

If a bump is needed, pick it by SemVer against the actual change set:

```
breaking CLI flag/behavior change, removed surface   -> major
new capability, new flag, new bridge behavior         -> minor
fix only, no new surface                              -> patch
```

Edit BOTH files in the same commit:

```
pyproject.toml                              [project] version
src/amplifier_app_opencode/__init__.py      __version__
```

---

## Phase 3: Changelog

`CHANGELOG.md` is Keep a Changelog with SemVer. The existing entries are
deliberately prose-heavy: they explain the mechanism and the reason, not
just the surface. Match that register -- a one-line bullet in a file full of
paragraphs reads as an afterthought.

Move whatever sits under `## [Unreleased]` into a new version heading, then
fill the gaps from the diff. Leave `## [Unreleased]` in place and empty.

```
## [Unreleased]

## [X.Y.Z] — YYYY-MM-DD

### Added
### Changed
### Fixed
### Security
### Notes
```

Write for a user of the tool, not a reader of the diff. Each entry should
answer: what can I now do, or what stopped being broken, and what do I have
to do to get it. If a change alters a default, moves the agent floor, or
requires action, say so plainly -- every existing floor bump in this file
names the specific capability that forced it (`GET /v1/skills`/`/v1/modes`,
`auth set --stdin`, namespaced reseller model ids, `--host-config` honoring
`provider.config`). Match that pattern for a new floor bump.

Do not describe internal refactors no consumer can observe. Confirm today's
date rather than assuming it.

---

## Phase 4: Floor check

Confirm, explicitly, whether `MIN_AGENT_VERSION` needs to move for this
release. State the rule plainly in the PR either way:

**This decision is made upstream, not here.** Whether a given amplifier-agent
release moves this repo's floor is owned by amplifier-agent's own upstream
release process, which executes the bump and opens the downstream PR here
when it does. This repo's release does not re-decide it and does not bump
it opportunistically because a newer amplifier-agent happens to exist --
`AGENTS.md` calls this out by name as a documented pitfall.

```bash
grep -m1 -A3 'MIN_AGENT_VERSION = ' src/amplifier_app_opencode/prereqs.py
```

If this release's scope (Phase 0) already includes a downstream PR from that
upstream process (i.e. `MIN_AGENT_VERSION`, `AGENT_PINNED_REF`, or
`AGENT_HARD_FLOOR` changed as part of the commits being released), confirm
the comment block above `MIN_AGENT_VERSION` names the specific capability
that forced it, in the same style as the existing entries, and fold that
into this release's changelog entry (Phase 3).

If it did not change, say so in the PR: "the agent floor is unchanged;
nothing in this release depends on new amplifier-agent behavior."

---

## Phase 5: Gate

`make check` plus a full clean e2e run are the ONLY gates. There is no CI
here to catch anything after this. Both must be green before a PR opens.

```bash
make check
```

Then the full suite, freshly provisioned, detached:

```bash
rm -f /tmp/release_e2e.log && setsid bash -c \
  'uv run python tests/e2e/cli.py run > /tmp/release_e2e.log 2>&1' \
  </dev/null >/dev/null 2>&1 &
echo "launched pid $!"
```

Poll rather than block (see Mechanics). This provisions a fresh DTU and runs
every suite: `agent_integration`, `bridge`, `chat`, `cli`, `config`,
`modes`, `shadowing`, `skills`, `traversal`. Roughly ten minutes for the
full ~54-test run.

If something fails, fix it or stop and report it. Do not open a release PR
on a red tree and plan to fix it in the PR -- there is no later gate that
will catch it, since merging IS the release.

---

## Phase 6: PR

Branch, commit, push, open.

```bash
git checkout -b chore/release-X.Y.Z origin/main
```

Conventional commit, mostly without a scope since this is a single package
(`AGENTS.md`'s convention). The version bump itself is usually folded into
the same descriptive commit as the change it accompanies, not a bare
version-only commit -- that is the actual pattern in this repo's history:

```
chore: raise amplifier-agent floor to 0.12.0, document raw LLM payload capture (0.4.0)
feat: GitHub Copilot passthrough + 0.3.0, agent floor to 0.11.0
feat: bridge amplifier-agent skills and modes into opencode (0.2.0)
```

If the sweep in Phase 1 removed things unrelated to the version bump, that
is a separate concern and reads better as its own commit. Stage explicitly;
never stage with `git add -A`.

Per `AGENTS.md`'s commit convention, a release-worthy PR title carries the
resulting version in parentheses at the end, describing what changed, e.g.:

```
feat: <what changed, in plain language> (X.Y.Z)
```

The PR body states facts and rationale only, never the steps of this
session. Cover:

- The user-visible change, in the same voice as the changelog entry
- Whether the agent-version floor moved, and the Phase 4 conclusion either
  way (moved because of upstream decision `<ref>`, or unchanged and why)
- That `pyproject.toml` and `__init__.py` versions agree
- That `make check` and a full clean e2e run are both green
- That **merging this PR is the release**: there is no tag push and no
  further publish step; the next `install.sh` run or `amplifier-opencode
  update` picks it up directly from `main`

Open it with `gh pr create`. Do not merge it yourself unless the user says
to.

---

## Report

Tell the user, plainly:

- What shipped: the version, and a one-line summary of the user-visible
  change
- The floor decision from Phase 4 and its reason, explicitly, either way
- That `make check` and the full e2e suite were both green before the PR
  opened
- The PR URL
- That once merged, users get the release on their next `install.sh` run or
  `amplifier-opencode update`

---

## Mechanics

The parts that silently waste the most time.

**The PR merge is the entire release mechanism.**

**Always detach long runs.** A bash tool timeout kills the process group and
orphans the DTU container. `nohup ... &` is not enough.

```bash
setsid bash -c '<cmd> > /tmp/x.log 2>&1' </dev/null >/dev/null 2>&1 &
echo "launched pid $!"
```

**Poll, never block.** Intervals of 60 to 115 seconds. Never above 120.

```bash
sleep 90; echo "=== $(date +%H:%M:%S) ==="; tail -n 25 /tmp/x.log; \
  pgrep -f "e2e/cli.py run" >/dev/null && echo RUNNING || echo DONE
```

**Never stage with `git add -A`.** Stage the paths you intend. A version
bump plus a sweep plus an unrelated experiment in the working tree is easy
to conflate otherwise.

**Two version files, one commit.** `pyproject.toml`'s `version` and
`src/amplifier_app_opencode/__init__.py`'s `__version__` are independent
strings with no automated check tying them together. Diff both in the same
review pass every time; the historical failure mode (0.2.0 shipping out of
sync) is exactly this step skipped.

**The floor is upstream's call, always.** Do not bump `MIN_AGENT_VERSION`,
`AGENT_PINNED_REF`, or `AGENT_HARD_FLOOR` in this repo's release process
because a newer `amplifier-agent` release exists. That decision and its
execution belong to `amplifier-agent`'s own upstream release process; this
skill only confirms and folds in what that process already delivered.

**Big output.** Pipe every long-running command through `tail -N` with a
sentinel echo after, and walk a large diff file by file rather than dumping
it all:

```bash
timeout 115 uv run python tests/e2e/cli.py run 2>&1 | tail -30; echo "EXIT_CHAIN_DONE"
```

**Sibling checkout and Docker for the full e2e gate.** The default local
mirror install needs `amplifier-agent` checked out as a sibling of this repo
and Docker running for the Gitea container. `ANTHROPIC_API_KEY` must be in
the host env.

**DTU naming.** `oc-e2e` is this harness's DTU and Gitea mirror name. Never
destroy a DTU or Gitea container you did not create.

More from microsoft/amplifier-app-opencode