release: v0.0.25 (#377)
* release: v0.0.25 * chore: add release publishing command
This commit is contained in:
parent
fec642a039
commit
3f2b4872ca
54
.agents/skills/merge-ready/SKILL.md
Normal file
54
.agents/skills/merge-ready/SKILL.md
Normal file
@ -0,0 +1,54 @@
|
||||
---
|
||||
name: merge-ready
|
||||
description: Take a branch from "code exists (or is about to)" to "ready for Ben's final review" — multi-axis subagent review with verified findings, fixes, ci:check, checkpoint commits, and an updated PR. Use whenever the user says a feature/fix/branch should be "merge ready", asks to get changes ready for review, or appends this to a build request ("build X and make it merge-ready").
|
||||
---
|
||||
|
||||
# Merge ready
|
||||
|
||||
Drive the current work to the point where the only remaining step is Ben's own review and merge. The deliverable is a pushed branch with a clean `pnpm ci:check`, checkpoint commits along the way, and an open PR with a high-level description plus review instructions.
|
||||
|
||||
**Never merge the PR. Ben always reviews last.**
|
||||
|
||||
## 0. Figure out the starting point
|
||||
|
||||
This skill composes with feature work — it is not only a review pass:
|
||||
|
||||
- **Invoked alongside a build request** ("build X, make it merge-ready"): implement the feature/fix first, committing as you go, then continue below. The review phases cover _all_ changes on the branch vs `origin/main`, not just the last edit.
|
||||
- **Invoked on existing work** ("make this branch merge-ready"): start directly at step 1. The scope is `git diff origin/main...HEAD` plus anything uncommitted.
|
||||
|
||||
## 1. Sync with main
|
||||
|
||||
- `git fetch origin main`. If the branch is behind, merge `origin/main` in and resolve conflicts (favor main's version for code this branch didn't intentionally change).
|
||||
- **Checkpoint:** commit the merge before starting review, so conflict resolution is auditable separately from review fixes.
|
||||
|
||||
## 2. Multi-axis subagent review
|
||||
|
||||
Spawn independent review subagents **in parallel**, one per axis, each given the branch diff scope (`git diff origin/main...HEAD`) and repo access:
|
||||
|
||||
1. **Unnecessary complexity** — thin wrappers, needless indirection, single-use abstractions, defensive guards for impossible states, dead config. This codebase deliberately stays simple.
|
||||
2. **Security** — authz on new endpoints (org/project scoping), SSRF, injection, secrets handling, anything user-input-shaped reaching D1/R2/external APIs.
|
||||
3. **Billing & metering** — ways a user could trigger DataForSEO/provider spend without being metered, charged-but-failed paths, retry/loop amplification, endpoints with unexpectedly high per-call user cost. Credits are billed via Autumn; uncounted spend is a revenue leak.
|
||||
4. **Library & project idioms** — TanStack (Router/Query/Start) used idiomatically; patterns match how the rest of the codebase already does it (Result-pattern error handling at provider seams, db/schema conventions, existing component patterns). Flag novel patterns where an established one exists.
|
||||
5. **Vibe-coded cruft** — leftover scaffolding, stale comments narrating the edit history, console.logs, TODO-without-owner, copy-pasted near-duplicates, files/exports nothing uses.
|
||||
|
||||
Each reviewer returns findings with file:line, severity (`blocker` / `should-fix` / `nitpick`), and a one-line rationale. Tell reviewers explicitly: this is an early-stage product — do not chase theoretical edge cases; mark anything debatable as `nitpick`.
|
||||
|
||||
## 3. Verify findings — never blindly accept
|
||||
|
||||
For each `blocker` and `should-fix` finding, spawn verification subagents (in parallel) that adversarially check the finding against the actual code and verdict **APPLY / APPLY-MODIFIED / REJECT** with reasoning. Drop rejected findings. Nitpicks don't need verification — they're reported, not necessarily fixed.
|
||||
|
||||
## 4. Fix, check, loop
|
||||
|
||||
- Apply verified `blocker`/`should-fix` fixes. Apply nitpicks only when trivial and clearly right; otherwise list them in the PR for Ben to judge.
|
||||
- **Checkpoint:** commit fixes in logical groups (e.g. one commit per axis or per concern) so the fix history is reviewable on its own.
|
||||
- Run `pnpm ci:check` (prettier, knip, tsc, oxlint). Fix failures and re-run until clean. If a fix was substantial (not formatting/lint), run a quick re-review of just that change.
|
||||
- Loop until ci:check passes and no verified findings remain unaddressed.
|
||||
|
||||
## 5. Push and open/update the PR
|
||||
|
||||
- Push the branch. Open a PR against `main` if one doesn't exist; otherwise update the existing PR's description.
|
||||
- PR description requirements:
|
||||
- **High-level** — what changed and why, written for a human skimming. No file paths, no per-file changelog.
|
||||
- **How to review** — a short ordered guide: what to look at first, what the risky/judgment-call areas are, what was deliberately left out of scope.
|
||||
- **Review notes** — unfixed nitpicks and any REJECT verdicts worth a second opinion, clearly labeled as such.
|
||||
- Report back to Ben: PR link, one-paragraph summary, and anything that still needs his judgment. Do not merge.
|
||||
68
.agents/skills/openseo-release-notes/SKILL.md
Normal file
68
.agents/skills/openseo-release-notes/SKILL.md
Normal file
@ -0,0 +1,68 @@
|
||||
---
|
||||
name: openseo-release-notes
|
||||
description: 'Cut an OpenSEO release — bump the version, draft user-facing release notes from commits since the last tag, run a review + subagent-verification pass, and open a "release: vX.X.X" PR. Use when the user asks to prepare a release, bump the version, or write release notes.'
|
||||
---
|
||||
|
||||
# OpenSEO release notes
|
||||
|
||||
Cut a release for this repo end to end. The deliverables are a version bump in `package.json`, a new `release-notes/v<version>.md`, and a PR against `origin/main` titled `release: v<version>`.
|
||||
|
||||
## 1. Bump the version
|
||||
|
||||
- Read `package.json`. If the branch has already bumped `version`, treat that as the source of truth and do not change it.
|
||||
- Otherwise bump the patch version (e.g. `0.0.19` → `0.0.20`). Only bump minor/major if explicitly asked.
|
||||
|
||||
## 2. Collect the changes since the last release
|
||||
|
||||
- Find the latest tag: `git tag --sort=-creatordate | head -1`. Verify the branch is up to date with `origin/main` (`git fetch origin main && git log HEAD..origin/main --oneline` should be empty; flag it if not).
|
||||
- List commits: `git log <last-tag>..HEAD --oneline`. You can also run `pnpm release:notes` for a raw commit inventory — use it only as a checklist of candidate changes, never as the draft's structure (its Improved/Changed/Docs sections must not appear in the notes).
|
||||
- For each commit, fetch the PR body and author (`gh pr view <num> --repo <repo> --json title,body,author`) — squash-commit subjects can be stale. The `(#NN)` in commit subjects can reference **either** repo: try `bensenescu/open-seo` (origin) first and fall back to `every-app/open-seo` (public) — outside contributors' PRs and their handles live on the public repo. Commits with no `(#NN)` may still be an outside contribution with a public PR (`gh pr list --repo every-app/open-seo --state merged --author <login>`); check `git log --format=%an` for the author. Verify claims against the final code when a PR body and commit subject disagree (features get reverted before merge).
|
||||
- Record the PR author's GitHub handle alongside each change so the bullet can credit them.
|
||||
|
||||
## 3. Draft the notes
|
||||
|
||||
Write `release-notes/v<version>.md`. **`release-notes/v0.0.24.md` is the canonical style exemplar** — match it (v0.0.25 and later follow the same style); v0.0.23 and earlier are the old verbose style, never imitate them. The notes are a scannable digest, not documentation: the whole file fits on one screen (roughly 15 lines including headings), and every line earns its place.
|
||||
|
||||
Format:
|
||||
|
||||
- Top line: a fragment naming the release's 2–3 highlights ("GSC UI, improved app layout and beta in app agent."). Not a "This release brings…" sentence.
|
||||
- Sections: `## What's new` and `## Fixed` only. There is no "Improved" section — an improvement is either headline-worthy (What's new) or it's cut.
|
||||
- **What's new bullets name the feature; they don't sell it.** One short line each ("Redesigned the app layout", "Get GSC Insights inside the app") — no em-dash feature tours, no "so you can…" benefit copy, no lists of everything the feature touches. If the name alone is ambiguous, one clause of plain-words context is the maximum.
|
||||
- At most **one sub-bullet per feature**, one short line: the single most useful detail, a requirement ("Requires `OPENROUTER_API_KEY`"), or an expectation-setter.
|
||||
- **Label rough features "(Beta)"** and set expectations honestly, including pointing at the better alternative for now. The expectation-setter rides the top-level line after a dash ("(Beta) In app agent - MCP is still recommended, but we'll be working to improve this during the summer."), keeping the sub-bullet slot free for a requirement or detail.
|
||||
- Fixed: 3–5 bullets, one plain sentence each, only bugs a user plausibly hit and would recognize ("Claude answers in AI search work again."). No error codes, status codes, schema/infra vocabulary, or mechanism. If more than four qualify, keep the ones hit in core flows (searches, audits, tracking, MCP answers) and drop fixes for recovering self-inflicted state (re-adding, un-archiving, refreshing) first.
|
||||
- **Credit the contributor.** End the bullet with `— thanks @handle` for outside contributors only — never for the maintainer's own PRs (`bensenescu`). Credit goes on the top-level bullet, not sub-bullets. Multiple contributors: `— thanks @a, @b`.
|
||||
- End with: `Full Changelog: https://github.com/every-app/open-seo/compare/v<prev>...v<version>`
|
||||
|
||||
Curation — this is where the work is. Cut aggressively; the Full Changelog link covers the long tail:
|
||||
|
||||
- Only changes to the **product itself** — the app, the MCP tools, the SEO data/features someone running OpenSEO actually uses. Litmus test per bullet: **would a self-hoster notice this while using the product?** Caring in the abstract (a new backend option, a raised cap) is not enough.
|
||||
- Do NOT mention:
|
||||
- **Marketing-website (`web/`) changes** — landing pages, copy, positioning, blog.
|
||||
- **Pricing / plans / subscription / billing** — paywalls, free-trial/plan changes, Autumn config. Hosted-commercial concerns, irrelevant to self-hosters.
|
||||
- **Onboarding-flow-only changes** — signup/onboarding chat, profiling steps, upgrade rails, email-verification UX. Not a product capability, even when sizable.
|
||||
- **Hosted-app internals & meta** — analytics, specs/ADRs, CI, refactors, dependency bumps.
|
||||
- **Invisible-to-the-user work, even when product-relevant** — security hardening, raised caps/limits, stability/memory/perf fixes, database/backend options and migrations. A user reading the notes should recognize every line from using the product; if they'd only notice it in a config file or an incident that no longer happens, cut it.
|
||||
- When torn between including and cutting, cut. A 4-bullet What's new that gets read beats a 10-bullet one that doesn't.
|
||||
- Never invent features — every claim must trace to a commit.
|
||||
- Numbers in bullets are usually selling — cut them; if one is genuinely load-bearing, quote the conservative, typical figure, never a cherry-picked best case.
|
||||
|
||||
## 4. Review and verify
|
||||
|
||||
1. Spawn a reviewer subagent with: the draft, the guidelines above, the per-commit facts you gathered, and repo access. It returns numbered review comments citing which guideline each violates. Its charge includes **verbosity**: flag any bullet that sells instead of names, any second sub-bullet, any Fixed bullet with mechanism vocabulary, and anything that pushes the file past one screen.
|
||||
2. For each substantive comment, spawn a verification subagent (in parallel) that adversarially checks the comment against the actual commits/code and verdicts APPLY / APPLY-MODIFIED / REJECT.
|
||||
3. Apply only verified comments.
|
||||
|
||||
## 5. Open the PR
|
||||
|
||||
- Commit the version bump, release notes, and any skill changes on a branch named `claude/v<version>` (use the current branch if it already follows this pattern).
|
||||
- Push to `origin` and open a PR against `main` titled exactly `release: v<version>`. PR body: the release notes content.
|
||||
- Do not tag or publish the GitHub release — that happens after merge. After merge, run `pnpm release:publish`. It reads the version from `package.json` and publishes the matching `release-notes/v<version>.md` to `every-app/open-seo`.
|
||||
- The equivalent command is:
|
||||
|
||||
```sh
|
||||
gh release create v<version> \
|
||||
--repo every-app/open-seo \
|
||||
--title v<version> \
|
||||
--notes-file release-notes/v<version>.md
|
||||
```
|
||||
@ -1,6 +1,6 @@
|
||||
---
|
||||
name: openseo-release-notes
|
||||
description: Cut an OpenSEO release — bump the version, draft user-facing release notes from commits since the last tag, run a review + subagent-verification pass, and open a "release: vX.X.X" PR. Use when the user asks to prepare a release, bump the version, or write release notes.
|
||||
description: 'Cut an OpenSEO release — bump the version, draft user-facing release notes from commits since the last tag, run a review + subagent-verification pass, and open a "release: vX.X.X" PR. Use when the user asks to prepare a release, bump the version, or write release notes.'
|
||||
---
|
||||
|
||||
# OpenSEO release notes
|
||||
@ -57,4 +57,12 @@ Curation — this is where the work is. Cut aggressively; the Full Changelog lin
|
||||
|
||||
- Commit the version bump, release notes, and any skill changes on a branch named `claude/v<version>` (use the current branch if it already follows this pattern).
|
||||
- Push to `origin` and open a PR against `main` titled exactly `release: v<version>`. PR body: the release notes content.
|
||||
- Do not tag or publish the GitHub release — that happens after merge. Suggest `gh release create v<version> --notes-file release-notes/v<version>.md` as the post-merge step.
|
||||
- Do not tag or publish the GitHub release — that happens after merge. After merge, run `pnpm release:publish`. It reads the version from `package.json` and publishes the matching `release-notes/v<version>.md` to `every-app/open-seo`.
|
||||
- The equivalent command is:
|
||||
|
||||
```sh
|
||||
gh release create v<version> \
|
||||
--repo every-app/open-seo \
|
||||
--title v<version> \
|
||||
--notes-file release-notes/v<version>.md
|
||||
```
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
"name": "open-seo",
|
||||
"private": true,
|
||||
"sideEffects": false,
|
||||
"version": "0.0.24",
|
||||
"version": "0.0.25",
|
||||
"type": "module",
|
||||
"packageManager": "pnpm@10.30.1",
|
||||
"scripts": {
|
||||
@ -32,6 +32,7 @@
|
||||
"deploy:postgres": "npm run db:migrate:pg && npm run build && wrangler deploy",
|
||||
"knip": "knip",
|
||||
"release:notes": "node scripts/release-notes.mjs",
|
||||
"release:publish": "node scripts/publish-release.mjs",
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest",
|
||||
"test:ci": "vitest run --reporter=dot",
|
||||
|
||||
15
release-notes/v0.0.25.md
Normal file
15
release-notes/v0.0.25.md
Normal file
@ -0,0 +1,15 @@
|
||||
Site Audit issues, local rank tracking and multiple Search Console accounts.
|
||||
|
||||
## What's new
|
||||
|
||||
- New Issues tab in Site Audit
|
||||
- City and region targeting for Rank Tracking — thanks @RDeemer63
|
||||
- Multiple Google accounts for Search Console
|
||||
|
||||
## Fixed
|
||||
|
||||
- Empty H1 tags are now reported as missing.
|
||||
- Redirect and non-HTML pages now display correctly in Site Audit.
|
||||
- Lighthouse no longer checks the wrong Site Audit start page when both slash forms exist.
|
||||
|
||||
Full Changelog: https://github.com/every-app/open-seo/compare/v0.0.24...v0.0.25
|
||||
58
scripts/publish-release.mjs
Normal file
58
scripts/publish-release.mjs
Normal file
@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
// @ts-check
|
||||
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { existsSync, readFileSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { parseArgs } from "node:util";
|
||||
|
||||
const argv = process.argv.slice(2);
|
||||
const normalizedArgv = argv[0] === "--" ? argv.slice(1) : argv;
|
||||
|
||||
const { values } = parseArgs({
|
||||
args: normalizedArgv,
|
||||
options: {
|
||||
"dry-run": { type: "boolean", default: false },
|
||||
},
|
||||
allowPositionals: false,
|
||||
});
|
||||
|
||||
const repoRoot = fileURLToPath(new URL("../", import.meta.url));
|
||||
const packageJsonPath = path.join(repoRoot, "package.json");
|
||||
/** @type {unknown} */
|
||||
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
||||
|
||||
if (!packageJson || typeof packageJson !== "object") {
|
||||
throw new Error("package.json must contain an object");
|
||||
}
|
||||
|
||||
const version = Reflect.get(packageJson, "version");
|
||||
if (typeof version !== "string" || !/^\d+\.\d+\.\d+$/.test(version)) {
|
||||
throw new Error(`Invalid package version: ${String(version)}`);
|
||||
}
|
||||
|
||||
const tag = `v${version}`;
|
||||
const notesFile = `release-notes/${tag}.md`;
|
||||
if (!existsSync(path.join(repoRoot, notesFile))) {
|
||||
throw new Error(`Missing release notes: ${notesFile}`);
|
||||
}
|
||||
|
||||
const args = [
|
||||
"release",
|
||||
"create",
|
||||
tag,
|
||||
"--repo",
|
||||
"every-app/open-seo",
|
||||
"--title",
|
||||
tag,
|
||||
"--notes-file",
|
||||
notesFile,
|
||||
];
|
||||
|
||||
if (values["dry-run"]) {
|
||||
process.stdout.write(`gh ${args.join(" ")}\n`);
|
||||
} else {
|
||||
execFileSync("gh", args, { cwd: repoRoot, stdio: "inherit" });
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user