Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 — <codename>`), 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:**
Expand Down
23 changes: 21 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 — <codename>`)
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
Expand Down Expand Up @@ -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
Expand Down
8 changes: 7 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,20 @@ 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.
- **Long-running work reports progress proactively** - During pipeline babysitting, bulk operations, or extended planning, post a one-line status roughly every 10 minutes and flag a suspected stall immediately with evidence; never let the user be the one to ask "still running?". Before starting a multi-item queue, confirm its extent and stop-point with the user.
- **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 — <codename>`), 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.
Expand Down
31 changes: 22 additions & 9 deletions agentic/commands/release.md
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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.

Expand Down Expand Up @@ -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 "<Codename> (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 — <Codename>" --notes-file <tmpfile>` — the body is
the new version's `###` sections copied verbatim, plus a trailing
`**Full Changelog:** https://github.com/MarkusNeusinger/anyplot/compare/v<last>...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 — <Codename>" --notes-file <tmpfile>`. The body:
- **Intro line:** merge count and PR range of the window (`git log v<last>..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<last>...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).
Expand Down
Loading