diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 69207514a8..678c4f8baa 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -8,10 +8,16 @@ A companion guide `CLAUDE.md` at the repo root carries the shared rules for Clau - **Always write in English** - All output text (code comments, commit messages, PR descriptions, issue comments, documentation) must be in English, even if the user writes in another language. - **Repository prose follows the Google developer documentation style guide** - `docs/`, `README.md`, `agentic/docs/`, changelog entries, and PR/issue text use [Google style](https://developers.google.com/style) (sentence-case headings, second person, numbered procedures); the concrete rules and the house-style exception (`docs/reference/style-guide.md` governs website/brand surfaces) live in the `write-docs` skill (`.claude/skills/write-docs/SKILL.md`). Existing docs migrate on touch, not via bulk rewrites. -- **Every PR updates `CHANGELOG.md`** - Add entries under `[Unreleased]` (Keep-a-Changelog categories, English, bold-titled bullets for headline entries, PR refs — like the existing entries) — that file is how releases get posted; a PR without its entry is incomplete. **Exempt:** the automated plot pipeline's output (spec-create, impl-generate/review/repair/merge, spec auto-polish, daily-regen PRs) and individual Dependabot bumps — those are summarized in aggregate at release time (see `agentic/commands/release.md`). This rule is duplicated in `CLAUDE.md` and `agentic/commands/pull_request.md`; keep all three in sync when changing it. When reviewing a non-exempt PR that lacks a changelog entry, flag it. +- **Changelog and releases** - see [Changelog + releases](#changelog--releases) below: every PR updates `CHANGELOG.md`, a release moves the section and bumps the version files via a PR, and the GitHub release is that section condensed, never copied. When reviewing a non-exempt PR that lacks a changelog entry, flag it. - **Never echo secret values into transcripts or logs** - Verify secrets by exit code or metadata, never by printing them. - **Structural fix over symptomatic fix** - When a cheap symptomatic fix and a correct structural fix compete, take the structural one: fix the cause, never mute the alarm. Never modify working code to make a broken test pass — fix the test or flag it. +## Changelog + releases + +- **Every PR updates `CHANGELOG.md`** under `[Unreleased]` (Keep-a-Changelog categories, English, bold-titled bullets for headline entries, PR refs — like the existing entries) — that file is how releases get posted; a PR without its entry is incomplete. **Exempt:** the automated plot pipeline's output (spec-create, impl-generate/review/repair/merge, spec auto-polish, daily-regen PRs) and individual Dependabot bumps — those are summarized in aggregate at release time (see `agentic/commands/release.md`). This rule is duplicated in `CLAUDE.md` and `agentic/commands/pull_request.md`; keep all three in sync when changing it. +- **A release** moves `[Unreleased]` under a new version heading (`## [X.Y.Z] — YYYY-MM-DD — `), adds the aggregate lines for the exempt classes (the italic *Catalog* line, the single **Dependencies:** bullet), and bumps the version files (`pyproject.toml` `project.version`, `uv.lock`, `app/package.json`) in the same commit, via a `release/vX.Y.Z` PR. Every bullet already carries its PR reference — `/pull_request` appends it when the PR opens. After the merge the tag goes on the merge commit and the GitHub release is created from the section. +- **A GitHub release is that section condensed, never copied:** an intro line (merge count, PR range, link to `CHANGELOG.md`); the section's own `### Added / Changed / Removed / Fixed` headings in the section's order (an empty one is omitted); one bullet per NOTABLE entry — chores, dependency bumps and small fixes are left out, no fixed count — each at most two lines: its bold title, one clause with the essence or the headline number, its PR reference; a compare link (`compare/vPREV...vNEW`) as the last line. Numbers are copied exactly; only PR numbers from the section are cited; the full text lives only in the CHANGELOG — the release page is the index into it. The cut procedure itself is `agentic/commands/release.md`. + ## Task Suitability **Good tasks for Copilot:** diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e8380333b..a066cda43d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,8 +5,18 @@ All notable changes to this project are documented here. The format is based on rather than library SemVer: major for milestone releases, minor for feature batches, patch for fix-only (see `agentic/commands/release.md`). -Every non-exempt PR adds its entries under `[Unreleased]`; a release moves that section under a -new version heading (see `agentic/commands/release.md`). Product, pipeline-infrastructure, and +Every non-exempt PR adds its entries under `[Unreleased]`, each bullet with its PR reference. A +release moves that section under a new version heading (`## [X.Y.Z] — YYYY-MM-DD — `) +and bumps `pyproject.toml` (`project.version`), `uv.lock` and `app/package.json` in the same +commit, via a PR (procedure: `agentic/commands/release.md`). After the merge the tag goes on the +merge commit and the GitHub release is created from the section — condensed, never copied (rule +of 2026-08-28): an intro line with the merge count, the PR range and a link to this file; the +section's own headings in the section's order (an empty one is omitted); one bullet per NOTABLE +entry — chores, dependency bumps and small fixes are left out, and there is no fixed count — at +most two lines each: its bold title, one clause with the essence or the headline number, its PR +reference; a compare link as the last line. Numbers are copied exactly and only PR numbers from +the section are cited; the full text lives only here — the release page is the index into it. +Product, pipeline-infrastructure, and docs changes are covered here. **Exempt:** the automated plot pipeline's routine output (spec-create, impl-generate/review/repair/merge, spec auto-polish, and daily-regen PRs) and individual Dependabot bumps — listing those per entry would drown the file (900+ automated @@ -211,6 +221,15 @@ aggregate instead: an italic *Catalog* line at the end of the version section an ### Changed +- **GitHub releases are the CHANGELOG section condensed, never copied** — a release body now + carries an intro line (merge count, PR range, link to this file), the section's own headings, + one bullet per notable entry at most two lines long (bold title, one clause with the essence or + the headline number, PR reference) and a compare link as the last line; chores, dependency bumps + and small fixes stay out, numbers are copied exactly, only PR numbers from the section are + cited, and the full text lives only here. The v3.1.0 release page was the whole section copied + — 640 lines. The rule lives in `CLAUDE.md` § "Changelog + releases", + `.github/copilot-instructions.md`, this file's header and `agentic/commands/release.md` — the + same rule kurrentschrift adopted on 2026-08-28 (#10810). - **`open-pr` skill: the post-merge Cloud Build watch names the region** — `gcloud builds list` without `--region=europe-west4` answers with months-old global builds (all `SUCCESS`, none from today), which reads like "nothing was triggered" while both regional deploys are diff --git a/CLAUDE.md b/CLAUDE.md index 0c97b4955e..7bc40f0d90 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -17,7 +17,7 @@ A companion guide `.github/copilot-instructions.md` carries the shared rules for - **Always write in English** - All output text (code comments, commit messages, PR descriptions, issue comments, documentation) must be in English, even if the user writes in another language. - **Repository prose follows the Google developer documentation style guide** - `docs/`, `README.md`, `agentic/docs/`, changelog entries, and PR/issue text use [Google style](https://developers.google.com/style) (sentence-case headings, second person, numbered procedures); the concrete rules and the house-style exception (`docs/reference/style-guide.md` governs website/brand surfaces) live in the `write-docs` skill. Existing docs migrate on touch, not via bulk rewrites. - **Update documentation when making changes** - When adding new features, events, or modifying behavior, always check if related documentation needs updating (e.g., `docs/reference/plausible.md` for analytics events, `docs/workflows/` for workflow changes, `docs/contributing.md` for user-facing changes). -- **Every PR updates `CHANGELOG.md`** - Add entries under `[Unreleased]` (Keep-a-Changelog categories, English, bold-titled bullets for headline entries, PR refs — like the existing entries) — that file is how releases get posted; a PR without its entry is incomplete. **Exempt:** the automated plot pipeline's output (spec-create, impl-generate/review/repair/merge, spec auto-polish, daily-regen PRs) and individual Dependabot bumps — those are summarized in aggregate at release time (see `agentic/commands/release.md`). This rule is duplicated in `.github/copilot-instructions.md` and `agentic/commands/pull_request.md`; keep all three in sync when changing it. +- **Changelog and releases** - see [Changelog + releases](#changelog--releases) below: every PR updates `CHANGELOG.md`, a release moves the section and bumps the version files via a PR, and the GitHub release is that section condensed, never copied. - **External-system writes need explicit, named authorization** - Merging or closing PRs/issues this session did not create, bulk merges, and label changes on others' PRs are blocked by the permission classifier unless the user named that action; a generic "ok, sounds good" authorizes nothing. The same discipline covers every prod-touching action in interactive sessions — Cloud SQL writes/DDL, GCS production-folder changes, Secret Manager access, Cloud Build config: name the exact action, resource, and id, and ask before acting (the automated `spec-*`/`impl-*` workflows write these by design and are exempt). For any change to `.claude/settings*.json`, use the built-in `/update-config` skill (a Claude Code harness skill, not a repo command) immediately — direct writes are blocked as self-modification and retrying variants just burns round trips. - **Never echo secret values into the transcript** - Verify secrets by exit code or metadata, never by printing them; never create a Secret Manager version via `echo` (the trailing newline corrupts the value). - **Snapshot before destructive prod operations** - Before anything that can overwrite or delete shared prod DB data or GCS production objects (bulk UPDATE/DELETE, a data-rewriting migration, bulk GCS overwrite/delete): take a timestamped backup first (`pg_dump` to a new directory outside the working tree, `gsutil cp` to a backup prefix), sanity-check it (row/object counts — a silent empty snapshot is worse than none because it looks like safety), and never write into, delete, or rename an existing snapshot. @@ -25,6 +25,12 @@ A companion guide `.github/copilot-instructions.md` carries the shared rules for - **Manual user tasks go to Todoist** - Whenever a session identifies a step only the user can or should do (adding `approved` labels, merge authorization, console/billing/DNS actions, secret rotations), create a task in the user's Todoist project **Anyplot** (via the Todoist MCP tools) naming the concrete action and a context link (PR/issue/run URL) — instead of leaving it buried in a chat reply. Interactive sessions only; GitHub Actions workflows have no MCP access. - **Fix small build/test blockers directly, even when out of scope** - If a typecheck error, failing test, lint failure, or other small pipeline blocker shows up while working on something unrelated (incl. things the current PR was not meant to touch), fix it in the same PR or a tiny follow-up — never leave it parked under "out of scope". A latent `tsc` error that doesn't surface locally will silently break the next Cloud Build, which deploys nothing new and leaves production stale even though every PR check looks green (this is exactly how PR #6961's frontend fixes never reached anyplot.ai — the unrelated `prism/r` TS7016 from #6944 was deferred, then blocked the next `yarn build`). The bar: if the fix is < ~20 lines and obviously correct, just do it; if it would expand scope meaningfully, ask first rather than deferring silently. +## Changelog + releases + +- **Every PR updates `CHANGELOG.md`** under `[Unreleased]` (Keep-a-Changelog categories, English, bold-titled bullets for headline entries, PR refs — like the existing entries) — that file is how releases get posted; a PR without its entry is incomplete. **Exempt:** the automated plot pipeline's output (spec-create, impl-generate/review/repair/merge, spec auto-polish, daily-regen PRs) and individual Dependabot bumps — those are summarized in aggregate at release time (see `agentic/commands/release.md`). This rule is duplicated in `.github/copilot-instructions.md` and `agentic/commands/pull_request.md`; keep all three in sync when changing it. +- **A release** moves `[Unreleased]` under a new version heading (`## [X.Y.Z] — YYYY-MM-DD — `), adds the aggregate lines for the exempt classes (the italic *Catalog* line, the single **Dependencies:** bullet), and bumps the version files (`pyproject.toml` `project.version`, `uv.lock`, `app/package.json`) in the same commit, via a `release/vX.Y.Z` PR. Every bullet already carries its PR reference — `/pull_request` appends it when the PR opens. After the merge the tag goes on the merge commit and the GitHub release is created from the section. +- **A GitHub release is that section condensed, never copied:** an intro line (merge count, PR range, link to `CHANGELOG.md`); the section's own `### Added / Changed / Removed / Fixed` headings in the section's order (an empty one is omitted); one bullet per NOTABLE entry — chores, dependency bumps and small fixes are left out, no fixed count — each at most two lines: its bold title, one clause with the essence or the headline number, its PR reference; a compare link (`compare/vPREV...vNEW`) as the last line. Numbers are copied exactly; only PR numbers from the section are cited; the full text lives only in the CHANGELOG — the release page is the index into it. The cut procedure itself is `agentic/commands/release.md`. + ## PR Follow-Through (mandatory after every `gh pr create`) After opening a PR, the work is **not** complete. Stay with the PR until both the pipeline is green AND review feedback has been addressed. "PR opened" is a checkpoint, not the finish line. diff --git a/agentic/commands/release.md b/agentic/commands/release.md index 49acf8ddf8..60489191d6 100644 --- a/agentic/commands/release.md +++ b/agentic/commands/release.md @@ -1,7 +1,9 @@ # Release Cut a new anyplot release: finalize the changelog, bump the version, tag, and publish the GitHub -release. The release notes ARE the changelog section — no notes are written from scratch. +release. The release notes are the changelog section **condensed** — never copied verbatim, never +written from scratch (owner rule, 2026-08-28; the shape is in `CLAUDE.md` § "Changelog + +releases", the procedure is step 7 below). ## Variables @@ -14,9 +16,9 @@ version: $1 (optional — e.g. `3.1.0`; if omitted, propose one from the `[Unrel precedent), minor for feature batches, patch for fix-only releases. - **Never work on `main` directly** — do the changelog/version edits on a `release/vX.Y.Z` branch and open a PR. -- The release PR should touch exactly three files: `CHANGELOG.md`, `pyproject.toml`, and - `uv.lock` (the lock pins the project's own version — v3.0.0 precedent, commit d05e1f2a7). Keep - the diff tiny and auditable. +- The release PR should touch exactly four files: `CHANGELOG.md`, `pyproject.toml`, `uv.lock` + (the lock pins the project's own version — v3.0.0 precedent, commit d05e1f2a7) and + `app/package.json` (step 4). Keep the diff tiny and auditable. - Pick a short **codename** (release theme, a few words) — it appears in three synchronized places: the changelog heading, the annotated tag message, and the GitHub release title. @@ -45,11 +47,22 @@ version: $1 (optional — e.g. `3.1.0`; if omitted, propose one from the `[Unrel from `CLAUDE.md`. Ask the user to merge unless explicitly authorized to merge autonomously. 6. **Tag after merge** (on the updated `main`): `git tag -a vX.Y.Z -m " (YYYY-MM-DD)" && git push origin vX.Y.Z` -7. **Publish the GitHub release** with the changelog section as body. Write the body to a temp - file first (it is multiline and contains backticks — do not inline it into `--notes`): - `gh release create vX.Y.Z --title "vX.Y.Z — " --notes-file ` — the body is - the new version's `###` sections copied verbatim, plus a trailing - `**Full Changelog:** https://github.com/MarkusNeusinger/anyplot/compare/v...vX.Y.Z` line. +7. **Publish the GitHub release** with the changelog section **condensed** as body — never the + section copied (the v3.1.0 page was 640 lines). Write the body to a temp file first (it is + multiline and contains backticks — do not inline it into `--notes`), then + `gh release create vX.Y.Z --title "vX.Y.Z — " --notes-file `. The body: + - **Intro line:** merge count and PR range of the window (`git log v..vX.Y.Z --oneline + --merges | wc -l` for the count; lowest and highest PR number for the range) and a link to + `CHANGELOG.md` — "the full record is [CHANGELOG.md](…), and each PR carries its own + reasoning". + - **The section's own `###` headings, in the section's order;** an empty heading is omitted. + - **One bullet per NOTABLE entry** — chores, dependency bumps, small fixes and the aggregate + lines are left out; there is no fixed count. Each bullet is at most two lines: the entry's + **bold title** verbatim, one clause with the essence or the headline number, its PR + reference. Numbers are copied exactly from the entry; only PR numbers that appear in the + section are cited. + - **Last line:** `**Full Changelog:** https://github.com/MarkusNeusinger/anyplot/compare/v...vX.Y.Z`. + The full text lives only in the CHANGELOG; the release page is the index into it. 8. **Verify:** `gh release view vX.Y.Z` renders correctly; the site masthead picks up the new tag automatically (`app/src/hooks/useLatestRelease.ts` fetches `releases/latest` with a 1 h localStorage cache — nothing to deploy).