{"owner":"GreptimeTeam","repo":"greptimedb","hasSkills":true,"totalSkillsCount":7,"totalTokensCount":17441,"categories":["anthropic-skill","root-instruction"],"hasMcp":false,"mcpConfig":null,"found":[".agents/skills/greptimedb-release-note/SKILL.md",".agents/skills/greptimedb-release/agents/openai.yaml",".agents/skills/greptimedb-release-note/agents/openai.yaml",".agents/skills/greptimedb-release/SKILL.md","AGENTS.md",".agents/skills/greptimedb-development-docker-image/SKILL.md",".agents/skills/greptimedb-fuzz-ci-failure-investigation/SKILL.md"],"skills":{".agents/skills/greptimedb-release-note/SKILL.md":"---\nname: greptimedb-release-note\ndescription: Generate a GreptimeDB release changelog (minor, patch, or alpha/beta/rc pre-release) with git cliff (correct range, subtract already-released patch PRs, rebuild contributors, add human-curated highlights and upgrade notes), output to a file, and prepare the docs-repo blog PR. Use when asked to write/generate a GreptimeDB release note or changelog.\n---\n\n# GreptimeDB Release Note / Changelog\n\nGenerate the changelog for a GreptimeDB release. Tooling: **`git cliff`** (config:\n`cliff.toml` in the greptimedb repo) and **`gh`**. Run inside the greptimedb checkout.\n\n**Prerequisites:** `git cliff`, `gh` (check `gh auth status`), and **Python** (the\nsubtract-PRs / rebuild-contributors step in §3–§4 is scripted). This skill writes\n`<remote>` for the git remote pointing at `GreptimeTeam/greptimedb` (often `upstream`,\nsometimes `origin`); resolve it with\n`git remote -v | grep -i 'GreptimeTeam/greptimedb' | awk '{print $1}' | head -1`.\n\n## 1. GitHub token (never print it)\n\n`git cliff` enriches commits with PR titles/authors via the GitHub API; for hundreds of\ncommits you need a token or you hit rate limits. Pass it inline and **never read or echo the\ntoken**:\n```\nGITHUB_TOKEN=$(gh auth token) git cliff ...\n```\nAlternatively the user sources an env file that exports `GITHUB_TOKEN` (don't read it). Without\na token it still runs, but may be rate-limited / incomplete.\n\n## 2. Determine the previous version (skip nightlies)\n\nFrom `gh release list --repo GreptimeTeam/greptimedb`, ignore `*-nightly-*` and build-suffixed\ntags (e.g. `v1.3.0-alpha.1-3c0e2a8d5-20260928-1790639248`).\n- Patch `vX.Y.Z` (Z>0): previous = `vX.Y.(Z-1)`.\n- New minor `vX.Y.0`: previous = the latest `vX.(Y-1).*` (for `v1.0.0`, the biggest `0.x`).\n- Pre-release `vX.Y.Z-{alpha,beta,rc}.N`: previous = the latest pre-release tag of the same\n  `vX.Y.Z` (e.g. `v1.3.0-beta.1` for `v1.3.0-beta.2`). If there is none (the first alpha),\n  see \"First pre-release\" in §3: the previous minor's `.0` may not be published yet, and since\n  v1.2 a `.0` tag sits on `release/vX.Y`, not on `main`.\nDouble-check with the user.\n\n## 3. Pick the range and generate\n\nKey topology fact: **patch tags** (`v1.0.1`, `v1.0.2`) live on `release/vX.Y` and are **NOT**\nancestors of `main` (they are cherry-picks with different SHAs). Up to v1.1 the `.0` tag was\nthe point where `release/vX.Y` left `main`; from v1.2 the branch is cut before the `.0`, and\npre-releases and the `.0` itself are tagged on the branch with backports. Verify with\n`git merge-base --is-ancestor <tag> <remote>/main`.\n\n### New minor (`X.Y.0`)\nThe note compares against the whole previous line: everything new since `release/vX.(Y-1)`\nleft `main`, minus what any published `vX.(Y-1).*` release (pre-releases, `.0`, patches)\nalready shipped. The line's own pre-releases (`vX.Y.0-beta.*`) are not subtracted; the `.0`\nnote covers the whole minor.\n\nBase = the **previous minor tag** (e.g. `v1.1.0`); tip = the tagged `vX.Y.0` commit, resolved as\nin \"Pre-release\" below. Split the range at the point where `release/vX.Y` left `main`:\n```\nbp=$(git merge-base <remote>/release/vX.Y <remote>/main)\n```\n- `<prev-minor-tag>..$bp` is on `main`; generate it with `git cliff`:\n  ```\n  GITHUB_TOKEN=$(gh auth token) git cliff <prev-minor-tag>..$bp --tag vX.Y.0 \\\n    -o /path/CHANGELOG-vX.Y.0.md\n  ```\n  `cliff.toml`'s `ignore_tags` folds in-range nightly tags into the single section.\n- `$bp..<tip>` is the release branch: backports, squashed picks, and commits without a PR.\n  `git cliff` fails on such commits (`Filter call 'sort' failed`), so build this part as a\n  pre-release: collect the original PRs with the loop in \"Pre-release\" and add their bullets.\n  List the commits that carry no PR with\n  `git log --format='%h %s' $bp..<tip> | grep -vE '\\(#[0-9]+\\)$'`; drop mechanical ones, and\n  for a manual port of a `main` PR (the body says `cherry picked from commit` a SHA that is not\n  on `main`), find that PR by title and cite it.\n\n**Then subtract PRs already shipped in published `vX.(Y-1).*` releases**: their `main` commits\nare inside the range. Build the set as for the first pre-release (below): the PR links in\nevery published `vX.(Y-1).*` release body, plus the `(#N)` line endings in the history of the\nnewest published `vX.(Y-1).*` tag. Remove every changelog bullet whose `#NNNN` is in that set (a\nsmall Python script is the reliable way — match `pull/<n>)` on lines starting with `*`).\n\n### Patch (`X.Y.Z`, Z>0, cherry-picks on the release branch)\nThe previous patch tag is an ancestor of the release branch, so a plain range works **when the\ncherry-picks land as individual commits**:\n```\nGITHUB_TOKEN=$(gh auth token) git cliff <prev-patch-tag>..<release-branch-tip> --tag vX.Y.Z -o ...\n```\nNo extra subtraction (the branch only contains the new cherry-picks).\n\n**Common case — squashed pick commit.** Patch branches are often a single squashed commit like\n`chore: pick fixes and bump version to vX.Y.Z (#NNNN)`, so the plain range above yields just\nthat one bullet. First **find the picked PRs**: read the squash commit message\n(`git log -1 --format=%B <release-branch-tip>`) — each `* fix: ... (#NN)` line is a picked PR.\nThen pick one of two ways to build the changelog (a patch is usually only a few PRs — **ask the\nuser which they prefer**):\n\n- **(a) Generate on `main` and filter** — lets `git cliff` produce correctly formatted bullets\n  (titles, authors, categories):\n  1. Locate and order the picked PRs' commits on `main` chronologically:\n     ```\n     git log --oneline <remote>/main --grep='#NN1' --grep='#NN2' --reverse\n     ```\n     The first line is the oldest pick, and the last line is the newest pick.\n  2. Run cliff over a `main` range covering all of them (base = parent of the oldest picked\n     commit, tip = the newest), then keep only the picked `#NN` bullets:\n     ```\n     GITHUB_TOKEN=$(gh auth token) git cliff <oldest-pick>~1..<newest-pick> --tag vX.Y.Z -o /tmp/raw.md\n     ```\n     The raw output includes unrelated PRs in the main range that were **not** picked — drop\n     them, then rebuild contributors (§4).\n- **(b) Hand-write the bullets** — a fine fallback for a small patch (and when the API/network is\n  flaky). For each picked PR fetch its title + author (`gh api repos/GreptimeTeam/greptimedb/pulls/<n>`)\n  and write the `* <title> by [@user] in [#NN](...)` lines plus the contributor list yourself.\n\nIf the branch history is otherwise messy, the same \"identify the picked PRs and keep only those\"\nrule applies.\n\n### First pre-release (`X.Y.0-alpha.1`, cut from main)\nThe base is the commit where the previous line's release branch left `main`. It exists when the\nalpha is cut, even if `vX.(Y-1).0` does not:\n```\nbase=$(git merge-base <remote>/release/vX.(Y-1) <tag>)\n```\nIf there is no `release/vX.(Y-1)`, use the newest published release tag that is an ancestor of\nthe tag (`git merge-base --is-ancestor <candidate> <tag>`). Run `git cliff <base>..<tag>`, then\nsubtract every PR that already shipped in a published `vX.(Y-1).*` release, pre-releases\nincluded. Take the union of the PR links in those release bodies and the `(#N)` line endings in\nthe history of the newest published `vX.(Y-1).*` tag:\n```\ngh release view <published-vX.(Y-1)-tag> --repo GreptimeTeam/greptimedb | grep -oE 'pull/[0-9]+'\ngit log <newest-published-vX.(Y-1)-tag> --format=%B | grep -oE '\\(#[0-9]+\\)$'\n```\nEither source alone misses PRs: a release body can omit a backport that its tag contains, and a\nbackport commit can name only the backport PR.\n\n### Pre-release (`X.Y.Z-{alpha,beta,rc}.N` after an earlier pre-release)\n**The tip is the tagged commit, not the release branch tip.** Resolve it from the remote and\nwrite this SHA in the docs PR body (§8) as what the note was checked against:\n```\ngit ls-remote --tags <remote> 'vX.Y.Z-beta.N*'\n```\nUse the `refs/tags/vX.Y.Z-beta.N` line (its `^{}` line if the tag is annotated), not the\nbuild-suffixed tags it also lists. PRs that land on `release/vX.Y` after the tag\n(`git log --oneline <tag>..<remote>/release/vX.Y`) belong to the next release, even if they are\nalready merged when you write the note.\n\nThe range `<prev-pre-release>..<tag>` usually mixes two kinds of commits: individual backports\n(`... [Backport release/vX.Y] (#NNNN)`) and squashed pick commits (`chore: backport selected\nchanges for ... (#NNNN)`). In both, the original PRs are the `(#NN)` at the end of body lines;\na backport commit can also carry prerequisite PRs. Collect the PR set, falling back to the\nsubject for commits whose body lists none:\n```\nfor c in $(git rev-list <prev-pre-release>..<tag>); do\n  prs=$(git log -1 --format=%b \"$c\" | grep -oE '\\(#[0-9]+\\)$')\n  [ -n \"$prs\" ] || prs=$(git log -1 --format=%s \"$c\" | grep -oE '\\(#[0-9]+\\)$')\n  echo \"$prs\"\ndone | tr -dc '0-9\\n' | sort -un\n```\nList the bullets under the **original** PRs (title and author of the PR merged to `main`), not\nthe backport PRs. Build them with patch (a) or (b) above; for (b), fetch\n`gh api repos/GreptimeTeam/greptimedb/pulls/<n> --jq '{number,title,author:.user.login,merged_at}'`,\ngroup by the `cliff.toml` parsers (first match wins), order by `merged_at`, and put `!` titles\nunder Breaking changes (§6).\n\nA squashed pick PR can also contain release-only commits that have no PR. List them:\n```\ngh api repos/GreptimeTeam/greptimedb/pulls/<squash-pr>/commits --paginate \\\n  --jq '.[].commit.message | split(\"\\n\")[0]' | grep -vE '\\(#[0-9]+\\)$'\n```\nMechanical ones (version bump, toolchain or lint adaptations; example:\n`fix: adapt release OptionMap iteration to Rust 1.98`) get no bullet. A release-only\ncommit that changes behavior gets a bullet in its group.\n\n> Flaky-network note for (a): `git cliff` fetches the repo's full commit history from the GitHub\n> API for author enrichment (paginates back thousands of commits, even for a tiny range). On a\n> flaky connection it can time out mid-pagination; it caches pages, so just **re-run until it\n> completes** — each retry resumes from the cache. If it keeps failing, fall back to (b).\n\n### Groups (all paths)\n`git cliff` drops PRs whose title isn't a conventional commit (`filter_unconventional`). List\nthem and add the user-facing ones by hand:\n```\ngit log --format=%s <range> | grep -E '\\(#[0-9]+\\)$' | grep -vE '^[a-z]+(\\([^)]*\\))?!?: '\n```\nUse only the groups in `cliff.toml`. `git cliff` prints a `### Build` section for `build:` commits\nbecause no parser matches them; move a user-facing `build(deps)` fix to Bug Fixes and the rest to\nMiscellaneous Tasks. Keep each bullet list free of blank lines; a blank line turns it into a\nloose list that renders with different spacing.\n\n## 4. Rebuild contributor sections after subtracting\n\n`cliff` computes `New Contributors` / `All Contributors` over the full range, so after removing\nlines, recompute from the **remaining** commit bullets:\n- **All Contributors** = sorted set of `@user` from remaining `* ... by [@user] ... in [#NN]`\n  lines (drop bots like `dependabot[bot]`).\n- **New Contributors** = drop any entry whose first-contribution PR was removed (their only\n  in-range PR shipped in a patch).\nDo this in the same script that subtracts the PRs.\n\nWhen the bullets are built by hand (§3 (b), pre-releases), `git cliff` computes no New\nContributors, and GitHub's generated notes don't either (they only see the backport PRs). An\nauthor is new if none of their merged PRs shipped before, i.e. none appears in the history of the\nbase tag:\n```\ngit log <base-tag> --format=%B | grep -oE '\\(#[0-9]+\\)$' | tr -dc '0-9\\n' | sort -u > /tmp/shipped.txt\ngh search prs --repo GreptimeTeam/greptimedb --author <user> --merged --limit 1000 \\\n  --json number --jq '.[].number' | sort -u > /tmp/author.txt\ncomm -12 /tmp/author.txt /tmp/shipped.txt   # empty => new contributor\n```\nRun it for every non-bot author in the release, and cite their earliest-merged PR in this\nrelease. \"Earliest merged PR is in range\" is not the same test: a contributor whose first PR\nmerged on `main` but was not picked into any release is still new when a later PR of theirs\nships.\n\n## 5. Verify\n\nCross-check the result against `git log <base>..<tip>`: no subtracted PR remains, and no\ngenuine new PR was dropped. Optionally drop the mechanical `chore: bump version to vX.Y.Z`\nline (ask the user; some prefer to keep it).\n\n## 6. Human-curated sections (insert after the `Release date:` line)\n\n**Style.** Follow the Google developer documentation style guide: sentence-case headings, active voice, present\ntense for behavior, American spelling, serial comma, code font for identifiers, bold only for UI\nnames. Keep it short:\n- Intro: two sentences at most, what the release covers and its main theme.\n- Highlight: one paragraph of up to four sentences, plus an example if it earns one (see below).\n- Upgrade note or breaking change: one bullet, one or two sentences: what changes, who is\n  affected, what to do.\n- Every sentence states a fact the reader acts on or checks. Delete sentences that restate the\n  heading or the previous sentence, transitions, and summaries.\n- No marketing words (`significantly`, `powerful`, `seamless`, `robust`, `enhanced`), no vague\n  verbs (`supports`, `handles`, `improves`; say what happens), no `several`/`many` when the number\n  is known. Keep the source's hedges; don't add new ones.\n\nExample:\n> Bad: This release significantly enhances write reliability by introducing a robust retry\n> mechanism that seamlessly handles transient failures.\n>\n> Good: In distributed clusters, the frontend retries a failed write up to three times with\n> exponential backoff. Set `max_retries = 0` to disable retries.\n\nMirror past release bodies (`gh release view v1.0.0 --repo GreptimeTeam/greptimedb`):\n- **Short intro** (see Style).\n- **`### 👍 Highlights`** — *few, deep* highlights, ranked by user impact. Fixes for silently\n  wrong query results, lost or invisible data, and security issues rank with new features and\n  can be highlights.\n  - Write a fix as its symptom and the affected deployments or usage, not the PR title. Example:\n    \"with no traffic, `errors / requests > 0.05` was true, so the alert fired\".\n  - State whether a feature is on by default. If an example config uses the default values, say\n    so.\n  - Give an example (SQL / TOML config) only where it helps readers act. Don't pick highlights,\n    or the example, by what is easy to demonstrate.\n  - One highlight covers one change or one user-visible problem. Don't merge unrelated PRs into\n    one entry because they touch the same component.\n  - Sources: the PR body's sections on impact, compatibility, and behavior changes, plus the\n    code and config it touches, not the title alone. Verify examples against the related docs\n    PR/page in the docs repo (`GreptimeTeam/docs`, often checked out locally),\n    `docs/reference/sql/*.md`, `config/*.example.toml`, `config/config.md`.\n  Do **not** mention implementation details, tiny features, or unfinished/experimental-but-not-ready\n  features. Always have the user review and edit the highlights; iterate.\n- **Dashboard** subsection — don't just bump the version. Read the dashboard PRs in the bundled\n  release (`gh release view <ver> --repo GreptimeTeam/dashboard`; then the linked PRs) and\n  describe the user-facing change. Use the names the UI shows, not the PR titles; check them in\n  the dashboard's locale strings at the bundled tag:\n  ```\n  gh api 'repos/GreptimeTeam/dashboard/contents/src/locale/en-US/menu.ts?ref=<dashboard-tag>' \\\n    --jq .content | base64 -d\n  ```\n  Screenshots need absolute `https://` URLs: the body is copied to the GitHub release, where\n  relative and site-root (`/img/...`) paths don't resolve.\n- **`### Upgrade notes`** and **`### Breaking changes`** — check every PR in the release for:\n  `!` titles; `Compatibility`, `Behavior change`, `Breaking` paragraphs in the body (often a\n  plain `Compatibility:` line, not a heading); unchecked \"API changes are backward compatible\" /\n  \"Schema or data changes are backward compatible\" boxes; new config options and their defaults;\n  changed defaults; persisted format changes; rolling-upgrade order; new hard limits. These\n  signals set reading order, not a filter: a limit can sit in plain prose with every box\n  checked, so read the body of every user-facing `feat`/`fix` PR. Examples of what is easy to\n  miss: a 512 MiB cap on decompressed request bodies (#9264); a default step for stepless\n  subqueries (#9481).\n  - **Upgrade notes**: steps operators take — upgrade order, index rebuilds, settings to review.\n  - **Breaking changes**: behavior that used to work and now errors or returns different\n    results. Put the `!` PR bullets here too, after these entries (see `v1.3.0-beta.1`).\n- **Benchmark results** (optional) — state the baseline version (often the previous formal\n  release, not the previous beta), data size, cache state, and hardware. Don't attribute results\n  to single PRs. Leave out results the benchmark marks as not for publication, and smoke-level\n  runs.\n\n## 7. Output\n\nWrite to `CHANGELOG-vX.Y.Z.md` (title `# vX.Y.Z`, which `--tag` produces) and **do not commit\nit**. The release runbook deletes it after the release + docs PR are done.\n\n## 8. Docs-repo blog variant + draft PR\n\nThe release note is also published as a blog post in **`GreptimeTeam/docs`**.\n- **Ask the user for their local `GreptimeTeam/docs` checkout path**; if they have none, offer\n  to clone (`git clone git@github.com:GreptimeTeam/docs.git <path>`).\n- File: `blog/release-X-Y-Z.md` (version with dashes; see `blog/release-1-0-0.md`). Pre-releases\n  use dashes throughout: `blog/release-1-3-0-beta-2.md`.\n- Content = docs frontmatter (below) followed by the GitHub release body, **keeping the\n  `# vX.Y.Z` H1 directly under the frontmatter**. The blog frontmatter has no `title:`, so\n  Docusaurus uses that H1 as the page title and sidebar label — omitting it makes the page\n  render as the filename (e.g. `release-1-1-0` instead of `v1.1.0`). Match the shape of\n  `blog/release-1-0-0.md`:\n  ```\n  ---\n  keywords: [release, GreptimeDB, changelog, vX.Y.Z]\n  description: GreptimeDB vX.Y.Z Changelog\n  date: YYYY-MM-DD\n  ---\n  # vX.Y.Z\n\n  Release date: ...\n  ```\n  The docs CI check `check-release-note-sync` requires the post minus its frontmatter to equal\n  the GitHub release body byte for byte, so the H1 goes on the line right after the closing\n  `---`, with no blank line.\n- The docs working tree may have unrelated WIP — **don't disturb it**: use a `git worktree` off\n  `origin/main`:\n  ```\n  git -C <docs> fetch origin main\n  git -C <docs> worktree add -b chore/X.Y.Z-release-note /tmp/docs-release-note origin/main\n  ```\n- **Read and follow the current docs repo PR template**\n  (`.github/pull_request_template.md`) from the checkout used for the release.\n  Fill in every required section; do not rely on hard-coded section names from an older\n  template. The checklist items are the author's to confirm: check the ones you did, and for one\n  that doesn't apply, check it and say why. For pre-releases, state the tagged SHA (§3) under\n  Verification.\n- **Build the site before opening the PR.** A new worktree has no `node_modules`. Install with\n  the pnpm version pinned in `package.json` (`packageManager`), as CI does:\n  ```\n  cd /tmp/docs-release-note\n  pm=$(jq -r .packageManager package.json)   # e.g. pnpm@8.6.0\n  npx -y \"$pm\" install --frozen-lockfile\n  npx -y \"$pm\" build\n  ```\n  If the build fails, fix it or stop; don't mark the PR ready for review.\n- Commit with sign-off, push, open a **draft** PR, then remove the worktree:\n  ```\n  git -C /tmp/docs-release-note add blog/release-X-Y-Z.md\n  git -C /tmp/docs-release-note commit -s -m \"docs: add X.Y.Z release note\"\n  git -C /tmp/docs-release-note push -u origin chore/X.Y.Z-release-note\n  gh pr create --draft --repo GreptimeTeam/docs --base main --head chore/X.Y.Z-release-note \\\n    --title \"docs: add X.Y.Z release note\" --body-file <template-filled body>\n  git -C <docs> worktree remove /tmp/docs-release-note\n  ```\n- Gotcha: `gh pr edit/create` may fail with an org-scope error if the token lacks `read:org`.\n  Edit the body via REST instead: `gh api repos/GreptimeTeam/docs/pulls/<n> -X PATCH -F body=@body.md`.\n",".agents/skills/greptimedb-release/agents/openai.yaml":"interface:\n  display_name: \"GreptimeDB Release\"\n  short_description: \"Publish a GreptimeDB release\"\n  default_prompt: \"Use $greptimedb-release to publish the next GreptimeDB release and coordinate the changelog, GitHub release, CI follow-up, and docs PR.\"\n",".agents/skills/greptimedb-release-note/agents/openai.yaml":"interface:\n  display_name: \"GreptimeDB Release Notes\"\n  short_description: \"Generate GreptimeDB release notes\"\n  default_prompt: \"Use $greptimedb-release-note to generate the GreptimeDB changelog, curate highlights, and prepare the docs release-note PR.\"\n",".agents/skills/greptimedb-release/SKILL.md":"---\nname: greptimedb-release\ndescription: Runbook for publishing a new GreptimeDB version (tag + GitHub release + docs release-note PR) on the upstream GreptimeTeam/greptimedb repo. Use when asked to \"release\" / \"publish\" a GreptimeDB version (e.g. v1.1.0, v1.0.3).\n---\n\n# GreptimeDB Release Runbook\n\nPublish a formal GreptimeDB release on **`GreptimeTeam/greptimedb`**. Always operate against\nthat repo (for `gh`, pass `--repo GreptimeTeam/greptimedb`). Changelog generation is a\nseparate, involved task — use the **`greptimedb-release-note`** skill for it.\n\nThis whole flow touches public infrastructure. Confirm with the user before the\noutward-facing step (creating the release, which creates the tag and triggers CI).\n\n## Prerequisites & remote\n\n- Tools: `gh` (check with `gh auth status`) and `git`. Changelog generation additionally\n  needs `git cliff` and Python — see the `greptimedb-release-note` skill.\n- **Resolve the remote first.** This runbook writes `<remote>` for the git remote pointing\n  at `GreptimeTeam/greptimedb`; substitute your actual name (often `upstream`, sometimes\n  `origin` on a direct clone):\n  ```\n  git remote -v | grep -i 'GreptimeTeam/greptimedb' | awk '{print $1}' | head -1\n  ```\n\n## 0. Inputs: version and branch\n\n- Ask the user for the **version** to release (e.g. `1.1.0`, `1.0.3`) and, if needed, the\n  **branch**.\n- **Branch is inferred from version**: `MAJOR.MINOR` → `release/v<MAJOR>.<MINOR>`.\n  - `v1.0.0 / v1.0.1 / v1.0.2` live on `release/v1.0`; `v1.1.0` lives on `release/v1.1`.\n  - Infer it, then double-check with the user.\n- **New minor/major (`X.Y.0`)** is cut from `main`. If `release/vX.Y` does not exist on\n  the remote, offer to create it from the intended `main` commit (with the user's consent):\n  `git push <remote> <commit>:refs/heads/release/vX.Y`.\n- **Patch (`X.Y.Z`, Z>0)** must use the existing `release/vX.Y` branch (it carries\n  cherry-picked commits).\n- Check what exists on the remote: `git ls-remote --heads <remote> 'release/*'`.\n\n## 1. Verify the Cargo version\n\nThe workspace version on the release branch **must** equal the version being released, or\nstop. **Fetch the branch first**, then read `FETCH_HEAD` directly — `git fetch <remote>\nrelease/vX.Y` only *opportunistically* updates the remote-tracking ref\n`<remote>/release/vX.Y` (and only when the remote has a matching configured refspec), so it\nmay be missing or stale (e.g. a custom `<remote>`, or a branch just created in §0). Reading\n`FETCH_HEAD` always reflects the tip just fetched:\n```\ngit fetch <remote> release/vX.Y\ngit show FETCH_HEAD:Cargo.toml | grep -A30 '\\[workspace.package\\]' | grep -m1 version\n```\n(For a fresh minor cut from main, `<remote>/main` and `release/vX.Y` are usually the same\ncommit.)\n\n## 2. Generate and curate the changelog\n\nUse the **`greptimedb-release-note`** skill. It produces `CHANGELOG-vX.Y.Z.md` (uncommitted)\nand the docs-blog variant. **Review the highlights with the user and iterate** before\npublishing.\n\n## 3. Create the GitHub release (creates the tag + triggers CI)\n\n**Do NOT pre-create the tag.** Creating the release creates the tag and fires the\ntag-push CI (`.github/workflows/release.yml`) that builds all binaries (**~hours**).\nConfirm with the user, then:\n```\ngh release create vX.Y.Z \\\n  --repo GreptimeTeam/greptimedb \\\n  --target release/vX.Y \\\n  --title \"Release vX.Y.Z\" \\\n  --notes-file CHANGELOG-vX.Y.Z.md \\\n  --prerelease\n```\n- Title convention: `Release vX.Y.Z`.\n- Always create as **`--prerelease`**: prerelease here is just a \"build in progress\" marker.\n  The CI clears it on success (see §5). (The user creating it on the web works too.)\n- Verify: `gh release view vX.Y.Z --repo GreptimeTeam/greptimedb --json name,tagName,isPrerelease,draft,targetCommitish`.\n\n## 4. Open the docs release-note PR (do not wait for CI)\n\nRight after triggering the release, open the docs draft PR (see the docs section of the\n`greptimedb-release-note` skill). It only needs the finalized changelog.\n\nThen **delete the local `CHANGELOG-vX.Y.Z.md`** (its content now lives in the release body\nand the docs blog post).\n\n## 5. After the CI build finishes (~hours)\n\nCI's `publish-github-release` action, for a tag matching `^vX.Y.Z$`, runs\n`ncipollo/release-action` with `allowUpdates: true`, **`prerelease=false`,\n`makeLatest=true`, `omitBody=true`** (so it keeps our changelog body but finalizes the\nflags). These flags are described from the current `.github/workflows/release.yml` —\n**verify against the workflow** if behavior differs. Verify success:\n```\ngh release view vX.Y.Z --repo GreptimeTeam/greptimedb --json isPrerelease,assets\n```\n**Latest handling is conditional on whether this is the newest version:**\n- **Releasing the latest version** (e.g. v1.1.0 when nothing newer exists) → CI's finalized\n  state (non-prerelease, latest) is correct; **do nothing**.\n- **Releasing a non-latest line** (e.g. a patch `v1.0.3` while `v1.1.0` is already latest) →\n  set it back to non-latest after CI finishes:\n  `gh release edit vX.Y.Z --repo GreptimeTeam/greptimedb --latest=false`.\n\n## 6. Rollback (only on failure, with double confirmation)\n\nShow the user exactly what will be removed first; never delete blindly.\n```\ngh release delete vX.Y.Z --repo GreptimeTeam/greptimedb   # remove the release\ngit push <remote> :refs/tags/vX.Y.Z                        # remove the tag\n```\n\n## Conventions / gotchas\n\n- Remotes: `<remote>` is whatever points at `GreptimeTeam/greptimedb` (see Prerequisites).\n  `gh` defaults can be unreliable in a repo with many remotes — always pass\n  `--repo GreptimeTeam/greptimedb`.\n- Release title is always `Release vX.Y.Z`.\n- When reasoning about \"previous version\", skip nightly / build-suffixed tags\n  (`*-nightly-*`, `vX.Y.Z-rc.N-<sha>-<date>-*`).\n","AGENTS.md":"# AGENTS.md\n\nGuidance for coding agents (Claude Code, Codex, ...) and contributors working in\nthis repository. `CLAUDE.md` is a symlink to this file. If `.local/AGENTS.md` or\n`.local/CLAUDE.md` is present, you **MUST** read it as well — it holds personal\nor machine-local overrides (gitignored, not shared). To record a personal or\nmachine-local override, write it to `.local/AGENTS.md` (create `.local/` and the\nfile if absent), not to this shared file.\n\nGreptimeDB is an open-source, cloud-native observability database for unified\ncollection and analysis of metrics, logs, and traces. It is written in Rust and\nprovides sub-second querying at PB scale with high cost efficiency.\n\n## Core commands\n\n| Task | Command |\n| --- | --- |\n| Build (debug) | `make build` |\n| Build (release) | `make build RELEASE=true` |\n| Run standalone | `cargo run -- standalone start` |\n| Targeted Rust tests | `cargo nextest run -p <package>` (preferred over `cargo test`) |\n| Full local Rust suite | `make test` |\n| SQL tests | `cargo sqlness bare` (single case: `cargo sqlness bare -t <name>`) |\n| Format | `make fmt` (and `make fmt-toml` for TOML) |\n| Lint | `make clippy` (= `cargo clippy --workspace --all-targets --all-features -- -D warnings`) |\n| Type check | `make check` |\n\nToolchain: Rust (stable, pinned by `rust-toolchain.toml`), Protobuf compiler (>= 3.15), C/C++ build essentials.\nInstall the test runner with `cargo install cargo-nextest --locked`.\n\n## Repo map\n\nGreptimeDB is a Cargo workspace rooted at the repository; most crates live under\n`src/` (plus `tests-fuzz`, `tests-integration`, `tests/runner`). Key areas:\n\n- **Frontend / protocol**: `src/frontend/` (request orchestration), `src/servers/`\n  (wire protocols), `src/sql/` (SQL parsing)\n- **Storage engines**: `src/mito2/` (main time-series engine), `src/metric-engine/`\n  (metrics), `src/file-engine/`\n- **Coordination**: `src/meta-srv/` (metadata & cluster control), `src/meta-client/`\n- **Execution / statements**: `src/operator/` (DDL/DML, request conversion, procedures)\n- **Stream / transform**: `src/flow/` (continuous aggregation), `src/pipeline/`\n- **Query / index**: `src/query/`, `src/promql/`, `src/index/`\n- **Shared**: `src/common/`, `src/datatypes/`, `src/store-api/` (engine contract),\n  `src/catalog/`, `src/table/`\n\nBefore editing a path, read every `AGENTS.md` from the repository root down to\nthat path. The root guide always applies; a nested guide adds or overrides rules\nfor its subtree. Changes spanning multiple subtrees must follow every applicable\nguide.\n\n`AGENTS.md` files are maps, not manuals. Keep them short and stable: record\nwhere code lives, important boundaries or change-coupling, high-cost gotchas,\nand the validation entry point. Implementation details belong in code. Do not\ncopy exhaustive test lists or workflow logic; link to the source of truth.\n\nHigh-change areas and specialized test suites carry their own `AGENTS.md` with\na module map, read/write paths, change-coupling points, and gotchas:\n\n- [`src/common/meta/AGENTS.md`](src/common/meta/AGENTS.md)\n- [`src/query/AGENTS.md`](src/query/AGENTS.md)\n- [`src/servers/AGENTS.md`](src/servers/AGENTS.md)\n- [`src/operator/AGENTS.md`](src/operator/AGENTS.md)\n- [`src/mito2/AGENTS.md`](src/mito2/AGENTS.md)\n- [`src/metric-engine/AGENTS.md`](src/metric-engine/AGENTS.md)\n- [`src/flow/AGENTS.md`](src/flow/AGENTS.md)\n- [`src/frontend/AGENTS.md`](src/frontend/AGENTS.md)\n- [`src/meta-srv/AGENTS.md`](src/meta-srv/AGENTS.md)\n- [`tests-integration/AGENTS.md`](tests-integration/AGENTS.md)\n- [`tests/compatibility/AGENTS.md`](tests/compatibility/AGENTS.md)\n- [`tests/perf/AGENTS.md`](tests/perf/AGENTS.md)\n\n## Rust style\n\nFollow [`docs/style-guide.md`](docs/style-guide.md) for Rust style, module\ndesign, naming, and reuse guidance.\n\n## Read before changing code\n\n- [`.agents/architecture-invariants.md`](.agents/architecture-invariants.md) —\n  repo-wide rules that are easy to violate and expensive to get wrong (persisted/\n  wire format compatibility, crate layering, async runtimes, error handling,\n  feature gating, the DataFusion fork).\n- [`.agents/generated-files.md`](.agents/generated-files.md) — tool-generated\n  artifacts that must not be hand-edited (sqlness `.result`, `config/config.md`,\n  Grafana dashboards, proto).\n- [`docs/style-guide.md`](docs/style-guide.md) — code style.\n- [`CONTRIBUTING.md`](CONTRIBUTING.md) — contribution flow and CLA.\n\n## High-signal entry points\n\n- Main binary: `src/cmd/src/bin/greptime.rs`\n- Configuration: `src/common/config/`, example TOMLs in `config/`\n- Error handling: `src/common/error/` (`ErrorExt`, `StatusCode`)\n- Protocol implementations: `src/servers/src/`\n\n## Worktree safety\n\n- Check `git status --short` before editing. Preserve unrelated tracked changes\n  and untracked files; re-read files changed by another process.\n- Do not rewrite history, force-push, or remove files in bulk unless the user\n  explicitly requests it.\n- Update generated artifacts only through the generators documented in\n  [`.agents/generated-files.md`](.agents/generated-files.md).\n\n## Validation by change type\n\nUse the narrowest command that covers the change, then expand only when its\nblast radius requires it.\n\n| Change | Minimum validation |\n| --- | --- |\n| One Rust crate | `cargo nextest run -p <package>` |\n| Cross-workspace Rust behavior | `make test`; inspect `.github/workflows/rust.yml` when CI parity matters |\n| SQL parsing, planning, execution, or output | `cargo sqlness bare -t <case>`; inspect regenerated `.result` files |\n| Persisted metadata or wire format | Add/run a case under `tests/compatibility/`; follow its `README.md` and `AGENTS.md` |\n| Public configuration | Update example TOMLs, loading/serialization snapshots, and docs; run `make config-docs` |\n| Query regression harness or DSL | Follow `tests/perf/AGENTS.md` |\n| Enterprise-gated code | Build/test with `--features enterprise` where applicable and run `make check-enterprise-license` |\n\nFor import/export, COPY, or snapshot-storage changes, follow the Windows\nportability and validation guidance in\n[`tests-integration/AGENTS.md`](tests-integration/AGENTS.md).\n\n## Before opening a PR\n\n1. If you added a `.rs`, `.py`, or `.ts` file, apply and verify its license\n   header with `hawkeye format` followed by `hawkeye check`. Use the inception\n   year from `licenserc.toml`, not the current year.\n2. `make fmt`\n3. `make clippy`\n4. `make test`\n5. `make check-udeps` (run `make fix-udeps` if it reports unused dependencies).\n6. If you added or changed a public configuration option, update the applicable\n   example TOMLs, configuration-loading and serialized-config snapshot tests,\n   and related user-facing documentation. Run `make config-docs` (needs Docker)\n   and commit the regenerated `config/config.md`.\n7. If you changed a persisted or wire format, add a compatibility test case (see\n   `.agents/architecture-invariants.md`).\n8. If you added or gated an enterprise-only file, give it the enterprise license\n   header, list it in `licenserc-enterprise.toml` (`includes`) and\n   `licenserc.toml` (`excludes`), and run `make check-enterprise-license`.\n9. Use a conventional-commit title, sign off commits (`git commit -s`), and sign\n   the CLA.\n10. When creating or updating a pull request, follow\n   [`.github/pull_request_template.md`](.github/pull_request_template.md): include\n   the CLA statement, fill the change-intention section with enough detail, and\n   update checklist items accurately. \"This PR requires documentation updates\"\n   refers to the official docs site repository\n   ([`GreptimeTeam/docs`](https://github.com/GreptimeTeam/docs)), not to rustdoc\n   or comments in this repo. Check it only when that site would be wrong or\n   incomplete without a matching change; a change users cannot observe does not\n   need it.\n11. If the change must also land on release branches, add the matching\n   `backport-<target>` labels (e.g. `backport-v1.3` targets `release/v1.3`). The\n   Backport workflow (`.github/workflows/backport.yml`) cherry-picks a merged PR\n   onto each target branch and opens a backport PR automatically; on conflicts\n   it aborts and files an issue for a manual backport.\n\n## More\n\n- Agent skills and resources: [`.agents/`](.agents/) (see [`.agents/README.md`](.agents/README.md))\n- Architecture decisions: [`docs/rfcs/`](docs/rfcs/)\n- How-to guides: [`docs/how-to/`](docs/how-to/)\n",".agents/skills/greptimedb-development-docker-image/SKILL.md":"---\nname: greptimedb-development-docker-image\ndescription: Builds a development-only GreptimeDB Docker image from a local debug binary for local-cluster testing, and optionally pushes it to a development registry. Use when the user asks to package, build, tag, publish, or cross-build a non-release GreptimeDB or GreptimeDB Enterprise image for debugging.\ncompatibility: Requires Docker with Buildx for cross-platform builds; Podman is supported for native-platform builds.\nmetadata:\n  protocols: docker buildx podman\n  platforms: linux-amd64 linux-arm64\n---\n\n# GreptimeDB Development Docker Image\n\n## Goal\n\nPackage a locally built GreptimeDB binary into a **development-only** Docker\nimage for debugging and local-cluster testing, optionally push it to a\ndevelopment registry, and retain the non-secret image settings in `.env` for\nthe next build. It is **not** a release-image workflow and must not be used to\npublish a production or release artifact.\n\nThis skill follows the documented development-image build procedure:\n\n- Build `greptime` with Cargo's `nightly` profile.\n- Copy the target binary into the Docker build context as `./greptime`.\n- Build the supplied `Dockerfile`, which uses Ubuntu 24.04 only as the runtime\n  base image and exposes the `greptime` binary as its entrypoint.\n\n## Inputs\n\nCollect or discover the following before any build or push:\n\n| Input | Discovery and rule |\n| --- | --- |\n| Source repository | Treat the directory in which this skill is invoked as the default workspace. Include it as an editable field in the batch configuration; do not ask a separate confirmation. Do not assume open-source versus Enterprise. |\n| Edition and binary | Default to `greptime`; use the binary name the user requests for Enterprise builds. The copied Docker-context filename must always be `greptime`. |\n| Cargo profile and binary source | Include this in the batch configuration. Default to rebuilding `nightly`; reuse an existing binary only after the user explicitly accepts that its freshness is unverified. |\n| Target platform | Preselect `linux/amd64` unless Docker's server is `linux/arm64`, then preselect `linux/arm64`. Let the user override it in the batch configuration. Ubuntu 24.04 is the runtime base image, not a target-platform choice. Each image has exactly one target platform, but it may differ from the host platform. |\n| Build mode | Ask whether the user wants a locally loadable debug image or a registry push. |\n| Registry/repository | Read `IMAGE_REGISTRY` and `IMAGE_REPOSITORY` only from the selected workspace's `.env` and prefill them in the batch configuration. Example: `registry.example.com/team` + `greptimedb-dev`. |\n| Tag | Read `IMAGE_TAG` only from the selected workspace's `.env` and prefill it in the batch configuration. When the tag exists in the selected registry, preselect an incremented version. |\n\nUse the image reference `${IMAGE_REGISTRY}/${IMAGE_REPOSITORY}:${IMAGE_TAG}`.\nIf the registry is intentionally empty, omit its slash rather than producing a\nleading slash.\n\n## Interactive Workflow\n\nUse the platform's interactive prompt component for every question, selection,\nand confirmation. Do not ask an open-ended text question when a single-choice,\nmulti-select, or confirmation component can represent the decision. If the\nplatform does not provide an interactive component, use the equivalent numbered\nor lettered prompt below and wait for input before continuing.\n\n### Batch configuration\n\nMinimize user round trips: run the collector first against the invocation\ndirectory, then use one interactive form or batched prompt to collect workspace,\nedition/binary, profile/reuse-or-rebuild choice, target platform, build mode,\nregistry/repository, tag, and whether to inspect the configured registry tag.\nUse the invocation directory as the editable workspace default. Prefill\ncollector values and mark recommended defaults. If the user changes workspace,\nrerun the collector for that workspace without asking another configuration\nquestion. Only ask a follow-up if information is missing or invalid, or a safety\ngate is required. Treat unchanged prefilled values as accepted.\n\nKeep separate confirmation components only for elevated privileges: `sudo`,\npackage installation, and privileged QEMU setup. Use one final confirmation for\nall non-privileged selected work.\n\nAlways offer **Cancel** for an action that can write state, build, push, or\nrequire elevated privileges. Display the relevant preview before the user\nconfirms it: source/binary path, `file` architecture result, platform(s), image\nreference, build mode, and non-secret `.env` values.\n\n### Batch configuration fields\n\nUse single-choice controls inside the one batch form for mutually exclusive\nchoices:\n\n| Decision | Required choices |\n| --- | --- |\n| Cargo output | Rebuild `nightly` (recommended), reuse an existing binary only with a freshness-unverified acknowledgement, choose a profile/binary |\n| Target platform | `linux/amd64` (default unless Docker server is arm64), `linux/arm64` (default when Docker server is arm64). The selected image may be cross-built with Buildx. |\n| Build mode | Load locally (recommended), push to development registry |\n| Registry/repository | Use `.env` defaults, enter new values |\n| Tag | Use proposed increment when the configured tag exists, enter a version |\n\nWhen push is selected and the image configuration is complete, automatically\nrun the read-only registry-tag check. It may return `unknown`; do not treat that\nas a missing tag.\n\nUse one final confirmation for all selected non-privileged operations:\n\n- persist changed non-secret image settings to `.env`;\n- run the selected Cargo build or reuse the resolved binary;\n- prepare the Docker context;\n- build, verify, and, when selected, push the image.\n\nThe final confirmation must clearly state: **“This creates a development and\nlocal-cluster test image, not a release or production artifact.”**\n\nFor a real multi-select only, use lettered choices (`[A]`, `[B]`) and accept\n`all`, `none`, or `cancel`; otherwise use single-choice components.\n\n### Preflight context collection\n\nBefore asking the profile, registry, or tag questions, run the bundled,\nread-only collector. It works on macOS and Linux, does not invoke `cargo build`,\nand emits JSON that can be shown or summarized to the user:\n\n```bash\npython3 <skill-dir>/scripts/collect_context.py \\\n  --source <source-checkout> \\\n  --bin greptime \\\n  --profile nightly \\\n  [--target <rust-target>]\n```\n\nUse its report to identify:\n\n- missing `IMAGE_REGISTRY`, `IMAGE_REPOSITORY`, or `IMAGE_TAG` values in the\n  selected workspace's `.env`;\n- Cargo binary targets available in the workspace and whether the requested\n  `--bin` exists;\n- the resolved Cargo target directory, expected profile output path, and whether\n  that binary already exists; and\n- macOS/Linux host architecture plus Docker's server platform when Docker is\n  available.\n\nWhen push is selected and image configuration is complete, rerun the collector\nwith `--check-registry-tag` and all selected image values. It uses the selected\nengine's read-only manifest inspection and returns `exists` or `unknown`;\n`unknown` includes an unavailable registry, an absent tag, or missing\nauthentication and must not be treated as an absent tag.\n\n```bash\npython3 <skill-dir>/scripts/collect_context.py \\\n  --source <source-checkout> \\\n  --bin <binary> \\\n  --profile <profile> \\\n  --engine <docker|podman> \\\n  --registry <registry> \\\n  --repository <repository> \\\n  --tag <tag> \\\n  --check-registry-tag\n```\n\nWhen the status is `exists` and the tag ends in a number, preselect the\ncollector's `candidate_tag` as the next development tag. The user must still\nconfirm it before `.env` is updated or an image is built. Never use automatic\nincrementing as permission to overwrite or push an existing image.\n\nIf Cargo metadata or the requested binary target is unavailable, stop before\nbuilding and ask the user to correct the source path or binary target.\n\nThe profile/binary choice is a field in the batch configuration, not a separate\nquestion. Its default is the existing `nightly` output when present:\n\n```text\nQuestion: Which Cargo build output should package this image?\nOptions:\n- Rebuild nightly (Recommended): run Cargo with `--profile nightly`, then use its output.\n- Reuse nightly output: use the existing output only after showing its path, modification time, size, and matching platform; freshness is unverified.\n- Choose profile or binary: provide a different Cargo profile or an explicit binary path.\n```\n\nDo not infer that a binary under `target/debug`, `target/release`, or another\nprofile is suitable. For an existing binary, show its resolved path and run\n`file` before asking for final build confirmation.\n\n## Environment State\n\nThe image configuration lives only in the selected workspace's `.env`. The\ncollector reads only the following keys and never reads or displays other `.env`\nvalues:\n\n```dotenv\nIMAGE_REGISTRY=registry.example.com/team\nIMAGE_REPOSITORY=greptimedb-dev\nIMAGE_TAG=dev-001\n```\n\n1. Read `.env` if it exists. Ignore blank lines and `#` comments.\n2. Show the discovered registry, repository, and proposed tag to the user.\n3. Include registry/repository and tag in the batch configuration even when\n   values already exist; saved values are defaults, not authorization to reuse\n   them.\n4. Compare the selected configuration with the three current managed values. If\n   all match, skip `.env` confirmation and do not update the file. Otherwise,\n   preview only the changed `IMAGE_*` values, request confirmation, and update\n   the file while preserving unrelated entries and comments.\n5. Never put registry credentials, access tokens, passwords, or `docker login`\n   output in `.env`, build commands, logs, or responses. Ask the user to log in\n   themselves if a push needs authentication.\n\nFor a repeated build, offer an incremented tag before asking. Use\n`scripts/next_image_tag.py --tag <existing-tag>` to calculate it. It increments\nthe final numeric component while preserving zero padding:\n\n```text\nweny-2025-0715-01 -> weny-2025-0715-02\nv0.1.4 -> v0.1.5\ndebug-009 -> debug-010\n```\n\nIf the saved tag has no trailing number, ask the user for the next tag rather\nthan inventing a versioning convention. Do not overwrite an existing image tag\nwithout explicit user confirmation.\n\nOnly if a managed value is missing or differs, persist the values with the\nbundled helper after confirmation instead of hand-editing the file:\n\n```bash\npython3 <skill-dir>/scripts/update_image_env.py \\\n  --env <source-checkout>/.env \\\n  --registry <registry> \\\n  --repository <repository> \\\n  --tag <tag>\n```\n\n## Build Procedure\n\n### 1. Inspect the host and container tooling\n\nRun these checks and report the selected path:\n\n```bash\nuname -s\nuname -m\ndocker version --format '{{.Server.Os}}/{{.Server.Arch}}'\ndocker buildx version\ndocker buildx inspect --bootstrap\n```\n\nFor a native-platform build, Podman can be used if Docker is unavailable. For\nany non-native request, require Docker Buildx. Do not silently fall back to a\nnative build when the requested image platform differs.\n\nIf the requested target is unavailable, stop and ask the user to configure a\nBuildx builder and cross-compilation toolchain externally. Do not automate\nprivileged QEMU or binfmt setup, and never run an unpinned privileged image.\n\n### 2. Build the executable for the image platform\n\nFrom the source checkout, build the requested binary. For open-source amd64:\n\n```bash\n<skill-dir>/scripts/build_binary.sh \\\n  --source <source-checkout> --package cmd --bin greptime --profile nightly\n```\n\nFor open-source arm64:\n\n```bash\n<skill-dir>/scripts/build_binary.sh \\\n  --source <source-checkout> \\\n  --package cmd \\\n  --bin greptime \\\n  --profile nightly \\\n  --target aarch64-unknown-linux-gnu\n```\n\nFor Enterprise, use the user-provided executable target, for example:\n\n```bash\n<skill-dir>/scripts/build_binary.sh \\\n  --source <source-checkout> --bin greptime-ent-cloud --profile nightly\n```\n\nFor a user-selected profile, replace `nightly` in the helper call and resolve\nthe binary under `target/<profile>/<binary>` (or\n`target/<rust-target>/<profile>/<binary>` for cross-compilation). Reuse the\nexisting target file only when the interactive profile question selected reuse;\notherwise invoke the helper to rebuild it.\n\nDo not use a binary compiled for the host architecture in an image intended for\nanother architecture. Determine the binary path from the Cargo target and\nprofile, then copy it into the Docker context as `greptime`:\n\n```bash\ncp <source-target-binary> <build-context>/greptime\n```\n\nVerify it before building the image with `file <build-context>/greptime`; its\nreported architecture must match the requested target platform.\n\nCreate a fresh isolated build context (never use the repository root) and use\nthe bundled preparation script. The helper rejects an existing Dockerfile or\nbinary output to prevent accidental context reuse:\n\n```bash\n<skill-dir>/scripts/prepare_context.sh \\\n  --context \"$(mktemp -d)\" \\\n  --binary <source-target-binary> \\\n  --dockerfile <skill-dir>/assets/Dockerfile \\\n  --platform <platform>\n```\n\n### 3. Build or push the image\n\nRun the command only after the user confirms the final image reference and\nwhether it should be pushed.\n\nUse the bundled build helper rather than spelling out individual Docker or\nPodman commands. It follows the existing scripts' Docker-first/Podman-fallback\nbehavior, uses Buildx for a non-native Docker request, and accepts exactly one\ntarget platform per image:\n\n```bash\n<skill-dir>/scripts/build_image.sh \\\n  --context <build-context> \\\n  --image <image-reference> \\\n  --platform <platform> \\\n  --mode local|push\n```\n\n`--mode local` builds a native image locally or uses `docker buildx --load` for\na non-native image. `--mode push` pushes the selected single-platform image.\nThe helper never pushes in `local` mode.\n\nThis is intentionally a runtime image built from a precompiled binary, not a\nmulti-stage Dockerfile. Ubuntu 24.04 is the runtime base image only; select one\ntarget platform separately as `linux/amd64` or `linux/arm64`. Only introduce a\nmulti-stage Dockerfile if the user asks to compile within Docker or the local\nRust toolchain cannot build the required target. Keep the final Ubuntu 24.04\nruntime stage and existing entrypoint unless the user asks to change runtime\nbehavior.\n\nAt the final confirmation, repeat that the selected reference is a\n**development/local-cluster test image**, not a release artifact. If the user\nrequests a release or production image, stop and direct them to the release\nprocess instead.\n\n### 4. Verify the result\n\nFor a local image, use the selected engine to inspect its architecture and\nstart it with `--help`:\n\n```bash\ndocker image inspect <image-reference> --format '{{.Os}}/{{.Architecture}}'\ndocker run --rm <image-reference> --help\n```\n\nFor Podman, use `podman image inspect <image-reference>` and\n`podman run --rm <image-reference> --help`. For a pushed Podman image, use\n`podman manifest inspect <image-reference>`.\n\nFor a pushed image, inspect its remote manifest:\n\n```bash\ndocker buildx imagetools inspect <image-reference>\n```\n\nReport the final image reference, target platform, source binary path, and\nwhether the image was loaded locally or pushed.\n\n## Safety Rules\n\n- This skill is only for development and local-cluster testing. Never present\n  its image as a production, release, or officially published artifact.\n- Never push by default. Local debug builds should use a local tag and load the\n  image unless the user explicitly requests a push.\n- Do not run `docker login`, privileged QEMU setup, package installation, or\n  overwrite a tag without confirmation.\n- Do not delete existing images, builders, binaries, or `.env` entries as part\n  of this workflow.\n- Stop and explain if Docker's server is unavailable, Buildx cannot support the\n  requested platform, or the copied binary architecture does not match the\n  image platform.\n",".agents/skills/greptimedb-fuzz-ci-failure-investigation/SKILL.md":"---\nname: greptimedb-fuzz-ci-failure-investigation\ndescription: Investigate a failed GreptimeDB fuzz CI target link by downloading GitHub Actions job logs plus fuzz artifacts such as kind logs, monitor dumps, and CSV dumps, then correlate the failure with local GreptimeDB source code. Use when the user provides a failed fuzz CI target/job URL or asks to diagnose GreptimeDB fuzz CI failures.\n---\n\n# GreptimeDB Fuzz CI Failure Investigation\n\nInvestigate failed fuzz-test jobs for **`GreptimeTeam/greptimedb`** from a local\ncheckout. The purpose is to download the failed job's CI output and fuzz artifacts,\nthen explain the likely cause by correlating the evidence with GreptimeDB source.\n\nAlways pass `--repo GreptimeTeam/greptimedb` to `gh`; local remotes may point to\nforks. Keep the workflow read-only: do not rerun jobs, cancel workflows, push,\ncomment on PRs, or delete artifacts unless the user explicitly asks.\n\n## Scope\n\nUse this skill for fuzz CI only. In `.github/workflows/integration.yml`, fuzz failures\nare the CI jobs that use `.github/actions/fuzz-test`:\n\n- `fuzztest` — standalone fuzz targets.\n- `unstable-fuzztest` — unstable standalone fuzz target.\n- `distributed-fuzztest` — distributed cluster fuzz targets.\n- `distributed-fuzztest-with-chaos` — distributed fuzz targets with Chaos Mesh.\n\nEach matrix job runs a semantic group of targets on one prepared environment.\nThe group defaults to fail-fast: after the first target failure it captures that\ntarget's diagnostics and marks the remaining targets as skipped. The workflow\nstrategy still uses `fail-fast: false`, so failures do not cancel other groups.\n\nThe reusable action `.github/actions/fuzz-test/action.yaml` delegates each target\nto `.github/scripts/run-fuzz-targets.sh`. CI passes `FUZZ_BIN_DIR`, so the script\nruns the prebuilt target executable:\n\n```bash\n<prebuilt-fuzz-binary> -max_total_time=<seconds> \\\n  -artifact_prefix=<target-dir>/libfuzzer/\n```\n\nWithout `FUZZ_BIN_DIR`, such as during local reproduction, the script falls back\nto `cargo fuzz run <target> --fuzz-dir tests-fuzz -D -s none` with the same\nlibFuzzer arguments.\n\nFailed jobs upload one group artifact. Its stable name identifies the job kind,\nmode, and group, for example:\n\n```text\nfuzz-distributed-remote-wal-database-and-regular-table\n```\n\nThe artifact contains `manifest.json`, `summary.md`, and one directory per target\nunder `targets/<target>/`. A failed distributed target can include `fuzz.log`,\nlibFuzzer reproducers, CSV/SQL traces, Kind logs, monitor dumps, and Kubernetes\nstate. Setup failures use `targets/setup/`.\n\n## Inputs\n\nThe normal user input is a **CI target link**: a GitHub Actions job URL for one\nfailed matrix target. Treat that URL as the primary source of truth. Example:\n\n```text\nhttps://github.com/GreptimeTeam/greptimedb/actions/runs/29000666156/job/86062894741\n```\n\nAlso accept these fallback inputs:\n\n- A workflow run URL, e.g.\n  `https://github.com/GreptimeTeam/greptimedb/actions/runs/29000666156`.\n- A run id plus a fuzz target, mode name, artifact name, PR, branch, or commit SHA.\n\nParse ids from URLs:\n\n```bash\nREPO=GreptimeTeam/greptimedb\nRUN_ID=29000666156\nJOB_ID=86062894741\n```\n\nWhen the input is a job URL, do not ask for the target name first. Use the job id\nto fetch metadata, then derive the group and mode from the job name. A distributed\nfuzz job name usually contains the CI group tuple:\n\n```text\nFuzz Test (Distributed, <mode>, <group>)\nFuzz Test with Chaos (Distributed, <mode>, <group>)\n```\n\nIf only a run URL is provided, list failed jobs and choose the fuzz job matching the\ngroup/mode. Ask only when multiple fuzz jobs are plausible and there is no group,\nmode, artifact, or job-url clue.\n\n## Prerequisites\n\n- `gh` must be installed and authenticated (`gh auth status`). Actions logs and\n  artifacts commonly require authentication even for a public repo.\n- `jq`, `unzip`, and standard shell tools are useful for reducing logs.\n- Use the local GreptimeDB checkout for source analysis. If the run's head SHA is\n  not the current checkout, inspect the code at the run's SHA before making claims.\n- Use a scratch directory outside the source tree, such as `/tmp/greptimedb-fuzz-ci`.\n\n## 1. Capture run and job metadata\n\nCreate a scratch directory:\n\n```bash\nmkdir -p /tmp/greptimedb-fuzz-ci/$RUN_ID\ncd /tmp/greptimedb-fuzz-ci/$RUN_ID\n```\n\nFetch run metadata:\n\n```bash\ngh run view \"$RUN_ID\" --repo \"$REPO\" \\\n  --json databaseId,name,displayTitle,event,headBranch,headSha,status,conclusion,createdAt,updatedAt,url,workflowName \\\n  > run.json\n```\n\nFetch all jobs through the REST API:\n\n```bash\ngh api \"repos/$REPO/actions/runs/$RUN_ID/jobs?per_page=100\" \\\n  --paginate --jq '.jobs[]' \\\n  | jq -s '{jobs: .}' > jobs.json\n```\n\nList failed fuzz-like jobs:\n\n```bash\njq -r '\n  .jobs[]\n  | select(.conclusion != null and .conclusion != \"success\")\n  | select(.name | test(\"Fuzz Test|fuzz\"; \"i\"))\n  | [.id, .name, .status, .conclusion, .html_url] | @tsv\n' jobs.json\n```\n\nFor a known `JOB_ID`, capture job name and step outcomes:\n\n```bash\njq -r --arg id \"$JOB_ID\" '\n  .jobs[] | select((.id | tostring) == $id) |\n  \"job=\\(.name) conclusion=\\(.conclusion) url=\\(.html_url)\",\n  (.steps[]? | [.number, .name, .status, .conclusion, .started_at, .completed_at] | @tsv)\n' jobs.json > job-summary.txt\n```\n\nRecord the fuzz job kind, matrix mode, group, failed step, event type, branch, and\nhead SHA. Do not assume `main` for PR or merge-queue runs. The failed target is\nauthoritative in the downloaded artifact's `manifest.json`; do not guess it from\nthe group name.\n\n## 2. Download CI output for the failed job\n\nDownload the selected job's log; this is the authoritative target CI output for the\n`cargo fuzz run` command:\n\n```bash\ngh run view \"$RUN_ID\" --repo \"$REPO\" --job \"$JOB_ID\" --log > job-$JOB_ID.log\n```\n\nDownload the full workflow log only when setup/build context matters:\n\n```bash\ngh run view \"$RUN_ID\" --repo \"$REPO\" --log > run-$RUN_ID.log\n```\n\nFallback if `gh run view --log` is incomplete. The job logs endpoint returns a\nplain text stream, while the full run logs endpoint returns a zip archive:\n\n```bash\ngh api \"repos/$REPO/actions/jobs/$JOB_ID/logs\" > job-$JOB_ID.log\ngh api \"repos/$REPO/actions/runs/$RUN_ID/logs\" > run-$RUN_ID-logs.zip\nunzip -oq run-$RUN_ID-logs.zip -d run-logs\n```\n\nIn the job log, inspect the `Run Fuzz Test` step first. Capture:\n\n- exact target name;\n- `max_total_time`;\n- panic/assertion/backtrace or libFuzzer crash output;\n- generated crash/reproducer path if printed;\n- timeout or resource symptoms.\n\n## 3. Find and download fuzz artifacts\n\nList artifacts:\n\n```bash\ngh api \"repos/$REPO/actions/runs/$RUN_ID/artifacts?per_page=100\" \\\n  --paginate --jq '.artifacts[]' \\\n  | jq -s '{artifacts: .}' > artifacts.json\njq -r '.artifacts[] | [.id, .name, .expired, .size_in_bytes] | @tsv' artifacts.json\n```\n\nMatch the one group artifact by job kind, mode slug, and group slug. If the display\nname does not make the slug obvious, list the run artifacts and choose the unique\n`fuzz-*` artifact whose suffix matches the group. Do not search for separate Kind,\nmonitor, or CSV artifacts; these are target subdirectories in the group artifact.\n\nDownload exact artifacts when possible:\n\n```bash\nARTIFACT='fuzz-distributed-remote-wal-database-and-regular-table'\ngh run download \"$RUN_ID\" --repo \"$REPO\" --name \"$ARTIFACT\" --dir artifacts/\"$ARTIFACT\"\n```\n\nFallback by artifact id:\n\n```bash\nARTIFACT_ID=$(jq -r --arg name \"$ARTIFACT\" '.artifacts[] | select(.name == $name and (.expired | not)) | .id' artifacts.json | head -n 1)\nif [ -z \"$ARTIFACT_ID\" ]; then\n  echo \"Artifact not found or expired: $ARTIFACT\"\n  exit 1\nfi\ngh api \"repos/$REPO/actions/artifacts/$ARTIFACT_ID/zip\" > artifact-$ARTIFACT_ID.zip\nmkdir -p artifacts/\"$ARTIFACT\"\nunzip -oq artifact-$ARTIFACT_ID.zip -d artifacts/\"$ARTIFACT\"\n```\n\nAfter download, read the manifest before the bulk logs:\n\n```bash\njq -r '.[] | [.target, .status, .exit_code, .after_prior_failure, .artifact_collection] | @tsv' \\\n  artifacts/\"$ARTIFACT\"/manifest.json\n```\n\nSelect the first `failure` entry as the primary target. Entries with\n`skipped_after_failure` did not run; entries with `after_prior_failure=true` ran\nagainst a potentially contaminated shared environment. Note expired or missing\nartifacts explicitly; retention is currently short (`retention-days: 3`).\n\n## 4. Understand artifact contents\n\nEvidence for target `<target>` is rooted at\n`artifacts/$ARTIFACT/targets/<target>/`. `result.json` records timestamps, status,\nexit code, fuzz budget, prior-failure provenance, and artifact collection status.\n`fuzz.log` is the complete libFuzzer output from the prebuilt target executable in\nCI; local fallback runs it through `cargo fuzz run`.\n\nKind logs under `targets/<target>/kind/` come from an immediate failure snapshot\nand usually include Kubernetes state plus container logs. Prioritize:\n\n- GreptimeDB component logs: frontend, datanode, metasrv, flownode, monitor.\n- Kubernetes pod descriptions/events: restarts, OOMKilled, scheduling, image pull,\n  volume, DNS, and network issues.\n- External service logs: etcd, Kafka, Minio, Chaos Mesh, Kafka WAL helper.\n\nMonitor dumps under `targets/<target>/monitor/` are collected by\n`.github/scripts/collect-fuzz-monitor-artifacts.sh`. They may include:\n\n- `state.log`, `sql.log`, `copy.log`, `port-forward.log`;\n- `*.show_create_table.sql` for `_gt_logs` and OpenTelemetry trace tables;\n- parquet dumps for `_gt_logs`, `opentelemetry_traces`,\n  `opentelemetry_traces_operations`, and `opentelemetry_traces_services`.\n\nCSV and SQL traces are under `targets/<target>/csv/`. LibFuzzer crash or timeout\nreproducers are under `targets/<target>/libfuzzer/`. Kubernetes descriptions and\nevents are under `targets/<target>/kubernetes/`. Standalone service logs are under\n`targets/<target>/service/` when available.\n\n## 5. Build a layered diagnosis model\n\nBefore drilling into a specific error string, build a small macro-level model of\nthe failure. This prevents overfitting on the loudest symptom, such as\n`libFuzzer: deadly signal`, `Internal error`, or a cleanup warning.\n\n### 5.1 Create a failure timeline\n\nRecord the key timestamps in order:\n\n- job start and setup steps;\n- external dependencies ready, such as etcd, Kafka, Minio, and Chaos Mesh;\n- GreptimeDB cluster ready;\n- fuzz binary starts;\n- first generated operation or SQL visible in the target log;\n- first suspicious component log line;\n- user-visible fuzz failure or panic;\n- artifact collection and cleanup.\n\nUse the timeline to classify the failure phase: setup, workload execution,\nstorage/WAL/write path, query path, cleanup, or runner infrastructure.\n\n### 5.2 Trace the error propagation chain\n\nFor distributed fuzz failures, identify the chain from outer symptom to inner\ncause candidate. Prefer this shape:\n\n```text\nfuzz panic/assertion\n  -> client-visible SQL/gRPC/HTTP error\n  -> frontend or monitor error\n  -> meta/datanode/flownode component error\n  -> storage/WAL/object-store/Kafka/etcd/k8s dependency error\n```\n\nAt each boundary, decide whether that layer produced the error or merely wrapped\nand forwarded a downstream error. Do not stop at wrapper errors unless there is no\ndeeper evidence.\n\n### 5.3 Investigate by component boundary\n\nFor distributed targets, scan evidence in this order unless the timeline points\nelsewhere:\n\n1. fuzz target and generated operations;\n2. frontend request handling and SQL/error response;\n3. metasrv metadata/procedure/routing;\n4. datanode/mito2/metric-engine region execution;\n5. WAL, Kafka, object store, Minio, etcd, or other dependencies;\n6. Kubernetes pod state, restarts, scheduling, DNS, network, and runner resource\n   signals.\n\nFor each layer, answer two questions: \"is this the first causal error?\" and \"is\nthis layer producing or propagating the failure?\"\n\n### 5.4 Verify intended config against runtime behavior\n\nWhen a failure appears configuration-related, compare all three levels before\nclaiming a config did or did not take effect:\n\n1. the workflow/action/Helm/YAML config at the tested SHA;\n2. the actual pod/container runtime config or startup logs, if available;\n3. the source path that consumes the config, including hard-coded defaults and\n   library retry/backoff settings.\n\nIf a logged value differs from the expected config, explicitly state whether the\nconfig was not applied, was overridden by another layer, or refers to a different\nkind of timeout/retry/deadline.\n\n### 5.5 Keep competing hypotheses until evidence rules them out\n\nBefore final classification, list at least two plausible causes and the evidence\nthat supports or weakens each, for example:\n\n- product bug;\n- fuzz/test invalid assumption or missing wait;\n- config not applied or overridden;\n- dependency/infra flake;\n- timeout, retry, or resource pressure.\n\nThen choose the most likely cause and keep the confidence scoped. It is often\nvalid to have high confidence in the observed error chain but only medium\nconfidence in the ultimate cause category.\n\n### 5.6 State what would disprove the diagnosis\n\nInclude one or two falsifiable checks, such as a same-SHA rerun, a fixed\nreproducer, a cross-target pattern in the same dependency mode, or one missing\nartifact/log that would change the conclusion.\n\n## 6. Reduce evidence before reading source\n\nExtract high-signal lines from job logs and artifacts:\n\n```bash\ngrep -RInE '(^|[^[:alpha:]])(ERROR|Error|WARN|panic|panicked|FAILED|failure|timeout|Timeout|assert|backtrace|SIG[A-Z]+|OOMKilled|CrashLoopBackOff|ImagePullBackOff|libFuzzer|AddressSanitizer|UndefinedBehaviorSanitizer)' \\\n  job-$JOB_ID.log run-$RUN_ID.log job-logs run-logs artifacts 2>/dev/null > signals.txt\n```\n\nThen identify the first causal failure, not the last cleanup error. A typical order:\n\n1. `cargo fuzz run` output in `job-$JOB_ID.log`.\n2. GreptimeDB component logs around the same timestamp.\n3. Kubernetes events/restarts/OOMs.\n4. Monitor/CSV dumps that show the generated SQL or cluster state.\n\nAvoid common mistakes:\n\n- Do not blame cleanup errors after the fuzz failure.\n- Do not treat expected retry warnings as root cause unless they align with the\n  assertion, crash, or timeout.\n- Do not assume an artifact is relevant only because it contains `fuzz`; match mode\n  and target.\n- Do not diagnose from current local `main` if the run used a different SHA.\n\n## 7. Inspect the tested source revision\n\nRead the tested SHA:\n\n```bash\nHEAD_SHA=$(jq -r '.headSha // empty' run.json)\nif [ -z \"$HEAD_SHA\" ]; then\n  echo \"Error: HEAD_SHA is empty or null\"\n  exit 1\nfi\n\nUPSTREAM_REMOTE=$(git remote -v \\\n  | awk '/GreptimeTeam\\/greptimedb(\\.git)?\\/?([[:space:]])/ { print $1; exit }')\nUPSTREAM_REMOTE=${UPSTREAM_REMOTE:-origin}\n\ngit rev-parse --verify \"$HEAD_SHA^{commit}\" >/dev/null 2>&1 \\\n  || git fetch \"$UPSTREAM_REMOTE\" \"$HEAD_SHA\"\n```\n\nIf local `HEAD` differs, inspect files at that SHA or create a temporary worktree:\n\n```bash\ngit worktree add --detach /tmp/greptimedb-fuzz-ci/$RUN_ID/source \"$HEAD_SHA\"\n```\n\nRemove temporary worktrees after investigation with `git worktree remove <path>`.\nNever overwrite the user's current branch.\n\n## 8. Map logs to GreptimeDB code\n\nStart from concrete strings: fuzz target name, panic text, assertion message,\nerror variant, SQL statement, or log message. Search/read the matching source at\nthe run SHA.\n\nUseful entry points:\n\n- `tests-fuzz/` — fuzz targets, operation generators, checkers, dump paths.\n- `.github/actions/fuzz-test/action.yaml` — exact CI fuzz runner configuration.\n- `.github/workflows/integration.yml` — fuzz matrix and artifact upload names.\n- `.github/scripts/collect-fuzz-monitor-artifacts.sh` — monitor dump contents.\n- `src/mito2/AGENTS.md` — storage/WAL/region failures.\n- `src/metric-engine/AGENTS.md` — metric/logical table failures.\n- `src/frontend/AGENTS.md` — SQL/protocol/query orchestration failures.\n- `src/meta-srv/AGENTS.md` — metadata/procedure/routing failures.\n- `src/flow/AGENTS.md` — flow failures.\n\nWhen reporting code evidence, cite the file/function/line and quote the log line\nthat reaches it. Separate confirmed facts from hypotheses.\n\n## 9. Classify the cause\n\nUse this taxonomy:\n\n- **Product bug**: GreptimeDB panics, returns wrong results, loses data, or violates\n  invariants under generated fuzz operations.\n- **Fuzz/test bug**: the fuzz target/generator/checker makes an invalid assumption\n  or fails to wait for an asynchronous condition it requires.\n- **Flake/race**: timing/order/resource issue, usually supported by timeouts,\n  retries, pod restarts, or prior similar failures.\n- **Environment/infra**: runner disk/OOM, Docker/kind, network, dependency install,\n  Kafka/Minio/etcd startup, or GitHub service issue.\n- **Unknown**: logs/artifacts are expired or evidence conflicts.\n\n## 10. Final response format\n\nUse this structure:\n\n```markdown\n## Summary\n- Likely cause: <one sentence>\n- Confidence: high|medium|low\n- Classification: product bug|fuzz/test bug|flake/race|environment/infra|unknown\n- Confidence basis: <which parts are confirmed vs inferred>\n\n## Evidence\n- `<job log or artifact path>`: quoted line(s)\n- `<source-file:line>`: relevant code path\n\n## Reasoning\n<short chain from fuzz target -> CI output -> artifact logs -> source code>\n\n## Alternative hypotheses\n- <hypothesis>: <supporting and weakening evidence>\n- <hypothesis>: <supporting and weakening evidence>\n\n## Next steps\n1. <minimal fix, reproduction command, or verification command>\n2. <rerun command or extra log needed, if any>\n\n## What would disprove this\n- <specific rerun, artifact, log line, or reproducer result that would change the diagnosis>\n```\n\nIf logs or artifacts cannot be downloaded because `gh` is not authenticated or the\nartifacts expired, say so directly and include the exact command/error.\n"},"files":{".agents/skills/greptimedb-release-note/SKILL.md":"---\nname: greptimedb-release-note\ndescription: Generate a GreptimeDB release changelog (minor, patch, or alpha/beta/rc pre-release) with git cliff (correct range, subtract already-released patch PRs, rebuild contributors, add human-curated highlights and upgrade notes), output to a file, and prepare the docs-repo blog PR. Use when asked to write/generate a GreptimeDB release note or changelog.\n---\n\n# GreptimeDB Release Note / Changelog\n\nGenerate the changelog for a GreptimeDB release. Tooling: **`git cliff`** (config:\n`cliff.toml` in the greptimedb repo) and **`gh`**. Run inside the greptimedb checkout.\n\n**Prerequisites:** `git cliff`, `gh` (check `gh auth status`), and **Python** (the\nsubtract-PRs / rebuild-contributors step in §3–§4 is scripted). This skill writes\n`<remote>` for the git remote pointing at `GreptimeTeam/greptimedb` (often `upstream`,\nsometimes `origin`); resolve it with\n`git remote -v | grep -i 'GreptimeTeam/greptimedb' | awk '{print $1}' | head -1`.\n\n## 1. GitHub token (never print it)\n\n`git cliff` enriches commits with PR titles/authors via the GitHub API; for hundreds of\ncommits you need a token or you hit rate limits. Pass it inline and **never read or echo the\ntoken**:\n```\nGITHUB_TOKEN=$(gh auth token) git cliff ...\n```\nAlternatively the user sources an env file that exports `GITHUB_TOKEN` (don't read it). Without\na token it still runs, but may be rate-limited / incomplete.\n\n## 2. Determine the previous version (skip nightlies)\n\nFrom `gh release list --repo GreptimeTeam/greptimedb`, ignore `*-nightly-*` and build-suffixed\ntags (e.g. `v1.3.0-alpha.1-3c0e2a8d5-20260928-1790639248`).\n- Patch `vX.Y.Z` (Z>0): previous = `vX.Y.(Z-1)`.\n- New minor `vX.Y.0`: previous = the latest `vX.(Y-1).*` (for `v1.0.0`, the biggest `0.x`).\n- Pre-release `vX.Y.Z-{alpha,beta,rc}.N`: previous = the latest pre-release tag of the same\n  `vX.Y.Z` (e.g. `v1.3.0-beta.1` for `v1.3.0-beta.2`). If there is none (the first alpha),\n  see \"First pre-release\" in §3: the previous minor's `.0` may not be published yet, and since\n  v1.2 a `.0` tag sits on `release/vX.Y`, not on `main`.\nDouble-check with the user.\n\n## 3. Pick the range and generate\n\nKey topology fact: **patch tags** (`v1.0.1`, `v1.0.2`) live on `release/vX.Y` and are **NOT**\nancestors of `main` (they are cherry-picks with different SHAs). Up to v1.1 the `.0` tag was\nthe point where `release/vX.Y` left `main`; from v1.2 the branch is cut before the `.0`, and\npre-releases and the `.0` itself are tagged on the branch with backports. Verify with\n`git merge-base --is-ancestor <tag> <remote>/main`.\n\n### New minor (`X.Y.0`)\nThe note compares against the whole previous line: everything new since `release/vX.(Y-1)`\nleft `main`, minus what any published `vX.(Y-1).*` release (pre-releases, `.0`, patches)\nalready shipped. The line's own pre-releases (`vX.Y.0-beta.*`) are not subtracted; the `.0`\nnote covers the whole minor.\n\nBase = the **previous minor tag** (e.g. `v1.1.0`); tip = the tagged `vX.Y.0` commit, resolved as\nin \"Pre-release\" below. Split the range at the point where `release/vX.Y` left `main`:\n```\nbp=$(git merge-base <remote>/release/vX.Y <remote>/main)\n```\n- `<prev-minor-tag>..$bp` is on `main`; generate it with `git cliff`:\n  ```\n  GITHUB_TOKEN=$(gh auth token) git cliff <prev-minor-tag>..$bp --tag vX.Y.0 \\\n    -o /path/CHANGELOG-vX.Y.0.md\n  ```\n  `cliff.toml`'s `ignore_tags` folds in-range nightly tags into the single section.\n- `$bp..<tip>` is the release branch: backports, squashed picks, and commits without a PR.\n  `git cliff` fails on such commits (`Filter call 'sort' failed`), so build this part as a\n  pre-release: collect the original PRs with the loop in \"Pre-release\" and add their bullets.\n  List the commits that carry no PR with\n  `git log --format='%h %s' $bp..<tip> | grep -vE '\\(#[0-9]+\\)$'`; drop mechanical ones, and\n  for a manual port of a `main` PR (the body says `cherry picked from commit` a SHA that is not\n  on `main`), find that PR by title and cite it.\n\n**Then subtract PRs already shipped in published `vX.(Y-1).*` releases**: their `main` commits\nare inside the range. Build the set as for the first pre-release (below): the PR links in\nevery published `vX.(Y-1).*` release body, plus the `(#N)` line endings in the history of the\nnewest published `vX.(Y-1).*` tag. Remove every changelog bullet whose `#NNNN` is in that set (a\nsmall Python script is the reliable way — match `pull/<n>)` on lines starting with `*`).\n\n### Patch (`X.Y.Z`, Z>0, cherry-picks on the release branch)\nThe previous patch tag is an ancestor of the release branch, so a plain range works **when the\ncherry-picks land as individual commits**:\n```\nGITHUB_TOKEN=$(gh auth token) git cliff <prev-patch-tag>..<release-branch-tip> --tag vX.Y.Z -o ...\n```\nNo extra subtraction (the branch only contains the new cherry-picks).\n\n**Common case — squashed pick commit.** Patch branches are often a single squashed commit like\n`chore: pick fixes and bump version to vX.Y.Z (#NNNN)`, so the plain range above yields just\nthat one bullet. First **find the picked PRs**: read the squash commit message\n(`git log -1 --format=%B <release-branch-tip>`) — each `* fix: ... (#NN)` line is a picked PR.\nThen pick one of two ways to build the changelog (a patch is usually only a few PRs — **ask the\nuser which they prefer**):\n\n- **(a) Generate on `main` and filter** — lets `git cliff` produce correctly formatted bullets\n  (titles, authors, categories):\n  1. Locate and order the picked PRs' commits on `main` chronologically:\n     ```\n     git log --oneline <remote>/main --grep='#NN1' --grep='#NN2' --reverse\n     ```\n     The first line is the oldest pick, and the last line is the newest pick.\n  2. Run cliff over a `main` range covering all of them (base = parent of the oldest picked\n     commit, tip = the newest), then keep only the picked `#NN` bullets:\n     ```\n     GITHUB_TOKEN=$(gh auth token) git cliff <oldest-pick>~1..<newest-pick> --tag vX.Y.Z -o /tmp/raw.md\n     ```\n     The raw output includes unrelated PRs in the main range that were **not** picked — drop\n     them, then rebuild contributors (§4).\n- **(b) Hand-write the bullets** — a fine fallback for a small patch (and when the API/network is\n  flaky). For each picked PR fetch its title + author (`gh api repos/GreptimeTeam/greptimedb/pulls/<n>`)\n  and write the `* <title> by [@user] in [#NN](...)` lines plus the contributor list yourself.\n\nIf the branch history is otherwise messy, the same \"identify the picked PRs and keep only those\"\nrule applies.\n\n### First pre-release (`X.Y.0-alpha.1`, cut from main)\nThe base is the commit where the previous line's release branch left `main`. It exists when the\nalpha is cut, even if `vX.(Y-1).0` does not:\n```\nbase=$(git merge-base <remote>/release/vX.(Y-1) <tag>)\n```\nIf there is no `release/vX.(Y-1)`, use the newest published release tag that is an ancestor of\nthe tag (`git merge-base --is-ancestor <candidate> <tag>`). Run `git cliff <base>..<tag>`, then\nsubtract every PR that already shipped in a published `vX.(Y-1).*` release, pre-releases\nincluded. Take the union of the PR links in those release bodies and the `(#N)` line endings in\nthe history of the newest published `vX.(Y-1).*` tag:\n```\ngh release view <published-vX.(Y-1)-tag> --repo GreptimeTeam/greptimedb | grep -oE 'pull/[0-9]+'\ngit log <newest-published-vX.(Y-1)-tag> --format=%B | grep -oE '\\(#[0-9]+\\)$'\n```\nEither source alone misses PRs: a release body can omit a backport that its tag contains, and a\nbackport commit can name only the backport PR.\n\n### Pre-release (`X.Y.Z-{alpha,beta,rc}.N` after an earlier pre-release)\n**The tip is the tagged commit, not the release branch tip.** Resolve it from the remote and\nwrite this SHA in the docs PR body (§8) as what the note was checked against:\n```\ngit ls-remote --tags <remote> 'vX.Y.Z-beta.N*'\n```\nUse the `refs/tags/vX.Y.Z-beta.N` line (its `^{}` line if the tag is annotated), not the\nbuild-suffixed tags it also lists. PRs that land on `release/vX.Y` after the tag\n(`git log --oneline <tag>..<remote>/release/vX.Y`) belong to the next release, even if they are\nalready merged when you write the note.\n\nThe range `<prev-pre-release>..<tag>` usually mixes two kinds of commits: individual backports\n(`... [Backport release/vX.Y] (#NNNN)`) and squashed pick commits (`chore: backport selected\nchanges for ... (#NNNN)`). In both, the original PRs are the `(#NN)` at the end of body lines;\na backport commit can also carry prerequisite PRs. Collect the PR set, falling back to the\nsubject for commits whose body lists none:\n```\nfor c in $(git rev-list <prev-pre-release>..<tag>); do\n  prs=$(git log -1 --format=%b \"$c\" | grep -oE '\\(#[0-9]+\\)$')\n  [ -n \"$prs\" ] || prs=$(git log -1 --format=%s \"$c\" | grep -oE '\\(#[0-9]+\\)$')\n  echo \"$prs\"\ndone | tr -dc '0-9\\n' | sort -un\n```\nList the bullets under the **original** PRs (title and author of the PR merged to `main`), not\nthe backport PRs. Build them with patch (a) or (b) above; for (b), fetch\n`gh api repos/GreptimeTeam/greptimedb/pulls/<n> --jq '{number,title,author:.user.login,merged_at}'`,\ngroup by the `cliff.toml` parsers (first match wins), order by `merged_at`, and put `!` titles\nunder Breaking changes (§6).\n\nA squashed pick PR can also contain release-only commits that have no PR. List them:\n```\ngh api repos/GreptimeTeam/greptimedb/pulls/<squash-pr>/commits --paginate \\\n  --jq '.[].commit.message | split(\"\\n\")[0]' | grep -vE '\\(#[0-9]+\\)$'\n```\nMechanical ones (version bump, toolchain or lint adaptations; example:\n`fix: adapt release OptionMap iteration to Rust 1.98`) get no bullet. A release-only\ncommit that changes behavior gets a bullet in its group.\n\n> Flaky-network note for (a): `git cliff` fetches the repo's full commit history from the GitHub\n> API for author enrichment (paginates back thousands of commits, even for a tiny range). On a\n> flaky connection it can time out mid-pagination; it caches pages, so just **re-run until it\n> completes** — each retry resumes from the cache. If it keeps failing, fall back to (b).\n\n### Groups (all paths)\n`git cliff` drops PRs whose title isn't a conventional commit (`filter_unconventional`). List\nthem and add the user-facing ones by hand:\n```\ngit log --format=%s <range> | grep -E '\\(#[0-9]+\\)$' | grep -vE '^[a-z]+(\\([^)]*\\))?!?: '\n```\nUse only the groups in `cliff.toml`. `git cliff` prints a `### Build` section for `build:` commits\nbecause no parser matches them; move a user-facing `build(deps)` fix to Bug Fixes and the rest to\nMiscellaneous Tasks. Keep each bullet list free of blank lines; a blank line turns it into a\nloose list that renders with different spacing.\n\n## 4. Rebuild contributor sections after subtracting\n\n`cliff` computes `New Contributors` / `All Contributors` over the full range, so after removing\nlines, recompute from the **remaining** commit bullets:\n- **All Contributors** = sorted set of `@user` from remaining `* ... by [@user] ... in [#NN]`\n  lines (drop bots like `dependabot[bot]`).\n- **New Contributors** = drop any entry whose first-contribution PR was removed (their only\n  in-range PR shipped in a patch).\nDo this in the same script that subtracts the PRs.\n\nWhen the bullets are built by hand (§3 (b), pre-releases), `git cliff` computes no New\nContributors, and GitHub's generated notes don't either (they only see the backport PRs). An\nauthor is new if none of their merged PRs shipped before, i.e. none appears in the history of the\nbase tag:\n```\ngit log <base-tag> --format=%B | grep -oE '\\(#[0-9]+\\)$' | tr -dc '0-9\\n' | sort -u > /tmp/shipped.txt\ngh search prs --repo GreptimeTeam/greptimedb --author <user> --merged --limit 1000 \\\n  --json number --jq '.[].number' | sort -u > /tmp/author.txt\ncomm -12 /tmp/author.txt /tmp/shipped.txt   # empty => new contributor\n```\nRun it for every non-bot author in the release, and cite their earliest-merged PR in this\nrelease. \"Earliest merged PR is in range\" is not the same test: a contributor whose first PR\nmerged on `main` but was not picked into any release is still new when a later PR of theirs\nships.\n\n## 5. Verify\n\nCross-check the result against `git log <base>..<tip>`: no subtracted PR remains, and no\ngenuine new PR was dropped. Optionally drop the mechanical `chore: bump version to vX.Y.Z`\nline (ask the user; some prefer to keep it).\n\n## 6. Human-curated sections (insert after the `Release date:` line)\n\n**Style.** Follow the Google developer documentation style guide: sentence-case headings, active voice, present\ntense for behavior, American spelling, serial comma, code font for identifiers, bold only for UI\nnames. Keep it short:\n- Intro: two sentences at most, what the release covers and its main theme.\n- Highlight: one paragraph of up to four sentences, plus an example if it earns one (see below).\n- Upgrade note or breaking change: one bullet, one or two sentences: what changes, who is\n  affected, what to do.\n- Every sentence states a fact the reader acts on or checks. Delete sentences that restate the\n  heading or the previous sentence, transitions, and summaries.\n- No marketing words (`significantly`, `powerful`, `seamless`, `robust`, `enhanced`), no vague\n  verbs (`supports`, `handles`, `improves`; say what happens), no `several`/`many` when the number\n  is known. Keep the source's hedges; don't add new ones.\n\nExample:\n> Bad: This release significantly enhances write reliability by introducing a robust retry\n> mechanism that seamlessly handles transient failures.\n>\n> Good: In distributed clusters, the frontend retries a failed write up to three times with\n> exponential backoff. Set `max_retries = 0` to disable retries.\n\nMirror past release bodies (`gh release view v1.0.0 --repo GreptimeTeam/greptimedb`):\n- **Short intro** (see Style).\n- **`### 👍 Highlights`** — *few, deep* highlights, ranked by user impact. Fixes for silently\n  wrong query results, lost or invisible data, and security issues rank with new features and\n  can be highlights.\n  - Write a fix as its symptom and the affected deployments or usage, not the PR title. Example:\n    \"with no traffic, `errors / requests > 0.05` was true, so the alert fired\".\n  - State whether a feature is on by default. If an example config uses the default values, say\n    so.\n  - Give an example (SQL / TOML config) only where it helps readers act. Don't pick highlights,\n    or the example, by what is easy to demonstrate.\n  - One highlight covers one change or one user-visible problem. Don't merge unrelated PRs into\n    one entry because they touch the same component.\n  - Sources: the PR body's sections on impact, compatibility, and behavior changes, plus the\n    code and config it touches, not the title alone. Verify examples against the related docs\n    PR/page in the docs repo (`GreptimeTeam/docs`, often checked out locally),\n    `docs/reference/sql/*.md`, `config/*.example.toml`, `config/config.md`.\n  Do **not** mention implementation details, tiny features, or unfinished/experimental-but-not-ready\n  features. Always have the user review and edit the highlights; iterate.\n- **Dashboard** subsection — don't just bump the version. Read the dashboard PRs in the bundled\n  release (`gh release view <ver> --repo GreptimeTeam/dashboard`; then the linked PRs) and\n  describe the user-facing change. Use the names the UI shows, not the PR titles; check them in\n  the dashboard's locale strings at the bundled tag:\n  ```\n  gh api 'repos/GreptimeTeam/dashboard/contents/src/locale/en-US/menu.ts?ref=<dashboard-tag>' \\\n    --jq .content | base64 -d\n  ```\n  Screenshots need absolute `https://` URLs: the body is copied to the GitHub release, where\n  relative and site-root (`/img/...`) paths don't resolve.\n- **`### Upgrade notes`** and **`### Breaking changes`** — check every PR in the release for:\n  `!` titles; `Compatibility`, `Behavior change`, `Breaking` paragraphs in the body (often a\n  plain `Compatibility:` line, not a heading); unchecked \"API changes are backward compatible\" /\n  \"Schema or data changes are backward compatible\" boxes; new config options and their defaults;\n  changed defaults; persisted format changes; rolling-upgrade order; new hard limits. These\n  signals set reading order, not a filter: a limit can sit in plain prose with every box\n  checked, so read the body of every user-facing `feat`/`fix` PR. Examples of what is easy to\n  miss: a 512 MiB cap on decompressed request bodies (#9264); a default step for stepless\n  subqueries (#9481).\n  - **Upgrade notes**: steps operators take — upgrade order, index rebuilds, settings to review.\n  - **Breaking changes**: behavior that used to work and now errors or returns different\n    results. Put the `!` PR bullets here too, after these entries (see `v1.3.0-beta.1`).\n- **Benchmark results** (optional) — state the baseline version (often the previous formal\n  release, not the previous beta), data size, cache state, and hardware. Don't attribute results\n  to single PRs. Leave out results the benchmark marks as not for publication, and smoke-level\n  runs.\n\n## 7. Output\n\nWrite to `CHANGELOG-vX.Y.Z.md` (title `# vX.Y.Z`, which `--tag` produces) and **do not commit\nit**. The release runbook deletes it after the release + docs PR are done.\n\n## 8. Docs-repo blog variant + draft PR\n\nThe release note is also published as a blog post in **`GreptimeTeam/docs`**.\n- **Ask the user for their local `GreptimeTeam/docs` checkout path**; if they have none, offer\n  to clone (`git clone git@github.com:GreptimeTeam/docs.git <path>`).\n- File: `blog/release-X-Y-Z.md` (version with dashes; see `blog/release-1-0-0.md`). Pre-releases\n  use dashes throughout: `blog/release-1-3-0-beta-2.md`.\n- Content = docs frontmatter (below) followed by the GitHub release body, **keeping the\n  `# vX.Y.Z` H1 directly under the frontmatter**. The blog frontmatter has no `title:`, so\n  Docusaurus uses that H1 as the page title and sidebar label — omitting it makes the page\n  render as the filename (e.g. `release-1-1-0` instead of `v1.1.0`). Match the shape of\n  `blog/release-1-0-0.md`:\n  ```\n  ---\n  keywords: [release, GreptimeDB, changelog, vX.Y.Z]\n  description: GreptimeDB vX.Y.Z Changelog\n  date: YYYY-MM-DD\n  ---\n  # vX.Y.Z\n\n  Release date: ...\n  ```\n  The docs CI check `check-release-note-sync` requires the post minus its frontmatter to equal\n  the GitHub release body byte for byte, so the H1 goes on the line right after the closing\n  `---`, with no blank line.\n- The docs working tree may have unrelated WIP — **don't disturb it**: use a `git worktree` off\n  `origin/main`:\n  ```\n  git -C <docs> fetch origin main\n  git -C <docs> worktree add -b chore/X.Y.Z-release-note /tmp/docs-release-note origin/main\n  ```\n- **Read and follow the current docs repo PR template**\n  (`.github/pull_request_template.md`) from the checkout used for the release.\n  Fill in every required section; do not rely on hard-coded section names from an older\n  template. The checklist items are the author's to confirm: check the ones you did, and for one\n  that doesn't apply, check it and say why. For pre-releases, state the tagged SHA (§3) under\n  Verification.\n- **Build the site before opening the PR.** A new worktree has no `node_modules`. Install with\n  the pnpm version pinned in `package.json` (`packageManager`), as CI does:\n  ```\n  cd /tmp/docs-release-note\n  pm=$(jq -r .packageManager package.json)   # e.g. pnpm@8.6.0\n  npx -y \"$pm\" install --frozen-lockfile\n  npx -y \"$pm\" build\n  ```\n  If the build fails, fix it or stop; don't mark the PR ready for review.\n- Commit with sign-off, push, open a **draft** PR, then remove the worktree:\n  ```\n  git -C /tmp/docs-release-note add blog/release-X-Y-Z.md\n  git -C /tmp/docs-release-note commit -s -m \"docs: add X.Y.Z release note\"\n  git -C /tmp/docs-release-note push -u origin chore/X.Y.Z-release-note\n  gh pr create --draft --repo GreptimeTeam/docs --base main --head chore/X.Y.Z-release-note \\\n    --title \"docs: add X.Y.Z release note\" --body-file <template-filled body>\n  git -C <docs> worktree remove /tmp/docs-release-note\n  ```\n- Gotcha: `gh pr edit/create` may fail with an org-scope error if the token lacks `read:org`.\n  Edit the body via REST instead: `gh api repos/GreptimeTeam/docs/pulls/<n> -X PATCH -F body=@body.md`.\n",".agents/skills/greptimedb-release/agents/openai.yaml":"interface:\n  display_name: \"GreptimeDB Release\"\n  short_description: \"Publish a GreptimeDB release\"\n  default_prompt: \"Use $greptimedb-release to publish the next GreptimeDB release and coordinate the changelog, GitHub release, CI follow-up, and docs PR.\"\n",".agents/skills/greptimedb-release-note/agents/openai.yaml":"interface:\n  display_name: \"GreptimeDB Release Notes\"\n  short_description: \"Generate GreptimeDB release notes\"\n  default_prompt: \"Use $greptimedb-release-note to generate the GreptimeDB changelog, curate highlights, and prepare the docs release-note PR.\"\n",".agents/skills/greptimedb-release/SKILL.md":"---\nname: greptimedb-release\ndescription: Runbook for publishing a new GreptimeDB version (tag + GitHub release + docs release-note PR) on the upstream GreptimeTeam/greptimedb repo. Use when asked to \"release\" / \"publish\" a GreptimeDB version (e.g. v1.1.0, v1.0.3).\n---\n\n# GreptimeDB Release Runbook\n\nPublish a formal GreptimeDB release on **`GreptimeTeam/greptimedb`**. Always operate against\nthat repo (for `gh`, pass `--repo GreptimeTeam/greptimedb`). Changelog generation is a\nseparate, involved task — use the **`greptimedb-release-note`** skill for it.\n\nThis whole flow touches public infrastructure. Confirm with the user before the\noutward-facing step (creating the release, which creates the tag and triggers CI).\n\n## Prerequisites & remote\n\n- Tools: `gh` (check with `gh auth status`) and `git`. Changelog generation additionally\n  needs `git cliff` and Python — see the `greptimedb-release-note` skill.\n- **Resolve the remote first.** This runbook writes `<remote>` for the git remote pointing\n  at `GreptimeTeam/greptimedb`; substitute your actual name (often `upstream`, sometimes\n  `origin` on a direct clone):\n  ```\n  git remote -v | grep -i 'GreptimeTeam/greptimedb' | awk '{print $1}' | head -1\n  ```\n\n## 0. Inputs: version and branch\n\n- Ask the user for the **version** to release (e.g. `1.1.0`, `1.0.3`) and, if needed, the\n  **branch**.\n- **Branch is inferred from version**: `MAJOR.MINOR` → `release/v<MAJOR>.<MINOR>`.\n  - `v1.0.0 / v1.0.1 / v1.0.2` live on `release/v1.0`; `v1.1.0` lives on `release/v1.1`.\n  - Infer it, then double-check with the user.\n- **New minor/major (`X.Y.0`)** is cut from `main`. If `release/vX.Y` does not exist on\n  the remote, offer to create it from the intended `main` commit (with the user's consent):\n  `git push <remote> <commit>:refs/heads/release/vX.Y`.\n- **Patch (`X.Y.Z`, Z>0)** must use the existing `release/vX.Y` branch (it carries\n  cherry-picked commits).\n- Check what exists on the remote: `git ls-remote --heads <remote> 'release/*'`.\n\n## 1. Verify the Cargo version\n\nThe workspace version on the release branch **must** equal the version being released, or\nstop. **Fetch the branch first**, then read `FETCH_HEAD` directly — `git fetch <remote>\nrelease/vX.Y` only *opportunistically* updates the remote-tracking ref\n`<remote>/release/vX.Y` (and only when the remote has a matching configured refspec), so it\nmay be missing or stale (e.g. a custom `<remote>`, or a branch just created in §0). Reading\n`FETCH_HEAD` always reflects the tip just fetched:\n```\ngit fetch <remote> release/vX.Y\ngit show FETCH_HEAD:Cargo.toml | grep -A30 '\\[workspace.package\\]' | grep -m1 version\n```\n(For a fresh minor cut from main, `<remote>/main` and `release/vX.Y` are usually the same\ncommit.)\n\n## 2. Generate and curate the changelog\n\nUse the **`greptimedb-release-note`** skill. It produces `CHANGELOG-vX.Y.Z.md` (uncommitted)\nand the docs-blog variant. **Review the highlights with the user and iterate** before\npublishing.\n\n## 3. Create the GitHub release (creates the tag + triggers CI)\n\n**Do NOT pre-create the tag.** Creating the release creates the tag and fires the\ntag-push CI (`.github/workflows/release.yml`) that builds all binaries (**~hours**).\nConfirm with the user, then:\n```\ngh release create vX.Y.Z \\\n  --repo GreptimeTeam/greptimedb \\\n  --target release/vX.Y \\\n  --title \"Release vX.Y.Z\" \\\n  --notes-file CHANGELOG-vX.Y.Z.md \\\n  --prerelease\n```\n- Title convention: `Release vX.Y.Z`.\n- Always create as **`--prerelease`**: prerelease here is just a \"build in progress\" marker.\n  The CI clears it on success (see §5). (The user creating it on the web works too.)\n- Verify: `gh release view vX.Y.Z --repo GreptimeTeam/greptimedb --json name,tagName,isPrerelease,draft,targetCommitish`.\n\n## 4. Open the docs release-note PR (do not wait for CI)\n\nRight after triggering the release, open the docs draft PR (see the docs section of the\n`greptimedb-release-note` skill). It only needs the finalized changelog.\n\nThen **delete the local `CHANGELOG-vX.Y.Z.md`** (its content now lives in the release body\nand the docs blog post).\n\n## 5. After the CI build finishes (~hours)\n\nCI's `publish-github-release` action, for a tag matching `^vX.Y.Z$`, runs\n`ncipollo/release-action` with `allowUpdates: true`, **`prerelease=false`,\n`makeLatest=true`, `omitBody=true`** (so it keeps our changelog body but finalizes the\nflags). These flags are described from the current `.github/workflows/release.yml` —\n**verify against the workflow** if behavior differs. Verify success:\n```\ngh release view vX.Y.Z --repo GreptimeTeam/greptimedb --json isPrerelease,assets\n```\n**Latest handling is conditional on whether this is the newest version:**\n- **Releasing the latest version** (e.g. v1.1.0 when nothing newer exists) → CI's finalized\n  state (non-prerelease, latest) is correct; **do nothing**.\n- **Releasing a non-latest line** (e.g. a patch `v1.0.3` while `v1.1.0` is already latest) →\n  set it back to non-latest after CI finishes:\n  `gh release edit vX.Y.Z --repo GreptimeTeam/greptimedb --latest=false`.\n\n## 6. Rollback (only on failure, with double confirmation)\n\nShow the user exactly what will be removed first; never delete blindly.\n```\ngh release delete vX.Y.Z --repo GreptimeTeam/greptimedb   # remove the release\ngit push <remote> :refs/tags/vX.Y.Z                        # remove the tag\n```\n\n## Conventions / gotchas\n\n- Remotes: `<remote>` is whatever points at `GreptimeTeam/greptimedb` (see Prerequisites).\n  `gh` defaults can be unreliable in a repo with many remotes — always pass\n  `--repo GreptimeTeam/greptimedb`.\n- Release title is always `Release vX.Y.Z`.\n- When reasoning about \"previous version\", skip nightly / build-suffixed tags\n  (`*-nightly-*`, `vX.Y.Z-rc.N-<sha>-<date>-*`).\n","AGENTS.md":"# AGENTS.md\n\nGuidance for coding agents (Claude Code, Codex, ...) and contributors working in\nthis repository. `CLAUDE.md` is a symlink to this file. If `.local/AGENTS.md` or\n`.local/CLAUDE.md` is present, you **MUST** read it as well — it holds personal\nor machine-local overrides (gitignored, not shared). To record a personal or\nmachine-local override, write it to `.local/AGENTS.md` (create `.local/` and the\nfile if absent), not to this shared file.\n\nGreptimeDB is an open-source, cloud-native observability database for unified\ncollection and analysis of metrics, logs, and traces. It is written in Rust and\nprovides sub-second querying at PB scale with high cost efficiency.\n\n## Core commands\n\n| Task | Command |\n| --- | --- |\n| Build (debug) | `make build` |\n| Build (release) | `make build RELEASE=true` |\n| Run standalone | `cargo run -- standalone start` |\n| Targeted Rust tests | `cargo nextest run -p <package>` (preferred over `cargo test`) |\n| Full local Rust suite | `make test` |\n| SQL tests | `cargo sqlness bare` (single case: `cargo sqlness bare -t <name>`) |\n| Format | `make fmt` (and `make fmt-toml` for TOML) |\n| Lint | `make clippy` (= `cargo clippy --workspace --all-targets --all-features -- -D warnings`) |\n| Type check | `make check` |\n\nToolchain: Rust (stable, pinned by `rust-toolchain.toml`), Protobuf compiler (>= 3.15), C/C++ build essentials.\nInstall the test runner with `cargo install cargo-nextest --locked`.\n\n## Repo map\n\nGreptimeDB is a Cargo workspace rooted at the repository; most crates live under\n`src/` (plus `tests-fuzz`, `tests-integration`, `tests/runner`). Key areas:\n\n- **Frontend / protocol**: `src/frontend/` (request orchestration), `src/servers/`\n  (wire protocols), `src/sql/` (SQL parsing)\n- **Storage engines**: `src/mito2/` (main time-series engine), `src/metric-engine/`\n  (metrics), `src/file-engine/`\n- **Coordination**: `src/meta-srv/` (metadata & cluster control), `src/meta-client/`\n- **Execution / statements**: `src/operator/` (DDL/DML, request conversion, procedures)\n- **Stream / transform**: `src/flow/` (continuous aggregation), `src/pipeline/`\n- **Query / index**: `src/query/`, `src/promql/`, `src/index/`\n- **Shared**: `src/common/`, `src/datatypes/`, `src/store-api/` (engine contract),\n  `src/catalog/`, `src/table/`\n\nBefore editing a path, read every `AGENTS.md` from the repository root down to\nthat path. The root guide always applies; a nested guide adds or overrides rules\nfor its subtree. Changes spanning multiple subtrees must follow every applicable\nguide.\n\n`AGENTS.md` files are maps, not manuals. Keep them short and stable: record\nwhere code lives, important boundaries or change-coupling, high-cost gotchas,\nand the validation entry point. Implementation details belong in code. Do not\ncopy exhaustive test lists or workflow logic; link to the source of truth.\n\nHigh-change areas and specialized test suites carry their own `AGENTS.md` with\na module map, read/write paths, change-coupling points, and gotchas:\n\n- [`src/common/meta/AGENTS.md`](src/common/meta/AGENTS.md)\n- [`src/query/AGENTS.md`](src/query/AGENTS.md)\n- [`src/servers/AGENTS.md`](src/servers/AGENTS.md)\n- [`src/operator/AGENTS.md`](src/operator/AGENTS.md)\n- [`src/mito2/AGENTS.md`](src/mito2/AGENTS.md)\n- [`src/metric-engine/AGENTS.md`](src/metric-engine/AGENTS.md)\n- [`src/flow/AGENTS.md`](src/flow/AGENTS.md)\n- [`src/frontend/AGENTS.md`](src/frontend/AGENTS.md)\n- [`src/meta-srv/AGENTS.md`](src/meta-srv/AGENTS.md)\n- [`tests-integration/AGENTS.md`](tests-integration/AGENTS.md)\n- [`tests/compatibility/AGENTS.md`](tests/compatibility/AGENTS.md)\n- [`tests/perf/AGENTS.md`](tests/perf/AGENTS.md)\n\n## Rust style\n\nFollow [`docs/style-guide.md`](docs/style-guide.md) for Rust style, module\ndesign, naming, and reuse guidance.\n\n## Read before changing code\n\n- [`.agents/architecture-invariants.md`](.agents/architecture-invariants.md) —\n  repo-wide rules that are easy to violate and expensive to get wrong (persisted/\n  wire format compatibility, crate layering, async runtimes, error handling,\n  feature gating, the DataFusion fork).\n- [`.agents/generated-files.md`](.agents/generated-files.md) — tool-generated\n  artifacts that must not be hand-edited (sqlness `.result`, `config/config.md`,\n  Grafana dashboards, proto).\n- [`docs/style-guide.md`](docs/style-guide.md) — code style.\n- [`CONTRIBUTING.md`](CONTRIBUTING.md) — contribution flow and CLA.\n\n## High-signal entry points\n\n- Main binary: `src/cmd/src/bin/greptime.rs`\n- Configuration: `src/common/config/`, example TOMLs in `config/`\n- Error handling: `src/common/error/` (`ErrorExt`, `StatusCode`)\n- Protocol implementations: `src/servers/src/`\n\n## Worktree safety\n\n- Check `git status --short` before editing. Preserve unrelated tracked changes\n  and untracked files; re-read files changed by another process.\n- Do not rewrite history, force-push, or remove files in bulk unless the user\n  explicitly requests it.\n- Update generated artifacts only through the generators documented in\n  [`.agents/generated-files.md`](.agents/generated-files.md).\n\n## Validation by change type\n\nUse the narrowest command that covers the change, then expand only when its\nblast radius requires it.\n\n| Change | Minimum validation |\n| --- | --- |\n| One Rust crate | `cargo nextest run -p <package>` |\n| Cross-workspace Rust behavior | `make test`; inspect `.github/workflows/rust.yml` when CI parity matters |\n| SQL parsing, planning, execution, or output | `cargo sqlness bare -t <case>`; inspect regenerated `.result` files |\n| Persisted metadata or wire format | Add/run a case under `tests/compatibility/`; follow its `README.md` and `AGENTS.md` |\n| Public configuration | Update example TOMLs, loading/serialization snapshots, and docs; run `make config-docs` |\n| Query regression harness or DSL | Follow `tests/perf/AGENTS.md` |\n| Enterprise-gated code | Build/test with `--features enterprise` where applicable and run `make check-enterprise-license` |\n\nFor import/export, COPY, or snapshot-storage changes, follow the Windows\nportability and validation guidance in\n[`tests-integration/AGENTS.md`](tests-integration/AGENTS.md).\n\n## Before opening a PR\n\n1. If you added a `.rs`, `.py`, or `.ts` file, apply and verify its license\n   header with `hawkeye format` followed by `hawkeye check`. Use the inception\n   year from `licenserc.toml`, not the current year.\n2. `make fmt`\n3. `make clippy`\n4. `make test`\n5. `make check-udeps` (run `make fix-udeps` if it reports unused dependencies).\n6. If you added or changed a public configuration option, update the applicable\n   example TOMLs, configuration-loading and serialized-config snapshot tests,\n   and related user-facing documentation. Run `make config-docs` (needs Docker)\n   and commit the regenerated `config/config.md`.\n7. If you changed a persisted or wire format, add a compatibility test case (see\n   `.agents/architecture-invariants.md`).\n8. If you added or gated an enterprise-only file, give it the enterprise license\n   header, list it in `licenserc-enterprise.toml` (`includes`) and\n   `licenserc.toml` (`excludes`), and run `make check-enterprise-license`.\n9. Use a conventional-commit title, sign off commits (`git commit -s`), and sign\n   the CLA.\n10. When creating or updating a pull request, follow\n   [`.github/pull_request_template.md`](.github/pull_request_template.md): include\n   the CLA statement, fill the change-intention section with enough detail, and\n   update checklist items accurately. \"This PR requires documentation updates\"\n   refers to the official docs site repository\n   ([`GreptimeTeam/docs`](https://github.com/GreptimeTeam/docs)), not to rustdoc\n   or comments in this repo. Check it only when that site would be wrong or\n   incomplete without a matching change; a change users cannot observe does not\n   need it.\n11. If the change must also land on release branches, add the matching\n   `backport-<target>` labels (e.g. `backport-v1.3` targets `release/v1.3`). The\n   Backport workflow (`.github/workflows/backport.yml`) cherry-picks a merged PR\n   onto each target branch and opens a backport PR automatically; on conflicts\n   it aborts and files an issue for a manual backport.\n\n## More\n\n- Agent skills and resources: [`.agents/`](.agents/) (see [`.agents/README.md`](.agents/README.md))\n- Architecture decisions: [`docs/rfcs/`](docs/rfcs/)\n- How-to guides: [`docs/how-to/`](docs/how-to/)\n",".agents/skills/greptimedb-development-docker-image/SKILL.md":"---\nname: greptimedb-development-docker-image\ndescription: Builds a development-only GreptimeDB Docker image from a local debug binary for local-cluster testing, and optionally pushes it to a development registry. Use when the user asks to package, build, tag, publish, or cross-build a non-release GreptimeDB or GreptimeDB Enterprise image for debugging.\ncompatibility: Requires Docker with Buildx for cross-platform builds; Podman is supported for native-platform builds.\nmetadata:\n  protocols: docker buildx podman\n  platforms: linux-amd64 linux-arm64\n---\n\n# GreptimeDB Development Docker Image\n\n## Goal\n\nPackage a locally built GreptimeDB binary into a **development-only** Docker\nimage for debugging and local-cluster testing, optionally push it to a\ndevelopment registry, and retain the non-secret image settings in `.env` for\nthe next build. It is **not** a release-image workflow and must not be used to\npublish a production or release artifact.\n\nThis skill follows the documented development-image build procedure:\n\n- Build `greptime` with Cargo's `nightly` profile.\n- Copy the target binary into the Docker build context as `./greptime`.\n- Build the supplied `Dockerfile`, which uses Ubuntu 24.04 only as the runtime\n  base image and exposes the `greptime` binary as its entrypoint.\n\n## Inputs\n\nCollect or discover the following before any build or push:\n\n| Input | Discovery and rule |\n| --- | --- |\n| Source repository | Treat the directory in which this skill is invoked as the default workspace. Include it as an editable field in the batch configuration; do not ask a separate confirmation. Do not assume open-source versus Enterprise. |\n| Edition and binary | Default to `greptime`; use the binary name the user requests for Enterprise builds. The copied Docker-context filename must always be `greptime`. |\n| Cargo profile and binary source | Include this in the batch configuration. Default to rebuilding `nightly`; reuse an existing binary only after the user explicitly accepts that its freshness is unverified. |\n| Target platform | Preselect `linux/amd64` unless Docker's server is `linux/arm64`, then preselect `linux/arm64`. Let the user override it in the batch configuration. Ubuntu 24.04 is the runtime base image, not a target-platform choice. Each image has exactly one target platform, but it may differ from the host platform. |\n| Build mode | Ask whether the user wants a locally loadable debug image or a registry push. |\n| Registry/repository | Read `IMAGE_REGISTRY` and `IMAGE_REPOSITORY` only from the selected workspace's `.env` and prefill them in the batch configuration. Example: `registry.example.com/team` + `greptimedb-dev`. |\n| Tag | Read `IMAGE_TAG` only from the selected workspace's `.env` and prefill it in the batch configuration. When the tag exists in the selected registry, preselect an incremented version. |\n\nUse the image reference `${IMAGE_REGISTRY}/${IMAGE_REPOSITORY}:${IMAGE_TAG}`.\nIf the registry is intentionally empty, omit its slash rather than producing a\nleading slash.\n\n## Interactive Workflow\n\nUse the platform's interactive prompt component for every question, selection,\nand confirmation. Do not ask an open-ended text question when a single-choice,\nmulti-select, or confirmation component can represent the decision. If the\nplatform does not provide an interactive component, use the equivalent numbered\nor lettered prompt below and wait for input before continuing.\n\n### Batch configuration\n\nMinimize user round trips: run the collector first against the invocation\ndirectory, then use one interactive form or batched prompt to collect workspace,\nedition/binary, profile/reuse-or-rebuild choice, target platform, build mode,\nregistry/repository, tag, and whether to inspect the configured registry tag.\nUse the invocation directory as the editable workspace default. Prefill\ncollector values and mark recommended defaults. If the user changes workspace,\nrerun the collector for that workspace without asking another configuration\nquestion. Only ask a follow-up if information is missing or invalid, or a safety\ngate is required. Treat unchanged prefilled values as accepted.\n\nKeep separate confirmation components only for elevated privileges: `sudo`,\npackage installation, and privileged QEMU setup. Use one final confirmation for\nall non-privileged selected work.\n\nAlways offer **Cancel** for an action that can write state, build, push, or\nrequire elevated privileges. Display the relevant preview before the user\nconfirms it: source/binary path, `file` architecture result, platform(s), image\nreference, build mode, and non-secret `.env` values.\n\n### Batch configuration fields\n\nUse single-choice controls inside the one batch form for mutually exclusive\nchoices:\n\n| Decision | Required choices |\n| --- | --- |\n| Cargo output | Rebuild `nightly` (recommended), reuse an existing binary only with a freshness-unverified acknowledgement, choose a profile/binary |\n| Target platform | `linux/amd64` (default unless Docker server is arm64), `linux/arm64` (default when Docker server is arm64). The selected image may be cross-built with Buildx. |\n| Build mode | Load locally (recommended), push to development registry |\n| Registry/repository | Use `.env` defaults, enter new values |\n| Tag | Use proposed increment when the configured tag exists, enter a version |\n\nWhen push is selected and the image configuration is complete, automatically\nrun the read-only registry-tag check. It may return `unknown`; do not treat that\nas a missing tag.\n\nUse one final confirmation for all selected non-privileged operations:\n\n- persist changed non-secret image settings to `.env`;\n- run the selected Cargo build or reuse the resolved binary;\n- prepare the Docker context;\n- build, verify, and, when selected, push the image.\n\nThe final confirmation must clearly state: **“This creates a development and\nlocal-cluster test image, not a release or production artifact.”**\n\nFor a real multi-select only, use lettered choices (`[A]`, `[B]`) and accept\n`all`, `none`, or `cancel`; otherwise use single-choice components.\n\n### Preflight context collection\n\nBefore asking the profile, registry, or tag questions, run the bundled,\nread-only collector. It works on macOS and Linux, does not invoke `cargo build`,\nand emits JSON that can be shown or summarized to the user:\n\n```bash\npython3 <skill-dir>/scripts/collect_context.py \\\n  --source <source-checkout> \\\n  --bin greptime \\\n  --profile nightly \\\n  [--target <rust-target>]\n```\n\nUse its report to identify:\n\n- missing `IMAGE_REGISTRY`, `IMAGE_REPOSITORY`, or `IMAGE_TAG` values in the\n  selected workspace's `.env`;\n- Cargo binary targets available in the workspace and whether the requested\n  `--bin` exists;\n- the resolved Cargo target directory, expected profile output path, and whether\n  that binary already exists; and\n- macOS/Linux host architecture plus Docker's server platform when Docker is\n  available.\n\nWhen push is selected and image configuration is complete, rerun the collector\nwith `--check-registry-tag` and all selected image values. It uses the selected\nengine's read-only manifest inspection and returns `exists` or `unknown`;\n`unknown` includes an unavailable registry, an absent tag, or missing\nauthentication and must not be treated as an absent tag.\n\n```bash\npython3 <skill-dir>/scripts/collect_context.py \\\n  --source <source-checkout> \\\n  --bin <binary> \\\n  --profile <profile> \\\n  --engine <docker|podman> \\\n  --registry <registry> \\\n  --repository <repository> \\\n  --tag <tag> \\\n  --check-registry-tag\n```\n\nWhen the status is `exists` and the tag ends in a number, preselect the\ncollector's `candidate_tag` as the next development tag. The user must still\nconfirm it before `.env` is updated or an image is built. Never use automatic\nincrementing as permission to overwrite or push an existing image.\n\nIf Cargo metadata or the requested binary target is unavailable, stop before\nbuilding and ask the user to correct the source path or binary target.\n\nThe profile/binary choice is a field in the batch configuration, not a separate\nquestion. Its default is the existing `nightly` output when present:\n\n```text\nQuestion: Which Cargo build output should package this image?\nOptions:\n- Rebuild nightly (Recommended): run Cargo with `--profile nightly`, then use its output.\n- Reuse nightly output: use the existing output only after showing its path, modification time, size, and matching platform; freshness is unverified.\n- Choose profile or binary: provide a different Cargo profile or an explicit binary path.\n```\n\nDo not infer that a binary under `target/debug`, `target/release`, or another\nprofile is suitable. For an existing binary, show its resolved path and run\n`file` before asking for final build confirmation.\n\n## Environment State\n\nThe image configuration lives only in the selected workspace's `.env`. The\ncollector reads only the following keys and never reads or displays other `.env`\nvalues:\n\n```dotenv\nIMAGE_REGISTRY=registry.example.com/team\nIMAGE_REPOSITORY=greptimedb-dev\nIMAGE_TAG=dev-001\n```\n\n1. Read `.env` if it exists. Ignore blank lines and `#` comments.\n2. Show the discovered registry, repository, and proposed tag to the user.\n3. Include registry/repository and tag in the batch configuration even when\n   values already exist; saved values are defaults, not authorization to reuse\n   them.\n4. Compare the selected configuration with the three current managed values. If\n   all match, skip `.env` confirmation and do not update the file. Otherwise,\n   preview only the changed `IMAGE_*` values, request confirmation, and update\n   the file while preserving unrelated entries and comments.\n5. Never put registry credentials, access tokens, passwords, or `docker login`\n   output in `.env`, build commands, logs, or responses. Ask the user to log in\n   themselves if a push needs authentication.\n\nFor a repeated build, offer an incremented tag before asking. Use\n`scripts/next_image_tag.py --tag <existing-tag>` to calculate it. It increments\nthe final numeric component while preserving zero padding:\n\n```text\nweny-2025-0715-01 -> weny-2025-0715-02\nv0.1.4 -> v0.1.5\ndebug-009 -> debug-010\n```\n\nIf the saved tag has no trailing number, ask the user for the next tag rather\nthan inventing a versioning convention. Do not overwrite an existing image tag\nwithout explicit user confirmation.\n\nOnly if a managed value is missing or differs, persist the values with the\nbundled helper after confirmation instead of hand-editing the file:\n\n```bash\npython3 <skill-dir>/scripts/update_image_env.py \\\n  --env <source-checkout>/.env \\\n  --registry <registry> \\\n  --repository <repository> \\\n  --tag <tag>\n```\n\n## Build Procedure\n\n### 1. Inspect the host and container tooling\n\nRun these checks and report the selected path:\n\n```bash\nuname -s\nuname -m\ndocker version --format '{{.Server.Os}}/{{.Server.Arch}}'\ndocker buildx version\ndocker buildx inspect --bootstrap\n```\n\nFor a native-platform build, Podman can be used if Docker is unavailable. For\nany non-native request, require Docker Buildx. Do not silently fall back to a\nnative build when the requested image platform differs.\n\nIf the requested target is unavailable, stop and ask the user to configure a\nBuildx builder and cross-compilation toolchain externally. Do not automate\nprivileged QEMU or binfmt setup, and never run an unpinned privileged image.\n\n### 2. Build the executable for the image platform\n\nFrom the source checkout, build the requested binary. For open-source amd64:\n\n```bash\n<skill-dir>/scripts/build_binary.sh \\\n  --source <source-checkout> --package cmd --bin greptime --profile nightly\n```\n\nFor open-source arm64:\n\n```bash\n<skill-dir>/scripts/build_binary.sh \\\n  --source <source-checkout> \\\n  --package cmd \\\n  --bin greptime \\\n  --profile nightly \\\n  --target aarch64-unknown-linux-gnu\n```\n\nFor Enterprise, use the user-provided executable target, for example:\n\n```bash\n<skill-dir>/scripts/build_binary.sh \\\n  --source <source-checkout> --bin greptime-ent-cloud --profile nightly\n```\n\nFor a user-selected profile, replace `nightly` in the helper call and resolve\nthe binary under `target/<profile>/<binary>` (or\n`target/<rust-target>/<profile>/<binary>` for cross-compilation). Reuse the\nexisting target file only when the interactive profile question selected reuse;\notherwise invoke the helper to rebuild it.\n\nDo not use a binary compiled for the host architecture in an image intended for\nanother architecture. Determine the binary path from the Cargo target and\nprofile, then copy it into the Docker context as `greptime`:\n\n```bash\ncp <source-target-binary> <build-context>/greptime\n```\n\nVerify it before building the image with `file <build-context>/greptime`; its\nreported architecture must match the requested target platform.\n\nCreate a fresh isolated build context (never use the repository root) and use\nthe bundled preparation script. The helper rejects an existing Dockerfile or\nbinary output to prevent accidental context reuse:\n\n```bash\n<skill-dir>/scripts/prepare_context.sh \\\n  --context \"$(mktemp -d)\" \\\n  --binary <source-target-binary> \\\n  --dockerfile <skill-dir>/assets/Dockerfile \\\n  --platform <platform>\n```\n\n### 3. Build or push the image\n\nRun the command only after the user confirms the final image reference and\nwhether it should be pushed.\n\nUse the bundled build helper rather than spelling out individual Docker or\nPodman commands. It follows the existing scripts' Docker-first/Podman-fallback\nbehavior, uses Buildx for a non-native Docker request, and accepts exactly one\ntarget platform per image:\n\n```bash\n<skill-dir>/scripts/build_image.sh \\\n  --context <build-context> \\\n  --image <image-reference> \\\n  --platform <platform> \\\n  --mode local|push\n```\n\n`--mode local` builds a native image locally or uses `docker buildx --load` for\na non-native image. `--mode push` pushes the selected single-platform image.\nThe helper never pushes in `local` mode.\n\nThis is intentionally a runtime image built from a precompiled binary, not a\nmulti-stage Dockerfile. Ubuntu 24.04 is the runtime base image only; select one\ntarget platform separately as `linux/amd64` or `linux/arm64`. Only introduce a\nmulti-stage Dockerfile if the user asks to compile within Docker or the local\nRust toolchain cannot build the required target. Keep the final Ubuntu 24.04\nruntime stage and existing entrypoint unless the user asks to change runtime\nbehavior.\n\nAt the final confirmation, repeat that the selected reference is a\n**development/local-cluster test image**, not a release artifact. If the user\nrequests a release or production image, stop and direct them to the release\nprocess instead.\n\n### 4. Verify the result\n\nFor a local image, use the selected engine to inspect its architecture and\nstart it with `--help`:\n\n```bash\ndocker image inspect <image-reference> --format '{{.Os}}/{{.Architecture}}'\ndocker run --rm <image-reference> --help\n```\n\nFor Podman, use `podman image inspect <image-reference>` and\n`podman run --rm <image-reference> --help`. For a pushed Podman image, use\n`podman manifest inspect <image-reference>`.\n\nFor a pushed image, inspect its remote manifest:\n\n```bash\ndocker buildx imagetools inspect <image-reference>\n```\n\nReport the final image reference, target platform, source binary path, and\nwhether the image was loaded locally or pushed.\n\n## Safety Rules\n\n- This skill is only for development and local-cluster testing. Never present\n  its image as a production, release, or officially published artifact.\n- Never push by default. Local debug builds should use a local tag and load the\n  image unless the user explicitly requests a push.\n- Do not run `docker login`, privileged QEMU setup, package installation, or\n  overwrite a tag without confirmation.\n- Do not delete existing images, builders, binaries, or `.env` entries as part\n  of this workflow.\n- Stop and explain if Docker's server is unavailable, Buildx cannot support the\n  requested platform, or the copied binary architecture does not match the\n  image platform.\n",".agents/skills/greptimedb-fuzz-ci-failure-investigation/SKILL.md":"---\nname: greptimedb-fuzz-ci-failure-investigation\ndescription: Investigate a failed GreptimeDB fuzz CI target link by downloading GitHub Actions job logs plus fuzz artifacts such as kind logs, monitor dumps, and CSV dumps, then correlate the failure with local GreptimeDB source code. Use when the user provides a failed fuzz CI target/job URL or asks to diagnose GreptimeDB fuzz CI failures.\n---\n\n# GreptimeDB Fuzz CI Failure Investigation\n\nInvestigate failed fuzz-test jobs for **`GreptimeTeam/greptimedb`** from a local\ncheckout. The purpose is to download the failed job's CI output and fuzz artifacts,\nthen explain the likely cause by correlating the evidence with GreptimeDB source.\n\nAlways pass `--repo GreptimeTeam/greptimedb` to `gh`; local remotes may point to\nforks. Keep the workflow read-only: do not rerun jobs, cancel workflows, push,\ncomment on PRs, or delete artifacts unless the user explicitly asks.\n\n## Scope\n\nUse this skill for fuzz CI only. In `.github/workflows/integration.yml`, fuzz failures\nare the CI jobs that use `.github/actions/fuzz-test`:\n\n- `fuzztest` — standalone fuzz targets.\n- `unstable-fuzztest` — unstable standalone fuzz target.\n- `distributed-fuzztest` — distributed cluster fuzz targets.\n- `distributed-fuzztest-with-chaos` — distributed fuzz targets with Chaos Mesh.\n\nEach matrix job runs a semantic group of targets on one prepared environment.\nThe group defaults to fail-fast: after the first target failure it captures that\ntarget's diagnostics and marks the remaining targets as skipped. The workflow\nstrategy still uses `fail-fast: false`, so failures do not cancel other groups.\n\nThe reusable action `.github/actions/fuzz-test/action.yaml` delegates each target\nto `.github/scripts/run-fuzz-targets.sh`. CI passes `FUZZ_BIN_DIR`, so the script\nruns the prebuilt target executable:\n\n```bash\n<prebuilt-fuzz-binary> -max_total_time=<seconds> \\\n  -artifact_prefix=<target-dir>/libfuzzer/\n```\n\nWithout `FUZZ_BIN_DIR`, such as during local reproduction, the script falls back\nto `cargo fuzz run <target> --fuzz-dir tests-fuzz -D -s none` with the same\nlibFuzzer arguments.\n\nFailed jobs upload one group artifact. Its stable name identifies the job kind,\nmode, and group, for example:\n\n```text\nfuzz-distributed-remote-wal-database-and-regular-table\n```\n\nThe artifact contains `manifest.json`, `summary.md`, and one directory per target\nunder `targets/<target>/`. A failed distributed target can include `fuzz.log`,\nlibFuzzer reproducers, CSV/SQL traces, Kind logs, monitor dumps, and Kubernetes\nstate. Setup failures use `targets/setup/`.\n\n## Inputs\n\nThe normal user input is a **CI target link**: a GitHub Actions job URL for one\nfailed matrix target. Treat that URL as the primary source of truth. Example:\n\n```text\nhttps://github.com/GreptimeTeam/greptimedb/actions/runs/29000666156/job/86062894741\n```\n\nAlso accept these fallback inputs:\n\n- A workflow run URL, e.g.\n  `https://github.com/GreptimeTeam/greptimedb/actions/runs/29000666156`.\n- A run id plus a fuzz target, mode name, artifact name, PR, branch, or commit SHA.\n\nParse ids from URLs:\n\n```bash\nREPO=GreptimeTeam/greptimedb\nRUN_ID=29000666156\nJOB_ID=86062894741\n```\n\nWhen the input is a job URL, do not ask for the target name first. Use the job id\nto fetch metadata, then derive the group and mode from the job name. A distributed\nfuzz job name usually contains the CI group tuple:\n\n```text\nFuzz Test (Distributed, <mode>, <group>)\nFuzz Test with Chaos (Distributed, <mode>, <group>)\n```\n\nIf only a run URL is provided, list failed jobs and choose the fuzz job matching the\ngroup/mode. Ask only when multiple fuzz jobs are plausible and there is no group,\nmode, artifact, or job-url clue.\n\n## Prerequisites\n\n- `gh` must be installed and authenticated (`gh auth status`). Actions logs and\n  artifacts commonly require authentication even for a public repo.\n- `jq`, `unzip`, and standard shell tools are useful for reducing logs.\n- Use the local GreptimeDB checkout for source analysis. If the run's head SHA is\n  not the current checkout, inspect the code at the run's SHA before making claims.\n- Use a scratch directory outside the source tree, such as `/tmp/greptimedb-fuzz-ci`.\n\n## 1. Capture run and job metadata\n\nCreate a scratch directory:\n\n```bash\nmkdir -p /tmp/greptimedb-fuzz-ci/$RUN_ID\ncd /tmp/greptimedb-fuzz-ci/$RUN_ID\n```\n\nFetch run metadata:\n\n```bash\ngh run view \"$RUN_ID\" --repo \"$REPO\" \\\n  --json databaseId,name,displayTitle,event,headBranch,headSha,status,conclusion,createdAt,updatedAt,url,workflowName \\\n  > run.json\n```\n\nFetch all jobs through the REST API:\n\n```bash\ngh api \"repos/$REPO/actions/runs/$RUN_ID/jobs?per_page=100\" \\\n  --paginate --jq '.jobs[]' \\\n  | jq -s '{jobs: .}' > jobs.json\n```\n\nList failed fuzz-like jobs:\n\n```bash\njq -r '\n  .jobs[]\n  | select(.conclusion != null and .conclusion != \"success\")\n  | select(.name | test(\"Fuzz Test|fuzz\"; \"i\"))\n  | [.id, .name, .status, .conclusion, .html_url] | @tsv\n' jobs.json\n```\n\nFor a known `JOB_ID`, capture job name and step outcomes:\n\n```bash\njq -r --arg id \"$JOB_ID\" '\n  .jobs[] | select((.id | tostring) == $id) |\n  \"job=\\(.name) conclusion=\\(.conclusion) url=\\(.html_url)\",\n  (.steps[]? | [.number, .name, .status, .conclusion, .started_at, .completed_at] | @tsv)\n' jobs.json > job-summary.txt\n```\n\nRecord the fuzz job kind, matrix mode, group, failed step, event type, branch, and\nhead SHA. Do not assume `main` for PR or merge-queue runs. The failed target is\nauthoritative in the downloaded artifact's `manifest.json`; do not guess it from\nthe group name.\n\n## 2. Download CI output for the failed job\n\nDownload the selected job's log; this is the authoritative target CI output for the\n`cargo fuzz run` command:\n\n```bash\ngh run view \"$RUN_ID\" --repo \"$REPO\" --job \"$JOB_ID\" --log > job-$JOB_ID.log\n```\n\nDownload the full workflow log only when setup/build context matters:\n\n```bash\ngh run view \"$RUN_ID\" --repo \"$REPO\" --log > run-$RUN_ID.log\n```\n\nFallback if `gh run view --log` is incomplete. The job logs endpoint returns a\nplain text stream, while the full run logs endpoint returns a zip archive:\n\n```bash\ngh api \"repos/$REPO/actions/jobs/$JOB_ID/logs\" > job-$JOB_ID.log\ngh api \"repos/$REPO/actions/runs/$RUN_ID/logs\" > run-$RUN_ID-logs.zip\nunzip -oq run-$RUN_ID-logs.zip -d run-logs\n```\n\nIn the job log, inspect the `Run Fuzz Test` step first. Capture:\n\n- exact target name;\n- `max_total_time`;\n- panic/assertion/backtrace or libFuzzer crash output;\n- generated crash/reproducer path if printed;\n- timeout or resource symptoms.\n\n## 3. Find and download fuzz artifacts\n\nList artifacts:\n\n```bash\ngh api \"repos/$REPO/actions/runs/$RUN_ID/artifacts?per_page=100\" \\\n  --paginate --jq '.artifacts[]' \\\n  | jq -s '{artifacts: .}' > artifacts.json\njq -r '.artifacts[] | [.id, .name, .expired, .size_in_bytes] | @tsv' artifacts.json\n```\n\nMatch the one group artifact by job kind, mode slug, and group slug. If the display\nname does not make the slug obvious, list the run artifacts and choose the unique\n`fuzz-*` artifact whose suffix matches the group. Do not search for separate Kind,\nmonitor, or CSV artifacts; these are target subdirectories in the group artifact.\n\nDownload exact artifacts when possible:\n\n```bash\nARTIFACT='fuzz-distributed-remote-wal-database-and-regular-table'\ngh run download \"$RUN_ID\" --repo \"$REPO\" --name \"$ARTIFACT\" --dir artifacts/\"$ARTIFACT\"\n```\n\nFallback by artifact id:\n\n```bash\nARTIFACT_ID=$(jq -r --arg name \"$ARTIFACT\" '.artifacts[] | select(.name == $name and (.expired | not)) | .id' artifacts.json | head -n 1)\nif [ -z \"$ARTIFACT_ID\" ]; then\n  echo \"Artifact not found or expired: $ARTIFACT\"\n  exit 1\nfi\ngh api \"repos/$REPO/actions/artifacts/$ARTIFACT_ID/zip\" > artifact-$ARTIFACT_ID.zip\nmkdir -p artifacts/\"$ARTIFACT\"\nunzip -oq artifact-$ARTIFACT_ID.zip -d artifacts/\"$ARTIFACT\"\n```\n\nAfter download, read the manifest before the bulk logs:\n\n```bash\njq -r '.[] | [.target, .status, .exit_code, .after_prior_failure, .artifact_collection] | @tsv' \\\n  artifacts/\"$ARTIFACT\"/manifest.json\n```\n\nSelect the first `failure` entry as the primary target. Entries with\n`skipped_after_failure` did not run; entries with `after_prior_failure=true` ran\nagainst a potentially contaminated shared environment. Note expired or missing\nartifacts explicitly; retention is currently short (`retention-days: 3`).\n\n## 4. Understand artifact contents\n\nEvidence for target `<target>` is rooted at\n`artifacts/$ARTIFACT/targets/<target>/`. `result.json` records timestamps, status,\nexit code, fuzz budget, prior-failure provenance, and artifact collection status.\n`fuzz.log` is the complete libFuzzer output from the prebuilt target executable in\nCI; local fallback runs it through `cargo fuzz run`.\n\nKind logs under `targets/<target>/kind/` come from an immediate failure snapshot\nand usually include Kubernetes state plus container logs. Prioritize:\n\n- GreptimeDB component logs: frontend, datanode, metasrv, flownode, monitor.\n- Kubernetes pod descriptions/events: restarts, OOMKilled, scheduling, image pull,\n  volume, DNS, and network issues.\n- External service logs: etcd, Kafka, Minio, Chaos Mesh, Kafka WAL helper.\n\nMonitor dumps under `targets/<target>/monitor/` are collected by\n`.github/scripts/collect-fuzz-monitor-artifacts.sh`. They may include:\n\n- `state.log`, `sql.log`, `copy.log`, `port-forward.log`;\n- `*.show_create_table.sql` for `_gt_logs` and OpenTelemetry trace tables;\n- parquet dumps for `_gt_logs`, `opentelemetry_traces`,\n  `opentelemetry_traces_operations`, and `opentelemetry_traces_services`.\n\nCSV and SQL traces are under `targets/<target>/csv/`. LibFuzzer crash or timeout\nreproducers are under `targets/<target>/libfuzzer/`. Kubernetes descriptions and\nevents are under `targets/<target>/kubernetes/`. Standalone service logs are under\n`targets/<target>/service/` when available.\n\n## 5. Build a layered diagnosis model\n\nBefore drilling into a specific error string, build a small macro-level model of\nthe failure. This prevents overfitting on the loudest symptom, such as\n`libFuzzer: deadly signal`, `Internal error`, or a cleanup warning.\n\n### 5.1 Create a failure timeline\n\nRecord the key timestamps in order:\n\n- job start and setup steps;\n- external dependencies ready, such as etcd, Kafka, Minio, and Chaos Mesh;\n- GreptimeDB cluster ready;\n- fuzz binary starts;\n- first generated operation or SQL visible in the target log;\n- first suspicious component log line;\n- user-visible fuzz failure or panic;\n- artifact collection and cleanup.\n\nUse the timeline to classify the failure phase: setup, workload execution,\nstorage/WAL/write path, query path, cleanup, or runner infrastructure.\n\n### 5.2 Trace the error propagation chain\n\nFor distributed fuzz failures, identify the chain from outer symptom to inner\ncause candidate. Prefer this shape:\n\n```text\nfuzz panic/assertion\n  -> client-visible SQL/gRPC/HTTP error\n  -> frontend or monitor error\n  -> meta/datanode/flownode component error\n  -> storage/WAL/object-store/Kafka/etcd/k8s dependency error\n```\n\nAt each boundary, decide whether that layer produced the error or merely wrapped\nand forwarded a downstream error. Do not stop at wrapper errors unless there is no\ndeeper evidence.\n\n### 5.3 Investigate by component boundary\n\nFor distributed targets, scan evidence in this order unless the timeline points\nelsewhere:\n\n1. fuzz target and generated operations;\n2. frontend request handling and SQL/error response;\n3. metasrv metadata/procedure/routing;\n4. datanode/mito2/metric-engine region execution;\n5. WAL, Kafka, object store, Minio, etcd, or other dependencies;\n6. Kubernetes pod state, restarts, scheduling, DNS, network, and runner resource\n   signals.\n\nFor each layer, answer two questions: \"is this the first causal error?\" and \"is\nthis layer producing or propagating the failure?\"\n\n### 5.4 Verify intended config against runtime behavior\n\nWhen a failure appears configuration-related, compare all three levels before\nclaiming a config did or did not take effect:\n\n1. the workflow/action/Helm/YAML config at the tested SHA;\n2. the actual pod/container runtime config or startup logs, if available;\n3. the source path that consumes the config, including hard-coded defaults and\n   library retry/backoff settings.\n\nIf a logged value differs from the expected config, explicitly state whether the\nconfig was not applied, was overridden by another layer, or refers to a different\nkind of timeout/retry/deadline.\n\n### 5.5 Keep competing hypotheses until evidence rules them out\n\nBefore final classification, list at least two plausible causes and the evidence\nthat supports or weakens each, for example:\n\n- product bug;\n- fuzz/test invalid assumption or missing wait;\n- config not applied or overridden;\n- dependency/infra flake;\n- timeout, retry, or resource pressure.\n\nThen choose the most likely cause and keep the confidence scoped. It is often\nvalid to have high confidence in the observed error chain but only medium\nconfidence in the ultimate cause category.\n\n### 5.6 State what would disprove the diagnosis\n\nInclude one or two falsifiable checks, such as a same-SHA rerun, a fixed\nreproducer, a cross-target pattern in the same dependency mode, or one missing\nartifact/log that would change the conclusion.\n\n## 6. Reduce evidence before reading source\n\nExtract high-signal lines from job logs and artifacts:\n\n```bash\ngrep -RInE '(^|[^[:alpha:]])(ERROR|Error|WARN|panic|panicked|FAILED|failure|timeout|Timeout|assert|backtrace|SIG[A-Z]+|OOMKilled|CrashLoopBackOff|ImagePullBackOff|libFuzzer|AddressSanitizer|UndefinedBehaviorSanitizer)' \\\n  job-$JOB_ID.log run-$RUN_ID.log job-logs run-logs artifacts 2>/dev/null > signals.txt\n```\n\nThen identify the first causal failure, not the last cleanup error. A typical order:\n\n1. `cargo fuzz run` output in `job-$JOB_ID.log`.\n2. GreptimeDB component logs around the same timestamp.\n3. Kubernetes events/restarts/OOMs.\n4. Monitor/CSV dumps that show the generated SQL or cluster state.\n\nAvoid common mistakes:\n\n- Do not blame cleanup errors after the fuzz failure.\n- Do not treat expected retry warnings as root cause unless they align with the\n  assertion, crash, or timeout.\n- Do not assume an artifact is relevant only because it contains `fuzz`; match mode\n  and target.\n- Do not diagnose from current local `main` if the run used a different SHA.\n\n## 7. Inspect the tested source revision\n\nRead the tested SHA:\n\n```bash\nHEAD_SHA=$(jq -r '.headSha // empty' run.json)\nif [ -z \"$HEAD_SHA\" ]; then\n  echo \"Error: HEAD_SHA is empty or null\"\n  exit 1\nfi\n\nUPSTREAM_REMOTE=$(git remote -v \\\n  | awk '/GreptimeTeam\\/greptimedb(\\.git)?\\/?([[:space:]])/ { print $1; exit }')\nUPSTREAM_REMOTE=${UPSTREAM_REMOTE:-origin}\n\ngit rev-parse --verify \"$HEAD_SHA^{commit}\" >/dev/null 2>&1 \\\n  || git fetch \"$UPSTREAM_REMOTE\" \"$HEAD_SHA\"\n```\n\nIf local `HEAD` differs, inspect files at that SHA or create a temporary worktree:\n\n```bash\ngit worktree add --detach /tmp/greptimedb-fuzz-ci/$RUN_ID/source \"$HEAD_SHA\"\n```\n\nRemove temporary worktrees after investigation with `git worktree remove <path>`.\nNever overwrite the user's current branch.\n\n## 8. Map logs to GreptimeDB code\n\nStart from concrete strings: fuzz target name, panic text, assertion message,\nerror variant, SQL statement, or log message. Search/read the matching source at\nthe run SHA.\n\nUseful entry points:\n\n- `tests-fuzz/` — fuzz targets, operation generators, checkers, dump paths.\n- `.github/actions/fuzz-test/action.yaml` — exact CI fuzz runner configuration.\n- `.github/workflows/integration.yml` — fuzz matrix and artifact upload names.\n- `.github/scripts/collect-fuzz-monitor-artifacts.sh` — monitor dump contents.\n- `src/mito2/AGENTS.md` — storage/WAL/region failures.\n- `src/metric-engine/AGENTS.md` — metric/logical table failures.\n- `src/frontend/AGENTS.md` — SQL/protocol/query orchestration failures.\n- `src/meta-srv/AGENTS.md` — metadata/procedure/routing failures.\n- `src/flow/AGENTS.md` — flow failures.\n\nWhen reporting code evidence, cite the file/function/line and quote the log line\nthat reaches it. Separate confirmed facts from hypotheses.\n\n## 9. Classify the cause\n\nUse this taxonomy:\n\n- **Product bug**: GreptimeDB panics, returns wrong results, loses data, or violates\n  invariants under generated fuzz operations.\n- **Fuzz/test bug**: the fuzz target/generator/checker makes an invalid assumption\n  or fails to wait for an asynchronous condition it requires.\n- **Flake/race**: timing/order/resource issue, usually supported by timeouts,\n  retries, pod restarts, or prior similar failures.\n- **Environment/infra**: runner disk/OOM, Docker/kind, network, dependency install,\n  Kafka/Minio/etcd startup, or GitHub service issue.\n- **Unknown**: logs/artifacts are expired or evidence conflicts.\n\n## 10. Final response format\n\nUse this structure:\n\n```markdown\n## Summary\n- Likely cause: <one sentence>\n- Confidence: high|medium|low\n- Classification: product bug|fuzz/test bug|flake/race|environment/infra|unknown\n- Confidence basis: <which parts are confirmed vs inferred>\n\n## Evidence\n- `<job log or artifact path>`: quoted line(s)\n- `<source-file:line>`: relevant code path\n\n## Reasoning\n<short chain from fuzz target -> CI output -> artifact logs -> source code>\n\n## Alternative hypotheses\n- <hypothesis>: <supporting and weakening evidence>\n- <hypothesis>: <supporting and weakening evidence>\n\n## Next steps\n1. <minimal fix, reproduction command, or verification command>\n2. <rerun command or extra log needed, if any>\n\n## What would disprove this\n- <specific rerun, artifact, log line, or reproducer result that would change the diagnosis>\n```\n\nIf logs or artifacts cannot be downloaded because `gh` is not authenticated or the\nartifacts expired, say so directly and include the exact command/error.\n"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","rawUrl":"https://raw.githubusercontent.com/GreptimeTeam/greptimedb/HEAD/AGENTS.md","title":"AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"# AGENTS.md\n\nGuidance for coding agents (Claude Code, Codex, ...) and contributors working in\nthis repository. `CLAUDE.md` is a symlink to this file. If `.local/AGENTS.md` or\n`.local/CLAUDE.md` is present, you **MUST** read it as well — it holds personal\nor machine-local overrides (gitignored, not shared). To record a personal or\nmachine-local override, write it to `.local/AGENTS.md` (create `.local/` and the\nfile if absent), not to this shared file.\n\nGreptimeDB is an open-source, cloud-native observability database for unified\ncollection and analysis of metrics, logs, and traces. It is written in Rust and\nprovides sub-second querying at PB scale with high cost efficiency.\n\n## Core commands\n\n| Task | Command |\n| --- | --- |\n| Build (debug) | `make build` |\n| Build (release) | `make build RELEASE=true` |\n| Run standalone | `cargo run -- standalone start` |\n| Targeted Rust tests | `cargo nextest run -p <package>` (preferred over `cargo test`) |\n| Full local Rust suite | `make test` |\n| SQL tests | `cargo sqlness bare` (single case: `cargo sqlness bare -t <name>`) |\n| Format | `make fmt` (and `make fmt-toml` for TOML) |\n| Lint | `make clippy` (= `cargo clippy --workspace --all-targets --all-features -- -D warnings`) |\n| Type check | `make check` |\n\nToolchain: Rust (stable, pinned by `rust-toolchain.toml`), Protobuf compiler (>= 3.15), C/C++ build essentials.\nInstall the test runner with `cargo install cargo-nextest --locked`.\n\n## Repo map\n\nGreptimeDB is a Cargo workspace rooted at the repository; most crates live under\n`src/` (plus `tests-fuzz`, `tests-integration`, `tests/runner`). Key areas:\n\n- **Frontend / protocol**: `src/frontend/` (request orchestration), `src/servers/`\n  (wire protocols), `src/sql/` (SQL parsing)\n- **Storage engines**: `src/mito2/` (main time-series engine), `src/metric-engine/`\n  (metrics), `src/file-engine/`\n- **Coordination**: `src/meta-srv/` (metadata & cluster control), `src/meta-client/`\n- **Execution / statements**: `src/operator/` (DDL/DML, request conversion, procedures)\n- **Stream / transform**: `src/flow/` (continuous aggregation), `src/pipeline/`\n- **Query / index**: `src/query/`, `src/promql/`, `src/index/`\n- **Shared**: `src/common/`, `src/datatypes/`, `src/store-api/` (engine contract),\n  `src/catalog/`, `src/table/`\n\nBefore editing a path, read every `AGENTS.md` from the repository root down to\nthat path. The root guide always applies; a nested guide adds or overrides rules\nfor its subtree. Changes spanning multiple subtrees must follow every applicable\nguide.\n\n`AGENTS.md` files are maps, not manuals. Keep them short and stable: record\nwhere code lives, important boundaries or change-coupling, high-cost gotchas,\nand the validation entry point. Implementation details belong in code. Do not\ncopy exhaustive test lists or workflow logic; link to the source of truth.\n\nHigh-change areas and specialized test suites carry their own `AGENTS.md` with\na module map, read/write paths, change-coupling points, and gotchas:\n\n- [`src/common/meta/AGENTS.md`](src/common/meta/AGENTS.md)\n- [`src/query/AGENTS.md`](src/query/AGENTS.md)\n- [`src/servers/AGENTS.md`](src/servers/AGENTS.md)\n- [`src/operator/AGENTS.md`](src/operator/AGENTS.md)\n- [`src/mito2/AGENTS.md`](src/mito2/AGENTS.md)\n- [`src/metric-engine/AGENTS.md`](src/metric-engine/AGENTS.md)\n- [`src/flow/AGENTS.md`](src/flow/AGENTS.md)\n- [`src/frontend/AGENTS.md`](src/frontend/AGENTS.md)\n- [`src/meta-srv/AGENTS.md`](src/meta-srv/AGENTS.md)\n- [`tests-integration/AGENTS.md`](tests-integration/AGENTS.md)\n- [`tests/compatibility/AGENTS.md`](tests/compatibility/AGENTS.md)\n- [`tests/perf/AGENTS.md`](tests/perf/AGENTS.md)\n\n## Rust style\n\nFollow [`docs/style-guide.md`](docs/style-guide.md) for Rust style, module\ndesign, naming, and reuse guidance.\n\n## Read before changing code\n\n- [`.agents/architecture-invariants.md`](.agents/architecture-invariants.md) —\n  repo-wide rules that are easy to violate and expensive to get wrong (persisted/\n  wire format compatibility, crate layering, async runtimes, error handling,\n  feature gating, the DataFusion fork).\n- [`.agents/generated-files.md`](.agents/generated-files.md) — tool-generated\n  artifacts that must not be hand-edited (sqlness `.result`, `config/config.md`,\n  Grafana dashboards, proto).\n- [`docs/style-guide.md`](docs/style-guide.md) — code style.\n- [`CONTRIBUTING.md`](CONTRIBUTING.md) — contribution flow and CLA.\n\n## High-signal entry points\n\n- Main binary: `src/cmd/src/bin/greptime.rs`\n- Configuration: `src/common/config/`, example TOMLs in `config/`\n- Error handling: `src/common/error/` (`ErrorExt`, `StatusCode`)\n- Protocol implementations: `src/servers/src/`\n\n## Worktree safety\n\n- Check `git status --short` before editing. Preserve unrelated tracked changes\n  and untracked files; re-read files changed by another process.\n- Do not rewrite history, force-push, or remove files in bulk unless the user\n  explicitly requests it.\n- Update generated artifacts only through the generators documented in\n  [`.agents/generated-files.md`](.agents/generated-files.md).\n\n## Validation by change type\n\nUse the narrowest command that covers the change, then expand only when its\nblast radius requires it.\n\n| Change | Minimum validation |\n| --- | --- |\n| One Rust crate | `cargo nextest run -p <package>` |\n| Cross-workspace Rust behavior | `make test`; inspect `.github/workflows/rust.yml` when CI parity matters |\n| SQL parsing, planning, execution, or output | `cargo sqlness bare -t <case>`; inspect regenerated `.result` files |\n| Persisted metadata or wire format | Add/run a case under `tests/compatibility/`; follow its `README.md` and `AGENTS.md` |\n| Public configuration | Update example TOMLs, loading/serialization snapshots, and docs; run `make config-docs` |\n| Query regression harness or DSL | Follow `tests/perf/AGENTS.md` |\n| Enterprise-gated code | Build/test with `--features enterprise` where applicable and run `make check-enterprise-license` |\n\nFor import/export, COPY, or snapshot-storage changes, follow the Windows\nportability and validation guidance in\n[`tests-integration/AGENTS.md`](tests-integration/AGENTS.md).\n\n## Before opening a PR\n\n1. If you added a `.rs`, `.py`, or `.ts` file, apply and verify its license\n   header with `hawkeye format` followed by `hawkeye check`. Use the inception\n   year from `licenserc.toml`, not the current year.\n2. `make fmt`\n3. `make clippy`\n4. `make test`\n5. `make check-udeps` (run `make fix-udeps` if it reports unused dependencies).\n6. If you added or changed a public configuration option, update the applicable\n   example TOMLs, configuration-loading and serialized-config snapshot tests,\n   and related user-facing documentation. Run `make config-docs` (needs Docker)\n   and commit the regenerated `config/config.md`.\n7. If you changed a persisted or wire format, add a compatibility test case (see\n   `.agents/architecture-invariants.md`).\n8. If you added or gated an enterprise-only file, give it the enterprise license\n   header, list it in `licenserc-enterprise.toml` (`includes`) and\n   `licenserc.toml` (`excludes`), and run `make check-enterprise-license`.\n9. Use a conventional-commit title, sign off commits (`git commit -s`), and sign\n   the CLA.\n10. When creating or updating a pull request, follow\n   [`.github/pull_request_template.md`](.github/pull_request_template.md): include\n   the CLA statement, fill the change-intention section with enough detail, and\n   update checklist items accurately. \"This PR requires documentation updates\"\n   refers to the official docs site repository\n   ([`GreptimeTeam/docs`](https://github.com/GreptimeTeam/docs)), not to rustdoc\n   or comments in this repo. Check it only when that site would be wrong or\n   incomplete without a matching change; a change users cannot observe does not\n   need it.\n11. If the change must also land on release branches, add the matching\n   `backport-<target>` labels (e.g. `backport-v1.3` targets `release/v1.3`). The\n   Backport workflow (`.github/workflows/backport.yml`) cherry-picks a merged PR\n   onto each target branch and opens a backport PR automatically; on conflicts\n   it aborts and files an issue for a manual backport.\n\n## More\n\n- Agent skills and resources: [`.agents/`](.agents/) (see [`.agents/README.md`](.agents/README.md))\n- Architecture decisions: [`docs/rfcs/`](docs/rfcs/)\n- How-to guides: [`docs/how-to/`](docs/how-to/)\n","isInternal":false,"tokens":2163,"sizeBytes":8393},{"name":"SKILL.md","path":".agents/skills/greptimedb-development-docker-image/SKILL.md","rawUrl":"https://raw.githubusercontent.com/GreptimeTeam/greptimedb/HEAD/.agents/skills/greptimedb-development-docker-image/SKILL.md","title":"greptimedb-development-docker-image","category":"anthropic-skill","format":"markdown","content":"---\nname: greptimedb-development-docker-image\ndescription: Builds a development-only GreptimeDB Docker image from a local debug binary for local-cluster testing, and optionally pushes it to a development registry. Use when the user asks to package, build, tag, publish, or cross-build a non-release GreptimeDB or GreptimeDB Enterprise image for debugging.\ncompatibility: Requires Docker with Buildx for cross-platform builds; Podman is supported for native-platform builds.\nmetadata:\n  protocols: docker buildx podman\n  platforms: linux-amd64 linux-arm64\n---\n\n# GreptimeDB Development Docker Image\n\n## Goal\n\nPackage a locally built GreptimeDB binary into a **development-only** Docker\nimage for debugging and local-cluster testing, optionally push it to a\ndevelopment registry, and retain the non-secret image settings in `.env` for\nthe next build. It is **not** a release-image workflow and must not be used to\npublish a production or release artifact.\n\nThis skill follows the documented development-image build procedure:\n\n- Build `greptime` with Cargo's `nightly` profile.\n- Copy the target binary into the Docker build context as `./greptime`.\n- Build the supplied `Dockerfile`, which uses Ubuntu 24.04 only as the runtime\n  base image and exposes the `greptime` binary as its entrypoint.\n\n## Inputs\n\nCollect or discover the following before any build or push:\n\n| Input | Discovery and rule |\n| --- | --- |\n| Source repository | Treat the directory in which this skill is invoked as the default workspace. Include it as an editable field in the batch configuration; do not ask a separate confirmation. Do not assume open-source versus Enterprise. |\n| Edition and binary | Default to `greptime`; use the binary name the user requests for Enterprise builds. The copied Docker-context filename must always be `greptime`. |\n| Cargo profile and binary source | Include this in the batch configuration. Default to rebuilding `nightly`; reuse an existing binary only after the user explicitly accepts that its freshness is unverified. |\n| Target platform | Preselect `linux/amd64` unless Docker's server is `linux/arm64`, then preselect `linux/arm64`. Let the user override it in the batch configuration. Ubuntu 24.04 is the runtime base image, not a target-platform choice. Each image has exactly one target platform, but it may differ from the host platform. |\n| Build mode | Ask whether the user wants a locally loadable debug image or a registry push. |\n| Registry/repository | Read `IMAGE_REGISTRY` and `IMAGE_REPOSITORY` only from the selected workspace's `.env` and prefill them in the batch configuration. Example: `registry.example.com/team` + `greptimedb-dev`. |\n| Tag | Read `IMAGE_TAG` only from the selected workspace's `.env` and prefill it in the batch configuration. When the tag exists in the selected registry, preselect an incremented version. |\n\nUse the image reference `${IMAGE_REGISTRY}/${IMAGE_REPOSITORY}:${IMAGE_TAG}`.\nIf the registry is intentionally empty, omit its slash rather than producing a\nleading slash.\n\n## Interactive Workflow\n\nUse the platform's interactive prompt component for every question, selection,\nand confirmation. Do not ask an open-ended text question when a single-choice,\nmulti-select, or confirmation component can represent the decision. If the\nplatform does not provide an interactive component, use the equivalent numbered\nor lettered prompt below and wait for input before continuing.\n\n### Batch configuration\n\nMinimize user round trips: run the collector first against the invocation\ndirectory, then use one interactive form or batched prompt to collect workspace,\nedition/binary, profile/reuse-or-rebuild choice, target platform, build mode,\nregistry/repository, tag, and whether to inspect the configured registry tag.\nUse the invocation directory as the editable workspace default. Prefill\ncollector values and mark recommended defaults. If the user changes workspace,\nrerun the collector for that workspace without asking another configuration\nquestion. Only ask a follow-up if information is missing or invalid, or a safety\ngate is required. Treat unchanged prefilled values as accepted.\n\nKeep separate confirmation components only for elevated privileges: `sudo`,\npackage installation, and privileged QEMU setup. Use one final confirmation for\nall non-privileged selected work.\n\nAlways offer **Cancel** for an action that can write state, build, push, or\nrequire elevated privileges. Display the relevant preview before the user\nconfirms it: source/binary path, `file` architecture result, platform(s), image\nreference, build mode, and non-secret `.env` values.\n\n### Batch configuration fields\n\nUse single-choice controls inside the one batch form for mutually exclusive\nchoices:\n\n| Decision | Required choices |\n| --- | --- |\n| Cargo output | Rebuild `nightly` (recommended), reuse an existing binary only with a freshness-unverified acknowledgement, choose a profile/binary |\n| Target platform | `linux/amd64` (default unless Docker server is arm64), `linux/arm64` (default when Docker server is arm64). The selected image may be cross-built with Buildx. |\n| Build mode | Load locally (recommended), push to development registry |\n| Registry/repository | Use `.env` defaults, enter new values |\n| Tag | Use proposed increment when the configured tag exists, enter a version |\n\nWhen push is selected and the image configuration is complete, automatically\nrun the read-only registry-tag check. It may return `unknown`; do not treat that\nas a missing tag.\n\nUse one final confirmation for all selected non-privileged operations:\n\n- persist changed non-secret image settings to `.env`;\n- run the selected Cargo build or reuse the resolved binary;\n- prepare the Docker context;\n- build, verify, and, when selected, push the image.\n\nThe final confirmation must clearly state: **“This creates a development and\nlocal-cluster test image, not a release or production artifact.”**\n\nFor a real multi-select only, use lettered choices (`[A]`, `[B]`) and accept\n`all`, `none`, or `cancel`; otherwise use single-choice components.\n\n### Preflight context collection\n\nBefore asking the profile, registry, or tag questions, run the bundled,\nread-only collector. It works on macOS and Linux, does not invoke `cargo build`,\nand emits JSON that can be shown or summarized to the user:\n\n```bash\npython3 <skill-dir>/scripts/collect_context.py \\\n  --source <source-checkout> \\\n  --bin greptime \\\n  --profile nightly \\\n  [--target <rust-target>]\n```\n\nUse its report to identify:\n\n- missing `IMAGE_REGISTRY`, `IMAGE_REPOSITORY`, or `IMAGE_TAG` values in the\n  selected workspace's `.env`;\n- Cargo binary targets available in the workspace and whether the requested\n  `--bin` exists;\n- the resolved Cargo target directory, expected profile output path, and whether\n  that binary already exists; and\n- macOS/Linux host architecture plus Docker's server platform when Docker is\n  available.\n\nWhen push is selected and image configuration is complete, rerun the collector\nwith `--check-registry-tag` and all selected image values. It uses the selected\nengine's read-only manifest inspection and returns `exists` or `unknown`;\n`unknown` includes an unavailable registry, an absent tag, or missing\nauthentication and must not be treated as an absent tag.\n\n```bash\npython3 <skill-dir>/scripts/collect_context.py \\\n  --source <source-checkout> \\\n  --bin <binary> \\\n  --profile <profile> \\\n  --engine <docker|podman> \\\n  --registry <registry> \\\n  --repository <repository> \\\n  --tag <tag> \\\n  --check-registry-tag\n```\n\nWhen the status is `exists` and the tag ends in a number, preselect the\ncollector's `candidate_tag` as the next development tag. The user must still\nconfirm it before `.env` is updated or an image is built. Never use automatic\nincrementing as permission to overwrite or push an existing image.\n\nIf Cargo metadata or the requested binary target is unavailable, stop before\nbuilding and ask the user to correct the source path or binary target.\n\nThe profile/binary choice is a field in the batch configuration, not a separate\nquestion. Its default is the existing `nightly` output when present:\n\n```text\nQuestion: Which Cargo build output should package this image?\nOptions:\n- Rebuild nightly (Recommended): run Cargo with `--profile nightly`, then use its output.\n- Reuse nightly output: use the existing output only after showing its path, modification time, size, and matching platform; freshness is unverified.\n- Choose profile or binary: provide a different Cargo profile or an explicit binary path.\n```\n\nDo not infer that a binary under `target/debug`, `target/release`, or another\nprofile is suitable. For an existing binary, show its resolved path and run\n`file` before asking for final build confirmation.\n\n## Environment State\n\nThe image configuration lives only in the selected workspace's `.env`. The\ncollector reads only the following keys and never reads or displays other `.env`\nvalues:\n\n```dotenv\nIMAGE_REGISTRY=registry.example.com/team\nIMAGE_REPOSITORY=greptimedb-dev\nIMAGE_TAG=dev-001\n```\n\n1. Read `.env` if it exists. Ignore blank lines and `#` comments.\n2. Show the discovered registry, repository, and proposed tag to the user.\n3. Include registry/repository and tag in the batch configuration even when\n   values already exist; saved values are defaults, not authorization to reuse\n   them.\n4. Compare the selected configuration with the three current managed values. If\n   all match, skip `.env` confirmation and do not update the file. Otherwise,\n   preview only the changed `IMAGE_*` values, request confirmation, and update\n   the file while preserving unrelated entries and comments.\n5. Never put registry credentials, access tokens, passwords, or `docker login`\n   output in `.env`, build commands, logs, or responses. Ask the user to log in\n   themselves if a push needs authentication.\n\nFor a repeated build, offer an incremented tag before asking. Use\n`scripts/next_image_tag.py --tag <existing-tag>` to calculate it. It increments\nthe final numeric component while preserving zero padding:\n\n```text\nweny-2025-0715-01 -> weny-2025-0715-02\nv0.1.4 -> v0.1.5\ndebug-009 -> debug-010\n```\n\nIf the saved tag has no trailing number, ask the user for the next tag rather\nthan inventing a versioning convention. Do not overwrite an existing image tag\nwithout explicit user confirmation.\n\nOnly if a managed value is missing or differs, persist the values with the\nbundled helper after confirmation instead of hand-editing the file:\n\n```bash\npython3 <skill-dir>/scripts/update_image_env.py \\\n  --env <source-checkout>/.env \\\n  --registry <registry> \\\n  --repository <repository> \\\n  --tag <tag>\n```\n\n## Build Procedure\n\n### 1. Inspect the host and container tooling\n\nRun these checks and report the selected path:\n\n```bash\nuname -s\nuname -m\ndocker version --format '{{.Server.Os}}/{{.Server.Arch}}'\ndocker buildx version\ndocker buildx inspect --bootstrap\n```\n\nFor a native-platform build, Podman can be used if Docker is unavailable. For\nany non-native request, require Docker Buildx. Do not silently fall back to a\nnative build when the requested image platform differs.\n\nIf the requested target is unavailable, stop and ask the user to configure a\nBuildx builder and cross-compilation toolchain externally. Do not automate\nprivileged QEMU or binfmt setup, and never run an unpinned privileged image.\n\n### 2. Build the executable for the image platform\n\nFrom the source checkout, build the requested binary. For open-source amd64:\n\n```bash\n<skill-dir>/scripts/build_binary.sh \\\n  --source <source-checkout> --package cmd --bin greptime --profile nightly\n```\n\nFor open-source arm64:\n\n```bash\n<skill-dir>/scripts/build_binary.sh \\\n  --source <source-checkout> \\\n  --package cmd \\\n  --bin greptime \\\n  --profile nightly \\\n  --target aarch64-unknown-linux-gnu\n```\n\nFor Enterprise, use the user-provided executable target, for example:\n\n```bash\n<skill-dir>/scripts/build_binary.sh \\\n  --source <source-checkout> --bin greptime-ent-cloud --profile nightly\n```\n\nFor a user-selected profile, replace `nightly` in the helper call and resolve\nthe binary under `target/<profile>/<binary>` (or\n`target/<rust-target>/<profile>/<binary>` for cross-compilation). Reuse the\nexisting target file only when the interactive profile question selected reuse;\notherwise invoke the helper to rebuild it.\n\nDo not use a binary compiled for the host architecture in an image intended for\nanother architecture. Determine the binary path from the Cargo target and\nprofile, then copy it into the Docker context as `greptime`:\n\n```bash\ncp <source-target-binary> <build-context>/greptime\n```\n\nVerify it before building the image with `file <build-context>/greptime`; its\nreported architecture must match the requested target platform.\n\nCreate a fresh isolated build context (never use the repository root) and use\nthe bundled preparation script. The helper rejects an existing Dockerfile or\nbinary output to prevent accidental context reuse:\n\n```bash\n<skill-dir>/scripts/prepare_context.sh \\\n  --context \"$(mktemp -d)\" \\\n  --binary <source-target-binary> \\\n  --dockerfile <skill-dir>/assets/Dockerfile \\\n  --platform <platform>\n```\n\n### 3. Build or push the image\n\nRun the command only after the user confirms the final image reference and\nwhether it should be pushed.\n\nUse the bundled build helper rather than spelling out individual Docker or\nPodman commands. It follows the existing scripts' Docker-first/Podman-fallback\nbehavior, uses Buildx for a non-native Docker request, and accepts exactly one\ntarget platform per image:\n\n```bash\n<skill-dir>/scripts/build_image.sh \\\n  --context <build-context> \\\n  --image <image-reference> \\\n  --platform <platform> \\\n  --mode local|push\n```\n\n`--mode local` builds a native image locally or uses `docker buildx --load` for\na non-native image. `--mode push` pushes the selected single-platform image.\nThe helper never pushes in `local` mode.\n\nThis is intentionally a runtime image built from a precompiled binary, not a\nmulti-stage Dockerfile. Ubuntu 24.04 is the runtime base image only; select one\ntarget platform separately as `linux/amd64` or `linux/arm64`. Only introduce a\nmulti-stage Dockerfile if the user asks to compile within Docker or the local\nRust toolchain cannot build the required target. Keep the final Ubuntu 24.04\nruntime stage and existing entrypoint unless the user asks to change runtime\nbehavior.\n\nAt the final confirmation, repeat that the selected reference is a\n**development/local-cluster test image**, not a release artifact. If the user\nrequests a release or production image, stop and direct them to the release\nprocess instead.\n\n### 4. Verify the result\n\nFor a local image, use the selected engine to inspect its architecture and\nstart it with `--help`:\n\n```bash\ndocker image inspect <image-reference> --format '{{.Os}}/{{.Architecture}}'\ndocker run --rm <image-reference> --help\n```\n\nFor Podman, use `podman image inspect <image-reference>` and\n`podman run --rm <image-reference> --help`. For a pushed Podman image, use\n`podman manifest inspect <image-reference>`.\n\nFor a pushed image, inspect its remote manifest:\n\n```bash\ndocker buildx imagetools inspect <image-reference>\n```\n\nReport the final image reference, target platform, source binary path, and\nwhether the image was loaded locally or pushed.\n\n## Safety Rules\n\n- This skill is only for development and local-cluster testing. Never present\n  its image as a production, release, or officially published artifact.\n- Never push by default. Local debug builds should use a local tag and load the\n  image unless the user explicitly requests a push.\n- Do not run `docker login`, privileged QEMU setup, package installation, or\n  overwrite a tag without confirmation.\n- Do not delete existing images, builders, binaries, or `.env` entries as part\n  of this workflow.\n- Stop and explain if Docker's server is unavailable, Buildx cannot support the\n  requested platform, or the copied binary architecture does not match the\n  image platform.\n","frontmatter":{"name":"greptimedb-development-docker-image","description":"Builds a development-only GreptimeDB Docker image from a local debug binary for local-cluster testing, and optionally pushes it to a development registry. Use when the user asks to package, build, tag, publish, or cross-build a non-release GreptimeDB or GreptimeDB Enterprise image for debugging.","compatibility":"Requires Docker with Buildx for cross-platform builds; Podman is supported for native-platform builds.","metadata":{"protocols":"docker buildx podman","platforms":"linux-amd64 linux-arm64"}},"isInternal":false,"tokens":3519,"sizeBytes":16005},{"name":"SKILL.md","path":".agents/skills/greptimedb-fuzz-ci-failure-investigation/SKILL.md","rawUrl":"https://raw.githubusercontent.com/GreptimeTeam/greptimedb/HEAD/.agents/skills/greptimedb-fuzz-ci-failure-investigation/SKILL.md","title":"greptimedb-fuzz-ci-failure-investigation","category":"anthropic-skill","format":"markdown","content":"---\nname: greptimedb-fuzz-ci-failure-investigation\ndescription: Investigate a failed GreptimeDB fuzz CI target link by downloading GitHub Actions job logs plus fuzz artifacts such as kind logs, monitor dumps, and CSV dumps, then correlate the failure with local GreptimeDB source code. Use when the user provides a failed fuzz CI target/job URL or asks to diagnose GreptimeDB fuzz CI failures.\n---\n\n# GreptimeDB Fuzz CI Failure Investigation\n\nInvestigate failed fuzz-test jobs for **`GreptimeTeam/greptimedb`** from a local\ncheckout. The purpose is to download the failed job's CI output and fuzz artifacts,\nthen explain the likely cause by correlating the evidence with GreptimeDB source.\n\nAlways pass `--repo GreptimeTeam/greptimedb` to `gh`; local remotes may point to\nforks. Keep the workflow read-only: do not rerun jobs, cancel workflows, push,\ncomment on PRs, or delete artifacts unless the user explicitly asks.\n\n## Scope\n\nUse this skill for fuzz CI only. In `.github/workflows/integration.yml`, fuzz failures\nare the CI jobs that use `.github/actions/fuzz-test`:\n\n- `fuzztest` — standalone fuzz targets.\n- `unstable-fuzztest` — unstable standalone fuzz target.\n- `distributed-fuzztest` — distributed cluster fuzz targets.\n- `distributed-fuzztest-with-chaos` — distributed fuzz targets with Chaos Mesh.\n\nEach matrix job runs a semantic group of targets on one prepared environment.\nThe group defaults to fail-fast: after the first target failure it captures that\ntarget's diagnostics and marks the remaining targets as skipped. The workflow\nstrategy still uses `fail-fast: false`, so failures do not cancel other groups.\n\nThe reusable action `.github/actions/fuzz-test/action.yaml` delegates each target\nto `.github/scripts/run-fuzz-targets.sh`. CI passes `FUZZ_BIN_DIR`, so the script\nruns the prebuilt target executable:\n\n```bash\n<prebuilt-fuzz-binary> -max_total_time=<seconds> \\\n  -artifact_prefix=<target-dir>/libfuzzer/\n```\n\nWithout `FUZZ_BIN_DIR`, such as during local reproduction, the script falls back\nto `cargo fuzz run <target> --fuzz-dir tests-fuzz -D -s none` with the same\nlibFuzzer arguments.\n\nFailed jobs upload one group artifact. Its stable name identifies the job kind,\nmode, and group, for example:\n\n```text\nfuzz-distributed-remote-wal-database-and-regular-table\n```\n\nThe artifact contains `manifest.json`, `summary.md`, and one directory per target\nunder `targets/<target>/`. A failed distributed target can include `fuzz.log`,\nlibFuzzer reproducers, CSV/SQL traces, Kind logs, monitor dumps, and Kubernetes\nstate. Setup failures use `targets/setup/`.\n\n## Inputs\n\nThe normal user input is a **CI target link**: a GitHub Actions job URL for one\nfailed matrix target. Treat that URL as the primary source of truth. Example:\n\n```text\nhttps://github.com/GreptimeTeam/greptimedb/actions/runs/29000666156/job/86062894741\n```\n\nAlso accept these fallback inputs:\n\n- A workflow run URL, e.g.\n  `https://github.com/GreptimeTeam/greptimedb/actions/runs/29000666156`.\n- A run id plus a fuzz target, mode name, artifact name, PR, branch, or commit SHA.\n\nParse ids from URLs:\n\n```bash\nREPO=GreptimeTeam/greptimedb\nRUN_ID=29000666156\nJOB_ID=86062894741\n```\n\nWhen the input is a job URL, do not ask for the target name first. Use the job id\nto fetch metadata, then derive the group and mode from the job name. A distributed\nfuzz job name usually contains the CI group tuple:\n\n```text\nFuzz Test (Distributed, <mode>, <group>)\nFuzz Test with Chaos (Distributed, <mode>, <group>)\n```\n\nIf only a run URL is provided, list failed jobs and choose the fuzz job matching the\ngroup/mode. Ask only when multiple fuzz jobs are plausible and there is no group,\nmode, artifact, or job-url clue.\n\n## Prerequisites\n\n- `gh` must be installed and authenticated (`gh auth status`). Actions logs and\n  artifacts commonly require authentication even for a public repo.\n- `jq`, `unzip`, and standard shell tools are useful for reducing logs.\n- Use the local GreptimeDB checkout for source analysis. If the run's head SHA is\n  not the current checkout, inspect the code at the run's SHA before making claims.\n- Use a scratch directory outside the source tree, such as `/tmp/greptimedb-fuzz-ci`.\n\n## 1. Capture run and job metadata\n\nCreate a scratch directory:\n\n```bash\nmkdir -p /tmp/greptimedb-fuzz-ci/$RUN_ID\ncd /tmp/greptimedb-fuzz-ci/$RUN_ID\n```\n\nFetch run metadata:\n\n```bash\ngh run view \"$RUN_ID\" --repo \"$REPO\" \\\n  --json databaseId,name,displayTitle,event,headBranch,headSha,status,conclusion,createdAt,updatedAt,url,workflowName \\\n  > run.json\n```\n\nFetch all jobs through the REST API:\n\n```bash\ngh api \"repos/$REPO/actions/runs/$RUN_ID/jobs?per_page=100\" \\\n  --paginate --jq '.jobs[]' \\\n  | jq -s '{jobs: .}' > jobs.json\n```\n\nList failed fuzz-like jobs:\n\n```bash\njq -r '\n  .jobs[]\n  | select(.conclusion != null and .conclusion != \"success\")\n  | select(.name | test(\"Fuzz Test|fuzz\"; \"i\"))\n  | [.id, .name, .status, .conclusion, .html_url] | @tsv\n' jobs.json\n```\n\nFor a known `JOB_ID`, capture job name and step outcomes:\n\n```bash\njq -r --arg id \"$JOB_ID\" '\n  .jobs[] | select((.id | tostring) == $id) |\n  \"job=\\(.name) conclusion=\\(.conclusion) url=\\(.html_url)\",\n  (.steps[]? | [.number, .name, .status, .conclusion, .started_at, .completed_at] | @tsv)\n' jobs.json > job-summary.txt\n```\n\nRecord the fuzz job kind, matrix mode, group, failed step, event type, branch, and\nhead SHA. Do not assume `main` for PR or merge-queue runs. The failed target is\nauthoritative in the downloaded artifact's `manifest.json`; do not guess it from\nthe group name.\n\n## 2. Download CI output for the failed job\n\nDownload the selected job's log; this is the authoritative target CI output for the\n`cargo fuzz run` command:\n\n```bash\ngh run view \"$RUN_ID\" --repo \"$REPO\" --job \"$JOB_ID\" --log > job-$JOB_ID.log\n```\n\nDownload the full workflow log only when setup/build context matters:\n\n```bash\ngh run view \"$RUN_ID\" --repo \"$REPO\" --log > run-$RUN_ID.log\n```\n\nFallback if `gh run view --log` is incomplete. The job logs endpoint returns a\nplain text stream, while the full run logs endpoint returns a zip archive:\n\n```bash\ngh api \"repos/$REPO/actions/jobs/$JOB_ID/logs\" > job-$JOB_ID.log\ngh api \"repos/$REPO/actions/runs/$RUN_ID/logs\" > run-$RUN_ID-logs.zip\nunzip -oq run-$RUN_ID-logs.zip -d run-logs\n```\n\nIn the job log, inspect the `Run Fuzz Test` step first. Capture:\n\n- exact target name;\n- `max_total_time`;\n- panic/assertion/backtrace or libFuzzer crash output;\n- generated crash/reproducer path if printed;\n- timeout or resource symptoms.\n\n## 3. Find and download fuzz artifacts\n\nList artifacts:\n\n```bash\ngh api \"repos/$REPO/actions/runs/$RUN_ID/artifacts?per_page=100\" \\\n  --paginate --jq '.artifacts[]' \\\n  | jq -s '{artifacts: .}' > artifacts.json\njq -r '.artifacts[] | [.id, .name, .expired, .size_in_bytes] | @tsv' artifacts.json\n```\n\nMatch the one group artifact by job kind, mode slug, and group slug. If the display\nname does not make the slug obvious, list the run artifacts and choose the unique\n`fuzz-*` artifact whose suffix matches the group. Do not search for separate Kind,\nmonitor, or CSV artifacts; these are target subdirectories in the group artifact.\n\nDownload exact artifacts when possible:\n\n```bash\nARTIFACT='fuzz-distributed-remote-wal-database-and-regular-table'\ngh run download \"$RUN_ID\" --repo \"$REPO\" --name \"$ARTIFACT\" --dir artifacts/\"$ARTIFACT\"\n```\n\nFallback by artifact id:\n\n```bash\nARTIFACT_ID=$(jq -r --arg name \"$ARTIFACT\" '.artifacts[] | select(.name == $name and (.expired | not)) | .id' artifacts.json | head -n 1)\nif [ -z \"$ARTIFACT_ID\" ]; then\n  echo \"Artifact not found or expired: $ARTIFACT\"\n  exit 1\nfi\ngh api \"repos/$REPO/actions/artifacts/$ARTIFACT_ID/zip\" > artifact-$ARTIFACT_ID.zip\nmkdir -p artifacts/\"$ARTIFACT\"\nunzip -oq artifact-$ARTIFACT_ID.zip -d artifacts/\"$ARTIFACT\"\n```\n\nAfter download, read the manifest before the bulk logs:\n\n```bash\njq -r '.[] | [.target, .status, .exit_code, .after_prior_failure, .artifact_collection] | @tsv' \\\n  artifacts/\"$ARTIFACT\"/manifest.json\n```\n\nSelect the first `failure` entry as the primary target. Entries with\n`skipped_after_failure` did not run; entries with `after_prior_failure=true` ran\nagainst a potentially contaminated shared environment. Note expired or missing\nartifacts explicitly; retention is currently short (`retention-days: 3`).\n\n## 4. Understand artifact contents\n\nEvidence for target `<target>` is rooted at\n`artifacts/$ARTIFACT/targets/<target>/`. `result.json` records timestamps, status,\nexit code, fuzz budget, prior-failure provenance, and artifact collection status.\n`fuzz.log` is the complete libFuzzer output from the prebuilt target executable in\nCI; local fallback runs it through `cargo fuzz run`.\n\nKind logs under `targets/<target>/kind/` come from an immediate failure snapshot\nand usually include Kubernetes state plus container logs. Prioritize:\n\n- GreptimeDB component logs: frontend, datanode, metasrv, flownode, monitor.\n- Kubernetes pod descriptions/events: restarts, OOMKilled, scheduling, image pull,\n  volume, DNS, and network issues.\n- External service logs: etcd, Kafka, Minio, Chaos Mesh, Kafka WAL helper.\n\nMonitor dumps under `targets/<target>/monitor/` are collected by\n`.github/scripts/collect-fuzz-monitor-artifacts.sh`. They may include:\n\n- `state.log`, `sql.log`, `copy.log`, `port-forward.log`;\n- `*.show_create_table.sql` for `_gt_logs` and OpenTelemetry trace tables;\n- parquet dumps for `_gt_logs`, `opentelemetry_traces`,\n  `opentelemetry_traces_operations`, and `opentelemetry_traces_services`.\n\nCSV and SQL traces are under `targets/<target>/csv/`. LibFuzzer crash or timeout\nreproducers are under `targets/<target>/libfuzzer/`. Kubernetes descriptions and\nevents are under `targets/<target>/kubernetes/`. Standalone service logs are under\n`targets/<target>/service/` when available.\n\n## 5. Build a layered diagnosis model\n\nBefore drilling into a specific error string, build a small macro-level model of\nthe failure. This prevents overfitting on the loudest symptom, such as\n`libFuzzer: deadly signal`, `Internal error`, or a cleanup warning.\n\n### 5.1 Create a failure timeline\n\nRecord the key timestamps in order:\n\n- job start and setup steps;\n- external dependencies ready, such as etcd, Kafka, Minio, and Chaos Mesh;\n- GreptimeDB cluster ready;\n- fuzz binary starts;\n- first generated operation or SQL visible in the target log;\n- first suspicious component log line;\n- user-visible fuzz failure or panic;\n- artifact collection and cleanup.\n\nUse the timeline to classify the failure phase: setup, workload execution,\nstorage/WAL/write path, query path, cleanup, or runner infrastructure.\n\n### 5.2 Trace the error propagation chain\n\nFor distributed fuzz failures, identify the chain from outer symptom to inner\ncause candidate. Prefer this shape:\n\n```text\nfuzz panic/assertion\n  -> client-visible SQL/gRPC/HTTP error\n  -> frontend or monitor error\n  -> meta/datanode/flownode component error\n  -> storage/WAL/object-store/Kafka/etcd/k8s dependency error\n```\n\nAt each boundary, decide whether that layer produced the error or merely wrapped\nand forwarded a downstream error. Do not stop at wrapper errors unless there is no\ndeeper evidence.\n\n### 5.3 Investigate by component boundary\n\nFor distributed targets, scan evidence in this order unless the timeline points\nelsewhere:\n\n1. fuzz target and generated operations;\n2. frontend request handling and SQL/error response;\n3. metasrv metadata/procedure/routing;\n4. datanode/mito2/metric-engine region execution;\n5. WAL, Kafka, object store, Minio, etcd, or other dependencies;\n6. Kubernetes pod state, restarts, scheduling, DNS, network, and runner resource\n   signals.\n\nFor each layer, answer two questions: \"is this the first causal error?\" and \"is\nthis layer producing or propagating the failure?\"\n\n### 5.4 Verify intended config against runtime behavior\n\nWhen a failure appears configuration-related, compare all three levels before\nclaiming a config did or did not take effect:\n\n1. the workflow/action/Helm/YAML config at the tested SHA;\n2. the actual pod/container runtime config or startup logs, if available;\n3. the source path that consumes the config, including hard-coded defaults and\n   library retry/backoff settings.\n\nIf a logged value differs from the expected config, explicitly state whether the\nconfig was not applied, was overridden by another layer, or refers to a different\nkind of timeout/retry/deadline.\n\n### 5.5 Keep competing hypotheses until evidence rules them out\n\nBefore final classification, list at least two plausible causes and the evidence\nthat supports or weakens each, for example:\n\n- product bug;\n- fuzz/test invalid assumption or missing wait;\n- config not applied or overridden;\n- dependency/infra flake;\n- timeout, retry, or resource pressure.\n\nThen choose the most likely cause and keep the confidence scoped. It is often\nvalid to have high confidence in the observed error chain but only medium\nconfidence in the ultimate cause category.\n\n### 5.6 State what would disprove the diagnosis\n\nInclude one or two falsifiable checks, such as a same-SHA rerun, a fixed\nreproducer, a cross-target pattern in the same dependency mode, or one missing\nartifact/log that would change the conclusion.\n\n## 6. Reduce evidence before reading source\n\nExtract high-signal lines from job logs and artifacts:\n\n```bash\ngrep -RInE '(^|[^[:alpha:]])(ERROR|Error|WARN|panic|panicked|FAILED|failure|timeout|Timeout|assert|backtrace|SIG[A-Z]+|OOMKilled|CrashLoopBackOff|ImagePullBackOff|libFuzzer|AddressSanitizer|UndefinedBehaviorSanitizer)' \\\n  job-$JOB_ID.log run-$RUN_ID.log job-logs run-logs artifacts 2>/dev/null > signals.txt\n```\n\nThen identify the first causal failure, not the last cleanup error. A typical order:\n\n1. `cargo fuzz run` output in `job-$JOB_ID.log`.\n2. GreptimeDB component logs around the same timestamp.\n3. Kubernetes events/restarts/OOMs.\n4. Monitor/CSV dumps that show the generated SQL or cluster state.\n\nAvoid common mistakes:\n\n- Do not blame cleanup errors after the fuzz failure.\n- Do not treat expected retry warnings as root cause unless they align with the\n  assertion, crash, or timeout.\n- Do not assume an artifact is relevant only because it contains `fuzz`; match mode\n  and target.\n- Do not diagnose from current local `main` if the run used a different SHA.\n\n## 7. Inspect the tested source revision\n\nRead the tested SHA:\n\n```bash\nHEAD_SHA=$(jq -r '.headSha // empty' run.json)\nif [ -z \"$HEAD_SHA\" ]; then\n  echo \"Error: HEAD_SHA is empty or null\"\n  exit 1\nfi\n\nUPSTREAM_REMOTE=$(git remote -v \\\n  | awk '/GreptimeTeam\\/greptimedb(\\.git)?\\/?([[:space:]])/ { print $1; exit }')\nUPSTREAM_REMOTE=${UPSTREAM_REMOTE:-origin}\n\ngit rev-parse --verify \"$HEAD_SHA^{commit}\" >/dev/null 2>&1 \\\n  || git fetch \"$UPSTREAM_REMOTE\" \"$HEAD_SHA\"\n```\n\nIf local `HEAD` differs, inspect files at that SHA or create a temporary worktree:\n\n```bash\ngit worktree add --detach /tmp/greptimedb-fuzz-ci/$RUN_ID/source \"$HEAD_SHA\"\n```\n\nRemove temporary worktrees after investigation with `git worktree remove <path>`.\nNever overwrite the user's current branch.\n\n## 8. Map logs to GreptimeDB code\n\nStart from concrete strings: fuzz target name, panic text, assertion message,\nerror variant, SQL statement, or log message. Search/read the matching source at\nthe run SHA.\n\nUseful entry points:\n\n- `tests-fuzz/` — fuzz targets, operation generators, checkers, dump paths.\n- `.github/actions/fuzz-test/action.yaml` — exact CI fuzz runner configuration.\n- `.github/workflows/integration.yml` — fuzz matrix and artifact upload names.\n- `.github/scripts/collect-fuzz-monitor-artifacts.sh` — monitor dump contents.\n- `src/mito2/AGENTS.md` — storage/WAL/region failures.\n- `src/metric-engine/AGENTS.md` — metric/logical table failures.\n- `src/frontend/AGENTS.md` — SQL/protocol/query orchestration failures.\n- `src/meta-srv/AGENTS.md` — metadata/procedure/routing failures.\n- `src/flow/AGENTS.md` — flow failures.\n\nWhen reporting code evidence, cite the file/function/line and quote the log line\nthat reaches it. Separate confirmed facts from hypotheses.\n\n## 9. Classify the cause\n\nUse this taxonomy:\n\n- **Product bug**: GreptimeDB panics, returns wrong results, loses data, or violates\n  invariants under generated fuzz operations.\n- **Fuzz/test bug**: the fuzz target/generator/checker makes an invalid assumption\n  or fails to wait for an asynchronous condition it requires.\n- **Flake/race**: timing/order/resource issue, usually supported by timeouts,\n  retries, pod restarts, or prior similar failures.\n- **Environment/infra**: runner disk/OOM, Docker/kind, network, dependency install,\n  Kafka/Minio/etcd startup, or GitHub service issue.\n- **Unknown**: logs/artifacts are expired or evidence conflicts.\n\n## 10. Final response format\n\nUse this structure:\n\n```markdown\n## Summary\n- Likely cause: <one sentence>\n- Confidence: high|medium|low\n- Classification: product bug|fuzz/test bug|flake/race|environment/infra|unknown\n- Confidence basis: <which parts are confirmed vs inferred>\n\n## Evidence\n- `<job log or artifact path>`: quoted line(s)\n- `<source-file:line>`: relevant code path\n\n## Reasoning\n<short chain from fuzz target -> CI output -> artifact logs -> source code>\n\n## Alternative hypotheses\n- <hypothesis>: <supporting and weakening evidence>\n- <hypothesis>: <supporting and weakening evidence>\n\n## Next steps\n1. <minimal fix, reproduction command, or verification command>\n2. <rerun command or extra log needed, if any>\n\n## What would disprove this\n- <specific rerun, artifact, log line, or reproducer result that would change the diagnosis>\n```\n\nIf logs or artifacts cannot be downloaded because `gh` is not authenticated or the\nartifacts expired, say so directly and include the exact command/error.\n","frontmatter":{"name":"greptimedb-fuzz-ci-failure-investigation","description":"Investigate a failed GreptimeDB fuzz CI target link by downloading GitHub Actions job logs plus fuzz artifacts such as kind logs, monitor dumps, and CSV dumps, then correlate the failure with local GreptimeDB source code. Use when the user provides a failed fuzz CI target/job URL or asks to diagnose GreptimeDB fuzz CI failures."},"isInternal":false,"tokens":4425,"sizeBytes":17746},{"name":"openai.yaml","path":".agents/skills/greptimedb-release-note/agents/openai.yaml","rawUrl":"https://raw.githubusercontent.com/GreptimeTeam/greptimedb/HEAD/.agents/skills/greptimedb-release-note/agents/openai.yaml","title":"Agents Skill","category":"anthropic-skill","format":"yaml","content":"interface:\n  display_name: \"GreptimeDB Release Notes\"\n  short_description: \"Generate GreptimeDB release notes\"\n  default_prompt: \"Use $greptimedb-release-note to generate the GreptimeDB changelog, curate highlights, and prepare the docs release-note PR.\"\n","isInternal":false,"tokens":59,"sizeBytes":255},{"name":"SKILL.md","path":".agents/skills/greptimedb-release-note/SKILL.md","rawUrl":"https://raw.githubusercontent.com/GreptimeTeam/greptimedb/HEAD/.agents/skills/greptimedb-release-note/SKILL.md","title":"greptimedb-release-note","category":"anthropic-skill","format":"markdown","content":"---\nname: greptimedb-release-note\ndescription: Generate a GreptimeDB release changelog (minor, patch, or alpha/beta/rc pre-release) with git cliff (correct range, subtract already-released patch PRs, rebuild contributors, add human-curated highlights and upgrade notes), output to a file, and prepare the docs-repo blog PR. Use when asked to write/generate a GreptimeDB release note or changelog.\n---\n\n# GreptimeDB Release Note / Changelog\n\nGenerate the changelog for a GreptimeDB release. Tooling: **`git cliff`** (config:\n`cliff.toml` in the greptimedb repo) and **`gh`**. Run inside the greptimedb checkout.\n\n**Prerequisites:** `git cliff`, `gh` (check `gh auth status`), and **Python** (the\nsubtract-PRs / rebuild-contributors step in §3–§4 is scripted). This skill writes\n`<remote>` for the git remote pointing at `GreptimeTeam/greptimedb` (often `upstream`,\nsometimes `origin`); resolve it with\n`git remote -v | grep -i 'GreptimeTeam/greptimedb' | awk '{print $1}' | head -1`.\n\n## 1. GitHub token (never print it)\n\n`git cliff` enriches commits with PR titles/authors via the GitHub API; for hundreds of\ncommits you need a token or you hit rate limits. Pass it inline and **never read or echo the\ntoken**:\n```\nGITHUB_TOKEN=$(gh auth token) git cliff ...\n```\nAlternatively the user sources an env file that exports `GITHUB_TOKEN` (don't read it). Without\na token it still runs, but may be rate-limited / incomplete.\n\n## 2. Determine the previous version (skip nightlies)\n\nFrom `gh release list --repo GreptimeTeam/greptimedb`, ignore `*-nightly-*` and build-suffixed\ntags (e.g. `v1.3.0-alpha.1-3c0e2a8d5-20260928-1790639248`).\n- Patch `vX.Y.Z` (Z>0): previous = `vX.Y.(Z-1)`.\n- New minor `vX.Y.0`: previous = the latest `vX.(Y-1).*` (for `v1.0.0`, the biggest `0.x`).\n- Pre-release `vX.Y.Z-{alpha,beta,rc}.N`: previous = the latest pre-release tag of the same\n  `vX.Y.Z` (e.g. `v1.3.0-beta.1` for `v1.3.0-beta.2`). If there is none (the first alpha),\n  see \"First pre-release\" in §3: the previous minor's `.0` may not be published yet, and since\n  v1.2 a `.0` tag sits on `release/vX.Y`, not on `main`.\nDouble-check with the user.\n\n## 3. Pick the range and generate\n\nKey topology fact: **patch tags** (`v1.0.1`, `v1.0.2`) live on `release/vX.Y` and are **NOT**\nancestors of `main` (they are cherry-picks with different SHAs). Up to v1.1 the `.0` tag was\nthe point where `release/vX.Y` left `main`; from v1.2 the branch is cut before the `.0`, and\npre-releases and the `.0` itself are tagged on the branch with backports. Verify with\n`git merge-base --is-ancestor <tag> <remote>/main`.\n\n### New minor (`X.Y.0`)\nThe note compares against the whole previous line: everything new since `release/vX.(Y-1)`\nleft `main`, minus what any published `vX.(Y-1).*` release (pre-releases, `.0`, patches)\nalready shipped. The line's own pre-releases (`vX.Y.0-beta.*`) are not subtracted; the `.0`\nnote covers the whole minor.\n\nBase = the **previous minor tag** (e.g. `v1.1.0`); tip = the tagged `vX.Y.0` commit, resolved as\nin \"Pre-release\" below. Split the range at the point where `release/vX.Y` left `main`:\n```\nbp=$(git merge-base <remote>/release/vX.Y <remote>/main)\n```\n- `<prev-minor-tag>..$bp` is on `main`; generate it with `git cliff`:\n  ```\n  GITHUB_TOKEN=$(gh auth token) git cliff <prev-minor-tag>..$bp --tag vX.Y.0 \\\n    -o /path/CHANGELOG-vX.Y.0.md\n  ```\n  `cliff.toml`'s `ignore_tags` folds in-range nightly tags into the single section.\n- `$bp..<tip>` is the release branch: backports, squashed picks, and commits without a PR.\n  `git cliff` fails on such commits (`Filter call 'sort' failed`), so build this part as a\n  pre-release: collect the original PRs with the loop in \"Pre-release\" and add their bullets.\n  List the commits that carry no PR with\n  `git log --format='%h %s' $bp..<tip> | grep -vE '\\(#[0-9]+\\)$'`; drop mechanical ones, and\n  for a manual port of a `main` PR (the body says `cherry picked from commit` a SHA that is not\n  on `main`), find that PR by title and cite it.\n\n**Then subtract PRs already shipped in published `vX.(Y-1).*` releases**: their `main` commits\nare inside the range. Build the set as for the first pre-release (below): the PR links in\nevery published `vX.(Y-1).*` release body, plus the `(#N)` line endings in the history of the\nnewest published `vX.(Y-1).*` tag. Remove every changelog bullet whose `#NNNN` is in that set (a\nsmall Python script is the reliable way — match `pull/<n>)` on lines starting with `*`).\n\n### Patch (`X.Y.Z`, Z>0, cherry-picks on the release branch)\nThe previous patch tag is an ancestor of the release branch, so a plain range works **when the\ncherry-picks land as individual commits**:\n```\nGITHUB_TOKEN=$(gh auth token) git cliff <prev-patch-tag>..<release-branch-tip> --tag vX.Y.Z -o ...\n```\nNo extra subtraction (the branch only contains the new cherry-picks).\n\n**Common case — squashed pick commit.** Patch branches are often a single squashed commit like\n`chore: pick fixes and bump version to vX.Y.Z (#NNNN)`, so the plain range above yields just\nthat one bullet. First **find the picked PRs**: read the squash commit message\n(`git log -1 --format=%B <release-branch-tip>`) — each `* fix: ... (#NN)` line is a picked PR.\nThen pick one of two ways to build the changelog (a patch is usually only a few PRs — **ask the\nuser which they prefer**):\n\n- **(a) Generate on `main` and filter** — lets `git cliff` produce correctly formatted bullets\n  (titles, authors, categories):\n  1. Locate and order the picked PRs' commits on `main` chronologically:\n     ```\n     git log --oneline <remote>/main --grep='#NN1' --grep='#NN2' --reverse\n     ```\n     The first line is the oldest pick, and the last line is the newest pick.\n  2. Run cliff over a `main` range covering all of them (base = parent of the oldest picked\n     commit, tip = the newest), then keep only the picked `#NN` bullets:\n     ```\n     GITHUB_TOKEN=$(gh auth token) git cliff <oldest-pick>~1..<newest-pick> --tag vX.Y.Z -o /tmp/raw.md\n     ```\n     The raw output includes unrelated PRs in the main range that were **not** picked — drop\n     them, then rebuild contributors (§4).\n- **(b) Hand-write the bullets** — a fine fallback for a small patch (and when the API/network is\n  flaky). For each picked PR fetch its title + author (`gh api repos/GreptimeTeam/greptimedb/pulls/<n>`)\n  and write the `* <title> by [@user] in [#NN](...)` lines plus the contributor list yourself.\n\nIf the branch history is otherwise messy, the same \"identify the picked PRs and keep only those\"\nrule applies.\n\n### First pre-release (`X.Y.0-alpha.1`, cut from main)\nThe base is the commit where the previous line's release branch left `main`. It exists when the\nalpha is cut, even if `vX.(Y-1).0` does not:\n```\nbase=$(git merge-base <remote>/release/vX.(Y-1) <tag>)\n```\nIf there is no `release/vX.(Y-1)`, use the newest published release tag that is an ancestor of\nthe tag (`git merge-base --is-ancestor <candidate> <tag>`). Run `git cliff <base>..<tag>`, then\nsubtract every PR that already shipped in a published `vX.(Y-1).*` release, pre-releases\nincluded. Take the union of the PR links in those release bodies and the `(#N)` line endings in\nthe history of the newest published `vX.(Y-1).*` tag:\n```\ngh release view <published-vX.(Y-1)-tag> --repo GreptimeTeam/greptimedb | grep -oE 'pull/[0-9]+'\ngit log <newest-published-vX.(Y-1)-tag> --format=%B | grep -oE '\\(#[0-9]+\\)$'\n```\nEither source alone misses PRs: a release body can omit a backport that its tag contains, and a\nbackport commit can name only the backport PR.\n\n### Pre-release (`X.Y.Z-{alpha,beta,rc}.N` after an earlier pre-release)\n**The tip is the tagged commit, not the release branch tip.** Resolve it from the remote and\nwrite this SHA in the docs PR body (§8) as what the note was checked against:\n```\ngit ls-remote --tags <remote> 'vX.Y.Z-beta.N*'\n```\nUse the `refs/tags/vX.Y.Z-beta.N` line (its `^{}` line if the tag is annotated), not the\nbuild-suffixed tags it also lists. PRs that land on `release/vX.Y` after the tag\n(`git log --oneline <tag>..<remote>/release/vX.Y`) belong to the next release, even if they are\nalready merged when you write the note.\n\nThe range `<prev-pre-release>..<tag>` usually mixes two kinds of commits: individual backports\n(`... [Backport release/vX.Y] (#NNNN)`) and squashed pick commits (`chore: backport selected\nchanges for ... (#NNNN)`). In both, the original PRs are the `(#NN)` at the end of body lines;\na backport commit can also carry prerequisite PRs. Collect the PR set, falling back to the\nsubject for commits whose body lists none:\n```\nfor c in $(git rev-list <prev-pre-release>..<tag>); do\n  prs=$(git log -1 --format=%b \"$c\" | grep -oE '\\(#[0-9]+\\)$')\n  [ -n \"$prs\" ] || prs=$(git log -1 --format=%s \"$c\" | grep -oE '\\(#[0-9]+\\)$')\n  echo \"$prs\"\ndone | tr -dc '0-9\\n' | sort -un\n```\nList the bullets under the **original** PRs (title and author of the PR merged to `main`), not\nthe backport PRs. Build them with patch (a) or (b) above; for (b), fetch\n`gh api repos/GreptimeTeam/greptimedb/pulls/<n> --jq '{number,title,author:.user.login,merged_at}'`,\ngroup by the `cliff.toml` parsers (first match wins), order by `merged_at`, and put `!` titles\nunder Breaking changes (§6).\n\nA squashed pick PR can also contain release-only commits that have no PR. List them:\n```\ngh api repos/GreptimeTeam/greptimedb/pulls/<squash-pr>/commits --paginate \\\n  --jq '.[].commit.message | split(\"\\n\")[0]' | grep -vE '\\(#[0-9]+\\)$'\n```\nMechanical ones (version bump, toolchain or lint adaptations; example:\n`fix: adapt release OptionMap iteration to Rust 1.98`) get no bullet. A release-only\ncommit that changes behavior gets a bullet in its group.\n\n> Flaky-network note for (a): `git cliff` fetches the repo's full commit history from the GitHub\n> API for author enrichment (paginates back thousands of commits, even for a tiny range). On a\n> flaky connection it can time out mid-pagination; it caches pages, so just **re-run until it\n> completes** — each retry resumes from the cache. If it keeps failing, fall back to (b).\n\n### Groups (all paths)\n`git cliff` drops PRs whose title isn't a conventional commit (`filter_unconventional`). List\nthem and add the user-facing ones by hand:\n```\ngit log --format=%s <range> | grep -E '\\(#[0-9]+\\)$' | grep -vE '^[a-z]+(\\([^)]*\\))?!?: '\n```\nUse only the groups in `cliff.toml`. `git cliff` prints a `### Build` section for `build:` commits\nbecause no parser matches them; move a user-facing `build(deps)` fix to Bug Fixes and the rest to\nMiscellaneous Tasks. Keep each bullet list free of blank lines; a blank line turns it into a\nloose list that renders with different spacing.\n\n## 4. Rebuild contributor sections after subtracting\n\n`cliff` computes `New Contributors` / `All Contributors` over the full range, so after removing\nlines, recompute from the **remaining** commit bullets:\n- **All Contributors** = sorted set of `@user` from remaining `* ... by [@user] ... in [#NN]`\n  lines (drop bots like `dependabot[bot]`).\n- **New Contributors** = drop any entry whose first-contribution PR was removed (their only\n  in-range PR shipped in a patch).\nDo this in the same script that subtracts the PRs.\n\nWhen the bullets are built by hand (§3 (b), pre-releases), `git cliff` computes no New\nContributors, and GitHub's generated notes don't either (they only see the backport PRs). An\nauthor is new if none of their merged PRs shipped before, i.e. none appears in the history of the\nbase tag:\n```\ngit log <base-tag> --format=%B | grep -oE '\\(#[0-9]+\\)$' | tr -dc '0-9\\n' | sort -u > /tmp/shipped.txt\ngh search prs --repo GreptimeTeam/greptimedb --author <user> --merged --limit 1000 \\\n  --json number --jq '.[].number' | sort -u > /tmp/author.txt\ncomm -12 /tmp/author.txt /tmp/shipped.txt   # empty => new contributor\n```\nRun it for every non-bot author in the release, and cite their earliest-merged PR in this\nrelease. \"Earliest merged PR is in range\" is not the same test: a contributor whose first PR\nmerged on `main` but was not picked into any release is still new when a later PR of theirs\nships.\n\n## 5. Verify\n\nCross-check the result against `git log <base>..<tip>`: no subtracted PR remains, and no\ngenuine new PR was dropped. Optionally drop the mechanical `chore: bump version to vX.Y.Z`\nline (ask the user; some prefer to keep it).\n\n## 6. Human-curated sections (insert after the `Release date:` line)\n\n**Style.** Follow the Google developer documentation style guide: sentence-case headings, active voice, present\ntense for behavior, American spelling, serial comma, code font for identifiers, bold only for UI\nnames. Keep it short:\n- Intro: two sentences at most, what the release covers and its main theme.\n- Highlight: one paragraph of up to four sentences, plus an example if it earns one (see below).\n- Upgrade note or breaking change: one bullet, one or two sentences: what changes, who is\n  affected, what to do.\n- Every sentence states a fact the reader acts on or checks. Delete sentences that restate the\n  heading or the previous sentence, transitions, and summaries.\n- No marketing words (`significantly`, `powerful`, `seamless`, `robust`, `enhanced`), no vague\n  verbs (`supports`, `handles`, `improves`; say what happens), no `several`/`many` when the number\n  is known. Keep the source's hedges; don't add new ones.\n\nExample:\n> Bad: This release significantly enhances write reliability by introducing a robust retry\n> mechanism that seamlessly handles transient failures.\n>\n> Good: In distributed clusters, the frontend retries a failed write up to three times with\n> exponential backoff. Set `max_retries = 0` to disable retries.\n\nMirror past release bodies (`gh release view v1.0.0 --repo GreptimeTeam/greptimedb`):\n- **Short intro** (see Style).\n- **`### 👍 Highlights`** — *few, deep* highlights, ranked by user impact. Fixes for silently\n  wrong query results, lost or invisible data, and security issues rank with new features and\n  can be highlights.\n  - Write a fix as its symptom and the affected deployments or usage, not the PR title. Example:\n    \"with no traffic, `errors / requests > 0.05` was true, so the alert fired\".\n  - State whether a feature is on by default. If an example config uses the default values, say\n    so.\n  - Give an example (SQL / TOML config) only where it helps readers act. Don't pick highlights,\n    or the example, by what is easy to demonstrate.\n  - One highlight covers one change or one user-visible problem. Don't merge unrelated PRs into\n    one entry because they touch the same component.\n  - Sources: the PR body's sections on impact, compatibility, and behavior changes, plus the\n    code and config it touches, not the title alone. Verify examples against the related docs\n    PR/page in the docs repo (`GreptimeTeam/docs`, often checked out locally),\n    `docs/reference/sql/*.md`, `config/*.example.toml`, `config/config.md`.\n  Do **not** mention implementation details, tiny features, or unfinished/experimental-but-not-ready\n  features. Always have the user review and edit the highlights; iterate.\n- **Dashboard** subsection — don't just bump the version. Read the dashboard PRs in the bundled\n  release (`gh release view <ver> --repo GreptimeTeam/dashboard`; then the linked PRs) and\n  describe the user-facing change. Use the names the UI shows, not the PR titles; check them in\n  the dashboard's locale strings at the bundled tag:\n  ```\n  gh api 'repos/GreptimeTeam/dashboard/contents/src/locale/en-US/menu.ts?ref=<dashboard-tag>' \\\n    --jq .content | base64 -d\n  ```\n  Screenshots need absolute `https://` URLs: the body is copied to the GitHub release, where\n  relative and site-root (`/img/...`) paths don't resolve.\n- **`### Upgrade notes`** and **`### Breaking changes`** — check every PR in the release for:\n  `!` titles; `Compatibility`, `Behavior change`, `Breaking` paragraphs in the body (often a\n  plain `Compatibility:` line, not a heading); unchecked \"API changes are backward compatible\" /\n  \"Schema or data changes are backward compatible\" boxes; new config options and their defaults;\n  changed defaults; persisted format changes; rolling-upgrade order; new hard limits. These\n  signals set reading order, not a filter: a limit can sit in plain prose with every box\n  checked, so read the body of every user-facing `feat`/`fix` PR. Examples of what is easy to\n  miss: a 512 MiB cap on decompressed request bodies (#9264); a default step for stepless\n  subqueries (#9481).\n  - **Upgrade notes**: steps operators take — upgrade order, index rebuilds, settings to review.\n  - **Breaking changes**: behavior that used to work and now errors or returns different\n    results. Put the `!` PR bullets here too, after these entries (see `v1.3.0-beta.1`).\n- **Benchmark results** (optional) — state the baseline version (often the previous formal\n  release, not the previous beta), data size, cache state, and hardware. Don't attribute results\n  to single PRs. Leave out results the benchmark marks as not for publication, and smoke-level\n  runs.\n\n## 7. Output\n\nWrite to `CHANGELOG-vX.Y.Z.md` (title `# vX.Y.Z`, which `--tag` produces) and **do not commit\nit**. The release runbook deletes it after the release + docs PR are done.\n\n## 8. Docs-repo blog variant + draft PR\n\nThe release note is also published as a blog post in **`GreptimeTeam/docs`**.\n- **Ask the user for their local `GreptimeTeam/docs` checkout path**; if they have none, offer\n  to clone (`git clone git@github.com:GreptimeTeam/docs.git <path>`).\n- File: `blog/release-X-Y-Z.md` (version with dashes; see `blog/release-1-0-0.md`). Pre-releases\n  use dashes throughout: `blog/release-1-3-0-beta-2.md`.\n- Content = docs frontmatter (below) followed by the GitHub release body, **keeping the\n  `# vX.Y.Z` H1 directly under the frontmatter**. The blog frontmatter has no `title:`, so\n  Docusaurus uses that H1 as the page title and sidebar label — omitting it makes the page\n  render as the filename (e.g. `release-1-1-0` instead of `v1.1.0`). Match the shape of\n  `blog/release-1-0-0.md`:\n  ```\n  ---\n  keywords: [release, GreptimeDB, changelog, vX.Y.Z]\n  description: GreptimeDB vX.Y.Z Changelog\n  date: YYYY-MM-DD\n  ---\n  # vX.Y.Z\n\n  Release date: ...\n  ```\n  The docs CI check `check-release-note-sync` requires the post minus its frontmatter to equal\n  the GitHub release body byte for byte, so the H1 goes on the line right after the closing\n  `---`, with no blank line.\n- The docs working tree may have unrelated WIP — **don't disturb it**: use a `git worktree` off\n  `origin/main`:\n  ```\n  git -C <docs> fetch origin main\n  git -C <docs> worktree add -b chore/X.Y.Z-release-note /tmp/docs-release-note origin/main\n  ```\n- **Read and follow the current docs repo PR template**\n  (`.github/pull_request_template.md`) from the checkout used for the release.\n  Fill in every required section; do not rely on hard-coded section names from an older\n  template. The checklist items are the author's to confirm: check the ones you did, and for one\n  that doesn't apply, check it and say why. For pre-releases, state the tagged SHA (§3) under\n  Verification.\n- **Build the site before opening the PR.** A new worktree has no `node_modules`. Install with\n  the pnpm version pinned in `package.json` (`packageManager`), as CI does:\n  ```\n  cd /tmp/docs-release-note\n  pm=$(jq -r .packageManager package.json)   # e.g. pnpm@8.6.0\n  npx -y \"$pm\" install --frozen-lockfile\n  npx -y \"$pm\" build\n  ```\n  If the build fails, fix it or stop; don't mark the PR ready for review.\n- Commit with sign-off, push, open a **draft** PR, then remove the worktree:\n  ```\n  git -C /tmp/docs-release-note add blog/release-X-Y-Z.md\n  git -C /tmp/docs-release-note commit -s -m \"docs: add X.Y.Z release note\"\n  git -C /tmp/docs-release-note push -u origin chore/X.Y.Z-release-note\n  gh pr create --draft --repo GreptimeTeam/docs --base main --head chore/X.Y.Z-release-note \\\n    --title \"docs: add X.Y.Z release note\" --body-file <template-filled body>\n  git -C <docs> worktree remove /tmp/docs-release-note\n  ```\n- Gotcha: `gh pr edit/create` may fail with an org-scope error if the token lacks `read:org`.\n  Edit the body via REST instead: `gh api repos/GreptimeTeam/docs/pulls/<n> -X PATCH -F body=@body.md`.\n","frontmatter":{"name":"greptimedb-release-note","description":"Generate a GreptimeDB release changelog (minor, patch, or alpha/beta/rc pre-release) with git cliff (correct range, subtract already-released patch PRs, rebuild contributors, add human-curated highlights and upgrade notes), output to a file, and prepare the docs-repo blog PR. Use when asked to write/generate a GreptimeDB release note or changelog."},"isInternal":false,"tokens":5592,"sizeBytes":20256},{"name":"openai.yaml","path":".agents/skills/greptimedb-release/agents/openai.yaml","rawUrl":"https://raw.githubusercontent.com/GreptimeTeam/greptimedb/HEAD/.agents/skills/greptimedb-release/agents/openai.yaml","title":"Agents Skill","category":"anthropic-skill","format":"yaml","content":"interface:\n  display_name: \"GreptimeDB Release\"\n  short_description: \"Publish a GreptimeDB release\"\n  default_prompt: \"Use $greptimedb-release to publish the next GreptimeDB release and coordinate the changelog, GitHub release, CI follow-up, and docs PR.\"\n","isInternal":false,"tokens":61,"sizeBytes":256},{"name":"SKILL.md","path":".agents/skills/greptimedb-release/SKILL.md","rawUrl":"https://raw.githubusercontent.com/GreptimeTeam/greptimedb/HEAD/.agents/skills/greptimedb-release/SKILL.md","title":"greptimedb-release","category":"anthropic-skill","format":"markdown","content":"---\nname: greptimedb-release\ndescription: Runbook for publishing a new GreptimeDB version (tag + GitHub release + docs release-note PR) on the upstream GreptimeTeam/greptimedb repo. Use when asked to \"release\" / \"publish\" a GreptimeDB version (e.g. v1.1.0, v1.0.3).\n---\n\n# GreptimeDB Release Runbook\n\nPublish a formal GreptimeDB release on **`GreptimeTeam/greptimedb`**. Always operate against\nthat repo (for `gh`, pass `--repo GreptimeTeam/greptimedb`). Changelog generation is a\nseparate, involved task — use the **`greptimedb-release-note`** skill for it.\n\nThis whole flow touches public infrastructure. Confirm with the user before the\noutward-facing step (creating the release, which creates the tag and triggers CI).\n\n## Prerequisites & remote\n\n- Tools: `gh` (check with `gh auth status`) and `git`. Changelog generation additionally\n  needs `git cliff` and Python — see the `greptimedb-release-note` skill.\n- **Resolve the remote first.** This runbook writes `<remote>` for the git remote pointing\n  at `GreptimeTeam/greptimedb`; substitute your actual name (often `upstream`, sometimes\n  `origin` on a direct clone):\n  ```\n  git remote -v | grep -i 'GreptimeTeam/greptimedb' | awk '{print $1}' | head -1\n  ```\n\n## 0. Inputs: version and branch\n\n- Ask the user for the **version** to release (e.g. `1.1.0`, `1.0.3`) and, if needed, the\n  **branch**.\n- **Branch is inferred from version**: `MAJOR.MINOR` → `release/v<MAJOR>.<MINOR>`.\n  - `v1.0.0 / v1.0.1 / v1.0.2` live on `release/v1.0`; `v1.1.0` lives on `release/v1.1`.\n  - Infer it, then double-check with the user.\n- **New minor/major (`X.Y.0`)** is cut from `main`. If `release/vX.Y` does not exist on\n  the remote, offer to create it from the intended `main` commit (with the user's consent):\n  `git push <remote> <commit>:refs/heads/release/vX.Y`.\n- **Patch (`X.Y.Z`, Z>0)** must use the existing `release/vX.Y` branch (it carries\n  cherry-picked commits).\n- Check what exists on the remote: `git ls-remote --heads <remote> 'release/*'`.\n\n## 1. Verify the Cargo version\n\nThe workspace version on the release branch **must** equal the version being released, or\nstop. **Fetch the branch first**, then read `FETCH_HEAD` directly — `git fetch <remote>\nrelease/vX.Y` only *opportunistically* updates the remote-tracking ref\n`<remote>/release/vX.Y` (and only when the remote has a matching configured refspec), so it\nmay be missing or stale (e.g. a custom `<remote>`, or a branch just created in §0). Reading\n`FETCH_HEAD` always reflects the tip just fetched:\n```\ngit fetch <remote> release/vX.Y\ngit show FETCH_HEAD:Cargo.toml | grep -A30 '\\[workspace.package\\]' | grep -m1 version\n```\n(For a fresh minor cut from main, `<remote>/main` and `release/vX.Y` are usually the same\ncommit.)\n\n## 2. Generate and curate the changelog\n\nUse the **`greptimedb-release-note`** skill. It produces `CHANGELOG-vX.Y.Z.md` (uncommitted)\nand the docs-blog variant. **Review the highlights with the user and iterate** before\npublishing.\n\n## 3. Create the GitHub release (creates the tag + triggers CI)\n\n**Do NOT pre-create the tag.** Creating the release creates the tag and fires the\ntag-push CI (`.github/workflows/release.yml`) that builds all binaries (**~hours**).\nConfirm with the user, then:\n```\ngh release create vX.Y.Z \\\n  --repo GreptimeTeam/greptimedb \\\n  --target release/vX.Y \\\n  --title \"Release vX.Y.Z\" \\\n  --notes-file CHANGELOG-vX.Y.Z.md \\\n  --prerelease\n```\n- Title convention: `Release vX.Y.Z`.\n- Always create as **`--prerelease`**: prerelease here is just a \"build in progress\" marker.\n  The CI clears it on success (see §5). (The user creating it on the web works too.)\n- Verify: `gh release view vX.Y.Z --repo GreptimeTeam/greptimedb --json name,tagName,isPrerelease,draft,targetCommitish`.\n\n## 4. Open the docs release-note PR (do not wait for CI)\n\nRight after triggering the release, open the docs draft PR (see the docs section of the\n`greptimedb-release-note` skill). It only needs the finalized changelog.\n\nThen **delete the local `CHANGELOG-vX.Y.Z.md`** (its content now lives in the release body\nand the docs blog post).\n\n## 5. After the CI build finishes (~hours)\n\nCI's `publish-github-release` action, for a tag matching `^vX.Y.Z$`, runs\n`ncipollo/release-action` with `allowUpdates: true`, **`prerelease=false`,\n`makeLatest=true`, `omitBody=true`** (so it keeps our changelog body but finalizes the\nflags). These flags are described from the current `.github/workflows/release.yml` —\n**verify against the workflow** if behavior differs. Verify success:\n```\ngh release view vX.Y.Z --repo GreptimeTeam/greptimedb --json isPrerelease,assets\n```\n**Latest handling is conditional on whether this is the newest version:**\n- **Releasing the latest version** (e.g. v1.1.0 when nothing newer exists) → CI's finalized\n  state (non-prerelease, latest) is correct; **do nothing**.\n- **Releasing a non-latest line** (e.g. a patch `v1.0.3` while `v1.1.0` is already latest) →\n  set it back to non-latest after CI finishes:\n  `gh release edit vX.Y.Z --repo GreptimeTeam/greptimedb --latest=false`.\n\n## 6. Rollback (only on failure, with double confirmation)\n\nShow the user exactly what will be removed first; never delete blindly.\n```\ngh release delete vX.Y.Z --repo GreptimeTeam/greptimedb   # remove the release\ngit push <remote> :refs/tags/vX.Y.Z                        # remove the tag\n```\n\n## Conventions / gotchas\n\n- Remotes: `<remote>` is whatever points at `GreptimeTeam/greptimedb` (see Prerequisites).\n  `gh` defaults can be unreliable in a repo with many remotes — always pass\n  `--repo GreptimeTeam/greptimedb`.\n- Release title is always `Release vX.Y.Z`.\n- When reasoning about \"previous version\", skip nightly / build-suffixed tags\n  (`*-nightly-*`, `vX.Y.Z-rc.N-<sha>-<date>-*`).\n","frontmatter":{"name":"greptimedb-release","description":"Runbook for publishing a new GreptimeDB version (tag + GitHub release + docs release-note PR) on the upstream GreptimeTeam/greptimedb repo. Use when asked to \"release\" / \"publish\" a GreptimeDB version (e.g. v1.1.0, v1.0.3)."},"isInternal":false,"tokens":1622,"sizeBytes":5771}],"systemPromptSnippet":"<yakaai_skills repo=\"GreptimeTeam/greptimedb\">\n<!-- File: AGENTS.md (Tokens: ~2163 | Category: root-instruction) -->\n# AGENTS.md\n\nGuidance for coding agents (Claude Code, Codex, ...) and contributors working in\nthis repository. `CLAUDE.md` is a symlink to this file. If `.local/AGENTS.md` or\n`.local/CLAUDE.md` is present, you **MUST** read it as well — it holds personal\nor machine-local overrides (gitignored, not shared). To record a personal or\nmachine-local override, write it to `.local/AGENTS.md` (create `.local/` and the\nfile if absent), not to this shared file.\n\nGreptimeDB is an open-source, cloud-native observability database for unified\ncollection and analysis of metrics, logs, and traces. It is written in Rust and\nprovides sub-second querying at PB scale with high cost efficiency.\n\n## Core commands\n\n| Task | Command |\n| --- | --- |\n| Build (debug) | `make build` |\n| Build (release) | `make build RELEASE=true` |\n| Run standalone | `cargo run -- standalone start` |\n| Targeted Rust tests | `cargo nextest run -p <package>` (preferred over `cargo test`) |\n| Full local Rust suite | `make test` |\n| SQL tests | `cargo sqlness bare` (single case: `cargo sqlness bare -t <name>`) |\n| Format | `make fmt` (and `make fmt-toml` for TOML) |\n| Lint | `make clippy` (= `cargo clippy --workspace --all-targets --all-features -- -D warnings`) |\n| Type check | `make check` |\n\nToolchain: Rust (stable, pinned by `rust-toolchain.toml`), Protobuf compiler (>= 3.15), C/C++ build essentials.\nInstall the test runner with `cargo install cargo-nextest --locked`.\n\n## Repo map\n\nGreptimeDB is a Cargo workspace rooted at the repository; most crates live under\n`src/` (plus `tests-fuzz`, `tests-integration`, `tests/runner`). Key areas:\n\n- **Frontend / protocol**: `src/frontend/` (request orchestration), `src/servers/`\n  (wire protocols), `src/sql/` (SQL parsing)\n- **Storage engines**: `src/mito2/` (main time-series engine), `src/metric-engine/`\n  (metrics), `src/file-engine/`\n- **Coordination**: `src/meta-srv/` (metadata & cluster control), `src/meta-client/`\n- **Execution / statements**: `src/operator/` (DDL/DML, request conversion, procedures)\n- **Stream / transform**: `src/flow/` (continuous aggregation), `src/pipeline/`\n- **Query / index**: `src/query/`, `src/promql/`, `src/index/`\n- **Shared**: `src/common/`, `src/datatypes/`, `src/store-api/` (engine contract),\n  `src/catalog/`, `src/table/`\n\nBefore editing a path, read every `AGENTS.md` from the repository root down to\nthat path. The root guide always applies; a nested guide adds or overrides rules\nfor its subtree. Changes spanning multiple subtrees must follow every applicable\nguide.\n\n`AGENTS.md` files are maps, not manuals. Keep them short and stable: record\nwhere code lives, important boundaries or change-coupling, high-cost gotchas,\nand the validation entry point. Implementation details belong in code. Do not\ncopy exhaustive test lists or workflow logic; link to the source of truth.\n\nHigh-change areas and specialized test suites carry their own `AGENTS.md` with\na module map, read/write paths, change-coupling points, and gotchas:\n\n- [`src/common/meta/AGENTS.md`](src/common/meta/AGENTS.md)\n- [`src/query/AGENTS.md`](src/query/AGENTS.md)\n- [`src/servers/AGENTS.md`](src/servers/AGENTS.md)\n- [`src/operator/AGENTS.md`](src/operator/AGENTS.md)\n- [`src/mito2/AGENTS.md`](src/mito2/AGENTS.md)\n- [`src/metric-engine/AGENTS.md`](src/metric-engine/AGENTS.md)\n- [`src/flow/AGENTS.md`](src/flow/AGENTS.md)\n- [`src/frontend/AGENTS.md`](src/frontend/AGENTS.md)\n- [`src/meta-srv/AGENTS.md`](src/meta-srv/AGENTS.md)\n- [`tests-integration/AGENTS.md`](tests-integration/AGENTS.md)\n- [`tests/compatibility/AGENTS.md`](tests/compatibility/AGENTS.md)\n- [`tests/perf/AGENTS.md`](tests/perf/AGENTS.md)\n\n## Rust style\n\nFollow [`docs/style-guide.md`](docs/style-guide.md) for Rust style, module\ndesign, naming, and reuse guidance.\n\n## Read before changing code\n\n- [`.agents/architecture-invariants.md`](.agents/architecture-invariants.md) —\n  repo-wide rules that are easy to violate and expensive to get wrong (persisted/\n  wire format compatibility, crate layering, async runtimes, error handling,\n  feature gating, the DataFusion fork).\n- [`.agents/generated-files.md`](.agents/generated-files.md) — tool-generated\n  artifacts that must not be hand-edited (sqlness `.result`, `config/config.md`,\n  Grafana dashboards, proto).\n- [`docs/style-guide.md`](docs/style-guide.md) — code style.\n- [`CONTRIBUTING.md`](CONTRIBUTING.md) — contribution flow and CLA.\n\n## High-signal entry points\n\n- Main binary: `src/cmd/src/bin/greptime.rs`\n- Configuration: `src/common/config/`, example TOMLs in `config/`\n- Error handling: `src/common/error/` (`ErrorExt`, `StatusCode`)\n- Protocol implementations: `src/servers/src/`\n\n## Worktree safety\n\n- Check `git status --short` before editing. Preserve unrelated tracked changes\n  and untracked files; re-read files changed by another process.\n- Do not rewrite history, force-push, or remove files in bulk unless the user\n  explicitly requests it.\n- Update generated artifacts only through the generators documented in\n  [`.agents/generated-files.md`](.agents/generated-files.md).\n\n## Validation by change type\n\nUse the narrowest command that covers the change, then expand only when its\nblast radius requires it.\n\n| Change | Minimum validation |\n| --- | --- |\n| One Rust crate | `cargo nextest run -p <package>` |\n| Cross-workspace Rust behavior | `make test`; inspect `.github/workflows/rust.yml` when CI parity matters |\n| SQL parsing, planning, execution, or output | `cargo sqlness bare -t <case>`; inspect regenerated `.result` files |\n| Persisted metadata or wire format | Add/run a case under `tests/compatibility/`; follow its `README.md` and `AGENTS.md` |\n| Public configuration | Update example TOMLs, loading/serialization snapshots, and docs; run `make config-docs` |\n| Query regression harness or DSL | Follow `tests/perf/AGENTS.md` |\n| Enterprise-gated code | Build/test with `--features enterprise` where applicable and run `make check-enterprise-license` |\n\nFor import/export, COPY, or snapshot-storage changes, follow the Windows\nportability and validation guidance in\n[`tests-integration/AGENTS.md`](tests-integration/AGENTS.md).\n\n## Before opening a PR\n\n1. If you added a `.rs`, `.py`, or `.ts` file, apply and verify its license\n   header with `hawkeye format` followed by `hawkeye check`. Use the inception\n   year from `licenserc.toml`, not the current year.\n2. `make fmt`\n3. `make clippy`\n4. `make test`\n5. `make check-udeps` (run `make fix-udeps` if it reports unused dependencies).\n6. If you added or changed a public configuration option, update the applicable\n   example TOMLs, configuration-loading and serialized-config snapshot tests,\n   and related user-facing documentation. Run `make config-docs` (needs Docker)\n   and commit the regenerated `config/config.md`.\n7. If you changed a persisted or wire format, add a compatibility test case (see\n   `.agents/architecture-invariants.md`).\n8. If you added or gated an enterprise-only file, give it the enterprise license\n   header, list it in `licenserc-enterprise.toml` (`includes`) and\n   `licenserc.toml` (`excludes`), and run `make check-enterprise-license`.\n9. Use a conventional-commit title, sign off commits (`git commit -s`), and sign\n   the CLA.\n10. When creating or updating a pull request, follow\n   [`.github/pull_request_template.md`](.github/pull_request_template.md): include\n   the CLA statement, fill the change-intention section with enough detail, and\n   update checklist items accurately. \"This PR requires documentation updates\"\n   refers to the official docs site repository\n   ([`GreptimeTeam/docs`](https://github.com/GreptimeTeam/docs)), not to rustdoc\n   or comments in this repo. Check it only when that site would be wrong or\n   incomplete without a matching change; a change users cannot observe does not\n   need it.\n11. If the change must also land on release branches, add the matching\n   `backport-<target>` labels (e.g. `backport-v1.3` targets `release/v1.3`). The\n   Backport workflow (`.github/workflows/backport.yml`) cherry-picks a merged PR\n   onto each target branch and opens a backport PR automatically; on conflicts\n   it aborts and files an issue for a manual backport.\n\n## More\n\n- Agent skills and resources: [`.agents/`](.agents/) (see [`.agents/README.md`](.agents/README.md))\n- Architecture decisions: [`docs/rfcs/`](docs/rfcs/)\n- How-to guides: [`docs/how-to/`](docs/how-to/)\n\n<!-- File: .agents/skills/greptimedb-development-docker-image/SKILL.md (Tokens: ~3519 | Category: anthropic-skill) -->\n---\nname: greptimedb-development-docker-image\ndescription: Builds a development-only GreptimeDB Docker image from a local debug binary for local-cluster testing, and optionally pushes it to a development registry. Use when the user asks to package, build, tag, publish, or cross-build a non-release GreptimeDB or GreptimeDB Enterprise image for debugging.\ncompatibility: Requires Docker with Buildx for cross-platform builds; Podman is supported for native-platform builds.\nmetadata:\n  protocols: docker buildx podman\n  platforms: linux-amd64 linux-arm64\n---\n\n# GreptimeDB Development Docker Image\n\n## Goal\n\nPackage a locally built GreptimeDB binary into a **development-only** Docker\nimage for debugging and local-cluster testing, optionally push it to a\ndevelopment registry, and retain the non-secret image settings in `.env` for\nthe next build. It is **not** a release-image workflow and must not be used to\npublish a production or release artifact.\n\nThis skill follows the documented development-image build procedure:\n\n- Build `greptime` with Cargo's `nightly` profile.\n- Copy the target binary into the Docker build context as `./greptime`.\n- Build the supplied `Dockerfile`, which uses Ubuntu 24.04 only as the runtime\n  base image and exposes the `greptime` binary as its entrypoint.\n\n## Inputs\n\nCollect or discover the following before any build or push:\n\n| Input | Discovery and rule |\n| --- | --- |\n| Source repository | Treat the directory in which this skill is invoked as the default workspace. Include it as an editable field in the batch configuration; do not ask a separate confirmation. Do not assume open-source versus Enterprise. |\n| Edition and binary | Default to `greptime`; use the binary name the user requests for Enterprise builds. The copied Docker-context filename must always be `greptime`. |\n| Cargo profile and binary source | Include this in the batch configuration. Default to rebuilding `nightly`; reuse an existing binary only after the user explicitly accepts that its freshness is unverified. |\n| Target platform | Preselect `linux/amd64` unless Docker's server is `linux/arm64`, then preselect `linux/arm64`. Let the user override it in the batch configuration. Ubuntu 24.04 is the runtime base image, not a target-platform choice. Each image has exactly one target platform, but it may differ from the host platform. |\n| Build mode | Ask whether the user wants a locally loadable debug image or a registry push. |\n| Registry/repository | Read `IMAGE_REGISTRY` and `IMAGE_REPOSITORY` only from the selected workspace's `.env` and prefill them in the batch configuration. Example: `registry.example.com/team` + `greptimedb-dev`. |\n| Tag | Read `IMAGE_TAG` only from the selected workspace's `.env` and prefill it in the batch configuration. When the tag exists in the selected registry, preselect an incremented version. |\n\nUse the image reference `${IMAGE_REGISTRY}/${IMAGE_REPOSITORY}:${IMAGE_TAG}`.\nIf the registry is intentionally empty, omit its slash rather than producing a\nleading slash.\n\n## Interactive Workflow\n\nUse the platform's interactive prompt component for every question, selection,\nand confirmation. Do not ask an open-ended text question when a single-choice,\nmulti-select, or confirmation component can represent the decision. If the\nplatform does not provide an interactive component, use the equivalent numbered\nor lettered prompt below and wait for input before continuing.\n\n### Batch configuration\n\nMinimize user round trips: run the collector first against the invocation\ndirectory, then use one interactive form or batched prompt to collect workspace,\nedition/binary, profile/reuse-or-rebuild choice, target platform, build mode,\nregistry/repository, tag, and whether to inspect the configured registry tag.\nUse the invocation directory as the editable workspace default. Prefill\ncollector values and mark recommended defaults. If the user changes workspace,\nrerun the collector for that workspace without asking another configuration\nquestion. Only ask a follow-up if information is missing or invalid, or a safety\ngate is required. Treat unchanged prefilled values as accepted.\n\nKeep separate confirmation components only for elevated privileges: `sudo`,\npackage installation, and privileged QEMU setup. Use one final confirmation for\nall non-privileged selected work.\n\nAlways offer **Cancel** for an action that can write state, build, push, or\nrequire elevated privileges. Display the relevant preview before the user\nconfirms it: source/binary path, `file` architecture result, platform(s), image\nreference, build mode, and non-secret `.env` values.\n\n### Batch configuration fields\n\nUse single-choice controls inside the one batch form for mutually exclusive\nchoices:\n\n| Decision | Required choices |\n| --- | --- |\n| Cargo output | Rebuild `nightly` (recommended), reuse an existing binary only with a freshness-unverified acknowledgement, choose a profile/binary |\n| Target platform | `linux/amd64` (default unless Docker server is arm64), `linux/arm64` (default when Docker server is arm64). The selected image may be cross-built with Buildx. |\n| Build mode | Load locally (recommended), push to development registry |\n| Registry/repository | Use `.env` defaults, enter new values |\n| Tag | Use proposed increment when the configured tag exists, enter a version |\n\nWhen push is selected and the image configuration is complete, automatically\nrun the read-only registry-tag check. It may return `unknown`; do not treat that\nas a missing tag.\n\nUse one final confirmation for all selected non-privileged operations:\n\n- persist changed non-secret image settings to `.env`;\n- run the selected Cargo build or reuse the resolved binary;\n- prepare the Docker context;\n- build, verify, and, when selected, push the image.\n\nThe final confirmation must clearly state: **“This creates a development and\nlocal-cluster test image, not a release or production artifact.”**\n\nFor a real multi-select only, use lettered choices (`[A]`, `[B]`) and accept\n`all`, `none`, or `cancel`; otherwise use single-choice components.\n\n### Preflight context collection\n\nBefore asking the profile, registry, or tag questions, run the bundled,\nread-only collector. It works on macOS and Linux, does not invoke `cargo build`,\nand emits JSON that can be shown or summarized to the user:\n\n```bash\npython3 <skill-dir>/scripts/collect_context.py \\\n  --source <source-checkout> \\\n  --bin greptime \\\n  --profile nightly \\\n  [--target <rust-target>]\n```\n\nUse its report to identify:\n\n- missing `IMAGE_REGISTRY`, `IMAGE_REPOSITORY`, or `IMAGE_TAG` values in the\n  selected workspace's `.env`;\n- Cargo binary targets available in the workspace and whether the requested\n  `--bin` exists;\n- the resolved Cargo target directory, expected profile output path, and whether\n  that binary already exists; and\n- macOS/Linux host architecture plus Docker's server platform when Docker is\n  available.\n\nWhen push is selected and image configuration is complete, rerun the collector\nwith `--check-registry-tag` and all selected image values. It uses the selected\nengine's read-only manifest inspection and returns `exists` or `unknown`;\n`unknown` includes an unavailable registry, an absent tag, or missing\nauthentication and must not be treated as an absent tag.\n\n```bash\npython3 <skill-dir>/scripts/collect_context.py \\\n  --source <source-checkout> \\\n  --bin <binary> \\\n  --profile <profile> \\\n  --engine <docker|podman> \\\n  --registry <registry> \\\n  --repository <repository> \\\n  --tag <tag> \\\n  --check-registry-tag\n```\n\nWhen the status is `exists` and the tag ends in a number, preselect the\ncollector's `candidate_tag` as the next development tag. The user must still\nconfirm it before `.env` is updated or an image is built. Never use automatic\nincrementing as permission to overwrite or push an existing image.\n\nIf Cargo metadata or the requested binary target is unavailable, stop before\nbuilding and ask the user to correct the source path or binary target.\n\nThe profile/binary choice is a field in the batch configuration, not a separate\nquestion. Its default is the existing `nightly` output when present:\n\n```text\nQuestion: Which Cargo build output should package this image?\nOptions:\n- Rebuild nightly (Recommended): run Cargo with `--profile nightly`, then use its output.\n- Reuse nightly output: use the existing output only after showing its path, modification time, size, and matching platform; freshness is unverified.\n- Choose profile or binary: provide a different Cargo profile or an explicit binary path.\n```\n\nDo not infer that a binary under `target/debug`, `target/release`, or another\nprofile is suitable. For an existing binary, show its resolved path and run\n`file` before asking for final build confirmation.\n\n## Environment State\n\nThe image configuration lives only in the selected workspace's `.env`. The\ncollector reads only the following keys and never reads or displays other `.env`\nvalues:\n\n```dotenv\nIMAGE_REGISTRY=registry.example.com/team\nIMAGE_REPOSITORY=greptimedb-dev\nIMAGE_TAG=dev-001\n```\n\n1. Read `.env` if it exists. Ignore blank lines and `#` comments.\n2. Show the discovered registry, repository, and proposed tag to the user.\n3. Include registry/repository and tag in the batch configuration even when\n   values already exist; saved values are defaults, not authorization to reuse\n   them.\n4. Compare the selected configuration with the three current managed values. If\n   all match, skip `.env` confirmation and do not update the file. Otherwise,\n   preview only the changed `IMAGE_*` values, request confirmation, and update\n   the file while preserving unrelated entries and comments.\n5. Never put registry credentials, access tokens, passwords, or `docker login`\n   output in `.env`, build commands, logs, or responses. Ask the user to log in\n   themselves if a push needs authentication.\n\nFor a repeated build, offer an incremented tag before asking. Use\n`scripts/next_image_tag.py --tag <existing-tag>` to calculate it. It increments\nthe final numeric component while preserving zero padding:\n\n```text\nweny-2025-0715-01 -> weny-2025-0715-02\nv0.1.4 -> v0.1.5\ndebug-009 -> debug-010\n```\n\nIf the saved tag has no trailing number, ask the user for the next tag rather\nthan inventing a versioning convention. Do not overwrite an existing image tag\nwithout explicit user confirmation.\n\nOnly if a managed value is missing or differs, persist the values with the\nbundled helper after confirmation instead of hand-editing the file:\n\n```bash\npython3 <skill-dir>/scripts/update_image_env.py \\\n  --env <source-checkout>/.env \\\n  --registry <registry> \\\n  --repository <repository> \\\n  --tag <tag>\n```\n\n## Build Procedure\n\n### 1. Inspect the host and container tooling\n\nRun these checks and report the selected path:\n\n```bash\nuname -s\nuname -m\ndocker version --format '{{.Server.Os}}/{{.Server.Arch}}'\ndocker buildx version\ndocker buildx inspect --bootstrap\n```\n\nFor a native-platform build, Podman can be used if Docker is unavailable. For\nany non-native request, require Docker Buildx. Do not silently fall back to a\nnative build when the requested image platform differs.\n\nIf the requested target is unavailable, stop and ask the user to configure a\nBuildx builder and cross-compilation toolchain externally. Do not automate\nprivileged QEMU or binfmt setup, and never run an unpinned privileged image.\n\n### 2. Build the executable for the image platform\n\nFrom the source checkout, build the requested binary. For open-source amd64:\n\n```bash\n<skill-dir>/scripts/build_binary.sh \\\n  --source <source-checkout> --package cmd --bin greptime --profile nightly\n```\n\nFor open-source arm64:\n\n```bash\n<skill-dir>/scripts/build_binary.sh \\\n  --source <source-checkout> \\\n  --package cmd \\\n  --bin greptime \\\n  --profile nightly \\\n  --target aarch64-unknown-linux-gnu\n```\n\nFor Enterprise, use the user-provided executable target, for example:\n\n```bash\n<skill-dir>/scripts/build_binary.sh \\\n  --source <source-checkout> --bin greptime-ent-cloud --profile nightly\n```\n\nFor a user-selected profile, replace `nightly` in the helper call and resolve\nthe binary under `target/<profile>/<binary>` (or\n`target/<rust-target>/<profile>/<binary>` for cross-compilation). Reuse the\nexisting target file only when the interactive profile question selected reuse;\notherwise invoke the helper to rebuild it.\n\nDo not use a binary compiled for the host architecture in an image intended for\nanother architecture. Determine the binary path from the Cargo target and\nprofile, then copy it into the Docker context as `greptime`:\n\n```bash\ncp <source-target-binary> <build-context>/greptime\n```\n\nVerify it before building the image with `file <build-context>/greptime`; its\nreported architecture must match the requested target platform.\n\nCreate a fresh isolated build context (never use the repository root) and use\nthe bundled preparation script. The helper rejects an existing Dockerfile or\nbinary output to prevent accidental context reuse:\n\n```bash\n<skill-dir>/scripts/prepare_context.sh \\\n  --context \"$(mktemp -d)\" \\\n  --binary <source-target-binary> \\\n  --dockerfile <skill-dir>/assets/Dockerfile \\\n  --platform <platform>\n```\n\n### 3. Build or push the image\n\nRun the command only after the user confirms the final image reference and\nwhether it should be pushed.\n\nUse the bundled build helper rather than spelling out individual Docker or\nPodman commands. It follows the existing scripts' Docker-first/Podman-fallback\nbehavior, uses Buildx for a non-native Docker request, and accepts exactly one\ntarget platform per image:\n\n```bash\n<skill-dir>/scripts/build_image.sh \\\n  --context <build-context> \\\n  --image <image-reference> \\\n  --platform <platform> \\\n  --mode local|push\n```\n\n`--mode local` builds a native image locally or uses `docker buildx --load` for\na non-native image. `--mode push` pushes the selected single-platform image.\nThe helper never pushes in `local` mode.\n\nThis is intentionally a runtime image built from a precompiled binary, not a\nmulti-stage Dockerfile. Ubuntu 24.04 is the runtime base image only; select one\ntarget platform separately as `linux/amd64` or `linux/arm64`. Only introduce a\nmulti-stage Dockerfile if the user asks to compile within Docker or the local\nRust toolchain cannot build the required target. Keep the final Ubuntu 24.04\nruntime stage and existing entrypoint unless the user asks to change runtime\nbehavior.\n\nAt the final confirmation, repeat that the selected reference is a\n**development/local-cluster test image**, not a release artifact. If the user\nrequests a release or production image, stop and direct them to the release\nprocess instead.\n\n### 4. Verify the result\n\nFor a local image, use the selected engine to inspect its architecture and\nstart it with `--help`:\n\n```bash\ndocker image inspect <image-reference> --format '{{.Os}}/{{.Architecture}}'\ndocker run --rm <image-reference> --help\n```\n\nFor Podman, use `podman image inspect <image-reference>` and\n`podman run --rm <image-reference> --help`. For a pushed Podman image, use\n`podman manifest inspect <image-reference>`.\n\nFor a pushed image, inspect its remote manifest:\n\n```bash\ndocker buildx imagetools inspect <image-reference>\n```\n\nReport the final image reference, target platform, source binary path, and\nwhether the image was loaded locally or pushed.\n\n## Safety Rules\n\n- This skill is only for development and local-cluster testing. Never present\n  its image as a production, release, or officially published artifact.\n- Never push by default. Local debug builds should use a local tag and load the\n  image unless the user explicitly requests a push.\n- Do not run `docker login`, privileged QEMU setup, package installation, or\n  overwrite a tag without confirmation.\n- Do not delete existing images, builders, binaries, or `.env` entries as part\n  of this workflow.\n- Stop and explain if Docker's server is unavailable, Buildx cannot support the\n  requested platform, or the copied binary architecture does not match the\n  image platform.\n\n<!-- File: .agents/skills/greptimedb-fuzz-ci-failure-investigation/SKILL.md (Tokens: ~4425 | Category: anthropic-skill) -->\n---\nname: greptimedb-fuzz-ci-failure-investigation\ndescription: Investigate a failed GreptimeDB fuzz CI target link by downloading GitHub Actions job logs plus fuzz artifacts such as kind logs, monitor dumps, and CSV dumps, then correlate the failure with local GreptimeDB source code. Use when the user provides a failed fuzz CI target/job URL or asks to diagnose GreptimeDB fuzz CI failures.\n---\n\n# GreptimeDB Fuzz CI Failure Investigation\n\nInvestigate failed fuzz-test jobs for **`GreptimeTeam/greptimedb`** from a local\ncheckout. The purpose is to download the failed job's CI output and fuzz artifacts,\nthen explain the likely cause by correlating the evidence with GreptimeDB source.\n\nAlways pass `--repo GreptimeTeam/greptimedb` to `gh`; local remotes may point to\nforks. Keep the workflow read-only: do not rerun jobs, cancel workflows, push,\ncomment on PRs, or delete artifacts unless the user explicitly asks.\n\n## Scope\n\nUse this skill for fuzz CI only. In `.github/workflows/integration.yml`, fuzz failures\nare the CI jobs that use `.github/actions/fuzz-test`:\n\n- `fuzztest` — standalone fuzz targets.\n- `unstable-fuzztest` — unstable standalone fuzz target.\n- `distributed-fuzztest` — distributed cluster fuzz targets.\n- `distributed-fuzztest-with-chaos` — distributed fuzz targets with Chaos Mesh.\n\nEach matrix job runs a semantic group of targets on one prepared environment.\nThe group defaults to fail-fast: after the first target failure it captures that\ntarget's diagnostics and marks the remaining targets as skipped. The workflow\nstrategy still uses `fail-fast: false`, so failures do not cancel other groups.\n\nThe reusable action `.github/actions/fuzz-test/action.yaml` delegates each target\nto `.github/scripts/run-fuzz-targets.sh`. CI passes `FUZZ_BIN_DIR`, so the script\nruns the prebuilt target executable:\n\n```bash\n<prebuilt-fuzz-binary> -max_total_time=<seconds> \\\n  -artifact_prefix=<target-dir>/libfuzzer/\n```\n\nWithout `FUZZ_BIN_DIR`, such as during local reproduction, the script falls back\nto `cargo fuzz run <target> --fuzz-dir tests-fuzz -D -s none` with the same\nlibFuzzer arguments.\n\nFailed jobs upload one group artifact. Its stable name identifies the job kind,\nmode, and group, for example:\n\n```text\nfuzz-distributed-remote-wal-database-and-regular-table\n```\n\nThe artifact contains `manifest.json`, `summary.md`, and one directory per target\nunder `targets/<target>/`. A failed distributed target can include `fuzz.log`,\nlibFuzzer reproducers, CSV/SQL traces, Kind logs, monitor dumps, and Kubernetes\nstate. Setup failures use `targets/setup/`.\n\n## Inputs\n\nThe normal user input is a **CI target link**: a GitHub Actions job URL for one\nfailed matrix target. Treat that URL as the primary source of truth. Example:\n\n```text\nhttps://github.com/GreptimeTeam/greptimedb/actions/runs/29000666156/job/86062894741\n```\n\nAlso accept these fallback inputs:\n\n- A workflow run URL, e.g.\n  `https://github.com/GreptimeTeam/greptimedb/actions/runs/29000666156`.\n- A run id plus a fuzz target, mode name, artifact name, PR, branch, or commit SHA.\n\nParse ids from URLs:\n\n```bash\nREPO=GreptimeTeam/greptimedb\nRUN_ID=29000666156\nJOB_ID=86062894741\n```\n\nWhen the input is a job URL, do not ask for the target name first. Use the job id\nto fetch metadata, then derive the group and mode from the job name. A distributed\nfuzz job name usually contains the CI group tuple:\n\n```text\nFuzz Test (Distributed, <mode>, <group>)\nFuzz Test with Chaos (Distributed, <mode>, <group>)\n```\n\nIf only a run URL is provided, list failed jobs and choose the fuzz job matching the\ngroup/mode. Ask only when multiple fuzz jobs are plausible and there is no group,\nmode, artifact, or job-url clue.\n\n## Prerequisites\n\n- `gh` must be installed and authenticated (`gh auth status`). Actions logs and\n  artifacts commonly require authentication even for a public repo.\n- `jq`, `unzip`, and standard shell tools are useful for reducing logs.\n- Use the local GreptimeDB checkout for source analysis. If the run's head SHA is\n  not the current checkout, inspect the code at the run's SHA before making claims.\n- Use a scratch directory outside the source tree, such as `/tmp/greptimedb-fuzz-ci`.\n\n## 1. Capture run and job metadata\n\nCreate a scratch directory:\n\n```bash\nmkdir -p /tmp/greptimedb-fuzz-ci/$RUN_ID\ncd /tmp/greptimedb-fuzz-ci/$RUN_ID\n```\n\nFetch run metadata:\n\n```bash\ngh run view \"$RUN_ID\" --repo \"$REPO\" \\\n  --json databaseId,name,displayTitle,event,headBranch,headSha,status,conclusion,createdAt,updatedAt,url,workflowName \\\n  > run.json\n```\n\nFetch all jobs through the REST API:\n\n```bash\ngh api \"repos/$REPO/actions/runs/$RUN_ID/jobs?per_page=100\" \\\n  --paginate --jq '.jobs[]' \\\n  | jq -s '{jobs: .}' > jobs.json\n```\n\nList failed fuzz-like jobs:\n\n```bash\njq -r '\n  .jobs[]\n  | select(.conclusion != null and .conclusion != \"success\")\n  | select(.name | test(\"Fuzz Test|fuzz\"; \"i\"))\n  | [.id, .name, .status, .conclusion, .html_url] | @tsv\n' jobs.json\n```\n\nFor a known `JOB_ID`, capture job name and step outcomes:\n\n```bash\njq -r --arg id \"$JOB_ID\" '\n  .jobs[] | select((.id | tostring) == $id) |\n  \"job=\\(.name) conclusion=\\(.conclusion) url=\\(.html_url)\",\n  (.steps[]? | [.number, .name, .status, .conclusion, .started_at, .completed_at] | @tsv)\n' jobs.json > job-summary.txt\n```\n\nRecord the fuzz job kind, matrix mode, group, failed step, event type, branch, and\nhead SHA. Do not assume `main` for PR or merge-queue runs. The failed target is\nauthoritative in the downloaded artifact's `manifest.json`; do not guess it from\nthe group name.\n\n## 2. Download CI output for the failed job\n\nDownload the selected job's log; this is the authoritative target CI output for the\n`cargo fuzz run` command:\n\n```bash\ngh run view \"$RUN_ID\" --repo \"$REPO\" --job \"$JOB_ID\" --log > job-$JOB_ID.log\n```\n\nDownload the full workflow log only when setup/build context matters:\n\n```bash\ngh run view \"$RUN_ID\" --repo \"$REPO\" --log > run-$RUN_ID.log\n```\n\nFallback if `gh run view --log` is incomplete. The job logs endpoint returns a\nplain text stream, while the full run logs endpoint returns a zip archive:\n\n```bash\ngh api \"repos/$REPO/actions/jobs/$JOB_ID/logs\" > job-$JOB_ID.log\ngh api \"repos/$REPO/actions/runs/$RUN_ID/logs\" > run-$RUN_ID-logs.zip\nunzip -oq run-$RUN_ID-logs.zip -d run-logs\n```\n\nIn the job log, inspect the `Run Fuzz Test` step first. Capture:\n\n- exact target name;\n- `max_total_time`;\n- panic/assertion/backtrace or libFuzzer crash output;\n- generated crash/reproducer path if printed;\n- timeout or resource symptoms.\n\n## 3. Find and download fuzz artifacts\n\nList artifacts:\n\n```bash\ngh api \"repos/$REPO/actions/runs/$RUN_ID/artifacts?per_page=100\" \\\n  --paginate --jq '.artifacts[]' \\\n  | jq -s '{artifacts: .}' > artifacts.json\njq -r '.artifacts[] | [.id, .name, .expired, .size_in_bytes] | @tsv' artifacts.json\n```\n\nMatch the one group artifact by job kind, mode slug, and group slug. If the display\nname does not make the slug obvious, list the run artifacts and choose the unique\n`fuzz-*` artifact whose suffix matches the group. Do not search for separate Kind,\nmonitor, or CSV artifacts; these are target subdirectories in the group artifact.\n\nDownload exact artifacts when possible:\n\n```bash\nARTIFACT='fuzz-distributed-remote-wal-database-and-regular-table'\ngh run download \"$RUN_ID\" --repo \"$REPO\" --name \"$ARTIFACT\" --dir artifacts/\"$ARTIFACT\"\n```\n\nFallback by artifact id:\n\n```bash\nARTIFACT_ID=$(jq -r --arg name \"$ARTIFACT\" '.artifacts[] | select(.name == $name and (.expired | not)) | .id' artifacts.json | head -n 1)\nif [ -z \"$ARTIFACT_ID\" ]; then\n  echo \"Artifact not found or expired: $ARTIFACT\"\n  exit 1\nfi\ngh api \"repos/$REPO/actions/artifacts/$ARTIFACT_ID/zip\" > artifact-$ARTIFACT_ID.zip\nmkdir -p artifacts/\"$ARTIFACT\"\nunzip -oq artifact-$ARTIFACT_ID.zip -d artifacts/\"$ARTIFACT\"\n```\n\nAfter download, read the manifest before the bulk logs:\n\n```bash\njq -r '.[] | [.target, .status, .exit_code, .after_prior_failure, .artifact_collection] | @tsv' \\\n  artifacts/\"$ARTIFACT\"/manifest.json\n```\n\nSelect the first `failure` entry as the primary target. Entries with\n`skipped_after_failure` did not run; entries with `after_prior_failure=true` ran\nagainst a potentially contaminated shared environment. Note expired or missing\nartifacts explicitly; retention is currently short (`retention-days: 3`).\n\n## 4. Understand artifact contents\n\nEvidence for target `<target>` is rooted at\n`artifacts/$ARTIFACT/targets/<target>/`. `result.json` records timestamps, status,\nexit code, fuzz budget, prior-failure provenance, and artifact collection status.\n`fuzz.log` is the complete libFuzzer output from the prebuilt target executable in\nCI; local fallback runs it through `cargo fuzz run`.\n\nKind logs under `targets/<target>/kind/` come from an immediate failure snapshot\nand usually include Kubernetes state plus container logs. Prioritize:\n\n- GreptimeDB component logs: frontend, datanode, metasrv, flownode, monitor.\n- Kubernetes pod descriptions/events: restarts, OOMKilled, scheduling, image pull,\n  volume, DNS, and network issues.\n- External service logs: etcd, Kafka, Minio, Chaos Mesh, Kafka WAL helper.\n\nMonitor dumps under `targets/<target>/monitor/` are collected by\n`.github/scripts/collect-fuzz-monitor-artifacts.sh`. They may include:\n\n- `state.log`, `sql.log`, `copy.log`, `port-forward.log`;\n- `*.show_create_table.sql` for `_gt_logs` and OpenTelemetry trace tables;\n- parquet dumps for `_gt_logs`, `opentelemetry_traces`,\n  `opentelemetry_traces_operations`, and `opentelemetry_traces_services`.\n\nCSV and SQL traces are under `targets/<target>/csv/`. LibFuzzer crash or timeout\nreproducers are under `targets/<target>/libfuzzer/`. Kubernetes descriptions and\nevents are under `targets/<target>/kubernetes/`. Standalone service logs are under\n`targets/<target>/service/` when available.\n\n## 5. Build a layered diagnosis model\n\nBefore drilling into a specific error string, build a small macro-level model of\nthe failure. This prevents overfitting on the loudest symptom, such as\n`libFuzzer: deadly signal`, `Internal error`, or a cleanup warning.\n\n### 5.1 Create a failure timeline\n\nRecord the key timestamps in order:\n\n- job start and setup steps;\n- external dependencies ready, such as etcd, Kafka, Minio, and Chaos Mesh;\n- GreptimeDB cluster ready;\n- fuzz binary starts;\n- first generated operation or SQL visible in the target log;\n- first suspicious component log line;\n- user-visible fuzz failure or panic;\n- artifact collection and cleanup.\n\nUse the timeline to classify the failure phase: setup, workload execution,\nstorage/WAL/write path, query path, cleanup, or runner infrastructure.\n\n### 5.2 Trace the error propagation chain\n\nFor distributed fuzz failures, identify the chain from outer symptom to inner\ncause candidate. Prefer this shape:\n\n```text\nfuzz panic/assertion\n  -> client-visible SQL/gRPC/HTTP error\n  -> frontend or monitor error\n  -> meta/datanode/flownode component error\n  -> storage/WAL/object-store/Kafka/etcd/k8s dependency error\n```\n\nAt each boundary, decide whether that layer produced the error or merely wrapped\nand forwarded a downstream error. Do not stop at wrapper errors unless there is no\ndeeper evidence.\n\n### 5.3 Investigate by component boundary\n\nFor distributed targets, scan evidence in this order unless the timeline points\nelsewhere:\n\n1. fuzz target and generated operations;\n2. frontend request handling and SQL/error response;\n3. metasrv metadata/procedure/routing;\n4. datanode/mito2/metric-engine region execution;\n5. WAL, Kafka, object store, Minio, etcd, or other dependencies;\n6. Kubernetes pod state, restarts, scheduling, DNS, network, and runner resource\n   signals.\n\nFor each layer, answer two questions: \"is this the first causal error?\" and \"is\nthis layer producing or propagating the failure?\"\n\n### 5.4 Verify intended config against runtime behavior\n\nWhen a failure appears configuration-related, compare all three levels before\nclaiming a config did or did not take effect:\n\n1. the workflow/action/Helm/YAML config at the tested SHA;\n2. the actual pod/container runtime config or startup logs, if available;\n3. the source path that consumes the config, including hard-coded defaults and\n   library retry/backoff settings.\n\nIf a logged value differs from the expected config, explicitly state whether the\nconfig was not applied, was overridden by another layer, or refers to a different\nkind of timeout/retry/deadline.\n\n### 5.5 Keep competing hypotheses until evidence rules them out\n\nBefore final classification, list at least two plausible causes and the evidence\nthat supports or weakens each, for example:\n\n- product bug;\n- fuzz/test invalid assumption or missing wait;\n- config not applied or overridden;\n- dependency/infra flake;\n- timeout, retry, or resource pressure.\n\nThen choose the most likely cause and keep the confidence scoped. It is often\nvalid to have high confidence in the observed error chain but only medium\nconfidence in the ultimate cause category.\n\n### 5.6 State what would disprove the diagnosis\n\nInclude one or two falsifiable checks, such as a same-SHA rerun, a fixed\nreproducer, a cross-target pattern in the same dependency mode, or one missing\nartifact/log that would change the conclusion.\n\n## 6. Reduce evidence before reading source\n\nExtract high-signal lines from job logs and artifacts:\n\n```bash\ngrep -RInE '(^|[^[:alpha:]])(ERROR|Error|WARN|panic|panicked|FAILED|failure|timeout|Timeout|assert|backtrace|SIG[A-Z]+|OOMKilled|CrashLoopBackOff|ImagePullBackOff|libFuzzer|AddressSanitizer|UndefinedBehaviorSanitizer)' \\\n  job-$JOB_ID.log run-$RUN_ID.log job-logs run-logs artifacts 2>/dev/null > signals.txt\n```\n\nThen identify the first causal failure, not the last cleanup error. A typical order:\n\n1. `cargo fuzz run` output in `job-$JOB_ID.log`.\n2. GreptimeDB component logs around the same timestamp.\n3. Kubernetes events/restarts/OOMs.\n4. Monitor/CSV dumps that show the generated SQL or cluster state.\n\nAvoid common mistakes:\n\n- Do not blame cleanup errors after the fuzz failure.\n- Do not treat expected retry warnings as root cause unless they align with the\n  assertion, crash, or timeout.\n- Do not assume an artifact is relevant only because it contains `fuzz`; match mode\n  and target.\n- Do not diagnose from current local `main` if the run used a different SHA.\n\n## 7. Inspect the tested source revision\n\nRead the tested SHA:\n\n```bash\nHEAD_SHA=$(jq -r '.headSha // empty' run.json)\nif [ -z \"$HEAD_SHA\" ]; then\n  echo \"Error: HEAD_SHA is empty or null\"\n  exit 1\nfi\n\nUPSTREAM_REMOTE=$(git remote -v \\\n  | awk '/GreptimeTeam\\/greptimedb(\\.git)?\\/?([[:space:]])/ { print $1; exit }')\nUPSTREAM_REMOTE=${UPSTREAM_REMOTE:-origin}\n\ngit rev-parse --verify \"$HEAD_SHA^{commit}\" >/dev/null 2>&1 \\\n  || git fetch \"$UPSTREAM_REMOTE\" \"$HEAD_SHA\"\n```\n\nIf local `HEAD` differs, inspect files at that SHA or create a temporary worktree:\n\n```bash\ngit worktree add --detach /tmp/greptimedb-fuzz-ci/$RUN_ID/source \"$HEAD_SHA\"\n```\n\nRemove temporary worktrees after investigation with `git worktree remove <path>`.\nNever overwrite the user's current branch.\n\n## 8. Map logs to GreptimeDB code\n\nStart from concrete strings: fuzz target name, panic text, assertion message,\nerror variant, SQL statement, or log message. Search/read the matching source at\nthe run SHA.\n\nUseful entry points:\n\n- `tests-fuzz/` — fuzz targets, operation generators, checkers, dump paths.\n- `.github/actions/fuzz-test/action.yaml` — exact CI fuzz runner configuration.\n- `.github/workflows/integration.yml` — fuzz matrix and artifact upload names.\n- `.github/scripts/collect-fuzz-monitor-artifacts.sh` — monitor dump contents.\n- `src/mito2/AGENTS.md` — storage/WAL/region failures.\n- `src/metric-engine/AGENTS.md` — metric/logical table failures.\n- `src/frontend/AGENTS.md` — SQL/protocol/query orchestration failures.\n- `src/meta-srv/AGENTS.md` — metadata/procedure/routing failures.\n- `src/flow/AGENTS.md` — flow failures.\n\nWhen reporting code evidence, cite the file/function/line and quote the log line\nthat reaches it. Separate confirmed facts from hypotheses.\n\n## 9. Classify the cause\n\nUse this taxonomy:\n\n- **Product bug**: GreptimeDB panics, returns wrong results, loses data, or violates\n  invariants under generated fuzz operations.\n- **Fuzz/test bug**: the fuzz target/generator/checker makes an invalid assumption\n  or fails to wait for an asynchronous condition it requires.\n- **Flake/race**: timing/order/resource issue, usually supported by timeouts,\n  retries, pod restarts, or prior similar failures.\n- **Environment/infra**: runner disk/OOM, Docker/kind, network, dependency install,\n  Kafka/Minio/etcd startup, or GitHub service issue.\n- **Unknown**: logs/artifacts are expired or evidence conflicts.\n\n## 10. Final response format\n\nUse this structure:\n\n```markdown\n## Summary\n- Likely cause: <one sentence>\n- Confidence: high|medium|low\n- Classification: product bug|fuzz/test bug|flake/race|environment/infra|unknown\n- Confidence basis: <which parts are confirmed vs inferred>\n\n## Evidence\n- `<job log or artifact path>`: quoted line(s)\n- `<source-file:line>`: relevant code path\n\n## Reasoning\n<short chain from fuzz target -> CI output -> artifact logs -> source code>\n\n## Alternative hypotheses\n- <hypothesis>: <supporting and weakening evidence>\n- <hypothesis>: <supporting and weakening evidence>\n\n## Next steps\n1. <minimal fix, reproduction command, or verification command>\n2. <rerun command or extra log needed, if any>\n\n## What would disprove this\n- <specific rerun, artifact, log line, or reproducer result that would change the diagnosis>\n```\n\nIf logs or artifacts cannot be downloaded because `gh` is not authenticated or the\nartifacts expired, say so directly and include the exact command/error.\n\n<!-- File: .agents/skills/greptimedb-release-note/agents/openai.yaml (Tokens: ~59 | Category: anthropic-skill) -->\ninterface:\n  display_name: \"GreptimeDB Release Notes\"\n  short_description: \"Generate GreptimeDB release notes\"\n  default_prompt: \"Use $greptimedb-release-note to generate the GreptimeDB changelog, curate highlights, and prepare the docs release-note PR.\"\n\n<!-- File: .agents/skills/greptimedb-release-note/SKILL.md (Tokens: ~5592 | Category: anthropic-skill) -->\n---\nname: greptimedb-release-note\ndescription: Generate a GreptimeDB release changelog (minor, patch, or alpha/beta/rc pre-release) with git cliff (correct range, subtract already-released patch PRs, rebuild contributors, add human-curated highlights and upgrade notes), output to a file, and prepare the docs-repo blog PR. Use when asked to write/generate a GreptimeDB release note or changelog.\n---\n\n# GreptimeDB Release Note / Changelog\n\nGenerate the changelog for a GreptimeDB release. Tooling: **`git cliff`** (config:\n`cliff.toml` in the greptimedb repo) and **`gh`**. Run inside the greptimedb checkout.\n\n**Prerequisites:** `git cliff`, `gh` (check `gh auth status`), and **Python** (the\nsubtract-PRs / rebuild-contributors step in §3–§4 is scripted). This skill writes\n`<remote>` for the git remote pointing at `GreptimeTeam/greptimedb` (often `upstream`,\nsometimes `origin`); resolve it with\n`git remote -v | grep -i 'GreptimeTeam/greptimedb' | awk '{print $1}' | head -1`.\n\n## 1. GitHub token (never print it)\n\n`git cliff` enriches commits with PR titles/authors via the GitHub API; for hundreds of\ncommits you need a token or you hit rate limits. Pass it inline and **never read or echo the\ntoken**:\n```\nGITHUB_TOKEN=$(gh auth token) git cliff ...\n```\nAlternatively the user sources an env file that exports `GITHUB_TOKEN` (don't read it). Without\na token it still runs, but may be rate-limited / incomplete.\n\n## 2. Determine the previous version (skip nightlies)\n\nFrom `gh release list --repo GreptimeTeam/greptimedb`, ignore `*-nightly-*` and build-suffixed\ntags (e.g. `v1.3.0-alpha.1-3c0e2a8d5-20260928-1790639248`).\n- Patch `vX.Y.Z` (Z>0): previous = `vX.Y.(Z-1)`.\n- New minor `vX.Y.0`: previous = the latest `vX.(Y-1).*` (for `v1.0.0`, the biggest `0.x`).\n- Pre-release `vX.Y.Z-{alpha,beta,rc}.N`: previous = the latest pre-release tag of the same\n  `vX.Y.Z` (e.g. `v1.3.0-beta.1` for `v1.3.0-beta.2`). If there is none (the first alpha),\n  see \"First pre-release\" in §3: the previous minor's `.0` may not be published yet, and since\n  v1.2 a `.0` tag sits on `release/vX.Y`, not on `main`.\nDouble-check with the user.\n\n## 3. Pick the range and generate\n\nKey topology fact: **patch tags** (`v1.0.1`, `v1.0.2`) live on `release/vX.Y` and are **NOT**\nancestors of `main` (they are cherry-picks with different SHAs). Up to v1.1 the `.0` tag was\nthe point where `release/vX.Y` left `main`; from v1.2 the branch is cut before the `.0`, and\npre-releases and the `.0` itself are tagged on the branch with backports. Verify with\n`git merge-base --is-ancestor <tag> <remote>/main`.\n\n### New minor (`X.Y.0`)\nThe note compares against the whole previous line: everything new since `release/vX.(Y-1)`\nleft `main`, minus what any published `vX.(Y-1).*` release (pre-releases, `.0`, patches)\nalready shipped. The line's own pre-releases (`vX.Y.0-beta.*`) are not subtracted; the `.0`\nnote covers the whole minor.\n\nBase = the **previous minor tag** (e.g. `v1.1.0`); tip = the tagged `vX.Y.0` commit, resolved as\nin \"Pre-release\" below. Split the range at the point where `release/vX.Y` left `main`:\n```\nbp=$(git merge-base <remote>/release/vX.Y <remote>/main)\n```\n- `<prev-minor-tag>..$bp` is on `main`; generate it with `git cliff`:\n  ```\n  GITHUB_TOKEN=$(gh auth token) git cliff <prev-minor-tag>..$bp --tag vX.Y.0 \\\n    -o /path/CHANGELOG-vX.Y.0.md\n  ```\n  `cliff.toml`'s `ignore_tags` folds in-range nightly tags into the single section.\n- `$bp..<tip>` is the release branch: backports, squashed picks, and commits without a PR.\n  `git cliff` fails on such commits (`Filter call 'sort' failed`), so build this part as a\n  pre-release: collect the original PRs with the loop in \"Pre-release\" and add their bullets.\n  List the commits that carry no PR with\n  `git log --format='%h %s' $bp..<tip> | grep -vE '\\(#[0-9]+\\)$'`; drop mechanical ones, and\n  for a manual port of a `main` PR (the body says `cherry picked from commit` a SHA that is not\n  on `main`), find that PR by title and cite it.\n\n**Then subtract PRs already shipped in published `vX.(Y-1).*` releases**: their `main` commits\nare inside the range. Build the set as for the first pre-release (below): the PR links in\nevery published `vX.(Y-1).*` release body, plus the `(#N)` line endings in the history of the\nnewest published `vX.(Y-1).*` tag. Remove every changelog bullet whose `#NNNN` is in that set (a\nsmall Python script is the reliable way — match `pull/<n>)` on lines starting with `*`).\n\n### Patch (`X.Y.Z`, Z>0, cherry-picks on the release branch)\nThe previous patch tag is an ancestor of the release branch, so a plain range works **when the\ncherry-picks land as individual commits**:\n```\nGITHUB_TOKEN=$(gh auth token) git cliff <prev-patch-tag>..<release-branch-tip> --tag vX.Y.Z -o ...\n```\nNo extra subtraction (the branch only contains the new cherry-picks).\n\n**Common case — squashed pick commit.** Patch branches are often a single squashed commit like\n`chore: pick fixes and bump version to vX.Y.Z (#NNNN)`, so the plain range above yields just\nthat one bullet. First **find the picked PRs**: read the squash commit message\n(`git log -1 --format=%B <release-branch-tip>`) — each `* fix: ... (#NN)` line is a picked PR.\nThen pick one of two ways to build the changelog (a patch is usually only a few PRs — **ask the\nuser which they prefer**):\n\n- **(a) Generate on `main` and filter** — lets `git cliff` produce correctly formatted bullets\n  (titles, authors, categories):\n  1. Locate and order the picked PRs' commits on `main` chronologically:\n     ```\n     git log --oneline <remote>/main --grep='#NN1' --grep='#NN2' --reverse\n     ```\n     The first line is the oldest pick, and the last line is the newest pick.\n  2. Run cliff over a `main` range covering all of them (base = parent of the oldest picked\n     commit, tip = the newest), then keep only the picked `#NN` bullets:\n     ```\n     GITHUB_TOKEN=$(gh auth token) git cliff <oldest-pick>~1..<newest-pick> --tag vX.Y.Z -o /tmp/raw.md\n     ```\n     The raw output includes unrelated PRs in the main range that were **not** picked — drop\n     them, then rebuild contributors (§4).\n- **(b) Hand-write the bullets** — a fine fallback for a small patch (and when the API/network is\n  flaky). For each picked PR fetch its title + author (`gh api repos/GreptimeTeam/greptimedb/pulls/<n>`)\n  and write the `* <title> by [@user] in [#NN](...)` lines plus the contributor list yourself.\n\nIf the branch history is otherwise messy, the same \"identify the picked PRs and keep only those\"\nrule applies.\n\n### First pre-release (`X.Y.0-alpha.1`, cut from main)\nThe base is the commit where the previous line's release branch left `main`. It exists when the\nalpha is cut, even if `vX.(Y-1).0` does not:\n```\nbase=$(git merge-base <remote>/release/vX.(Y-1) <tag>)\n```\nIf there is no `release/vX.(Y-1)`, use the newest published release tag that is an ancestor of\nthe tag (`git merge-base --is-ancestor <candidate> <tag>`). Run `git cliff <base>..<tag>`, then\nsubtract every PR that already shipped in a published `vX.(Y-1).*` release, pre-releases\nincluded. Take the union of the PR links in those release bodies and the `(#N)` line endings in\nthe history of the newest published `vX.(Y-1).*` tag:\n```\ngh release view <published-vX.(Y-1)-tag> --repo GreptimeTeam/greptimedb | grep -oE 'pull/[0-9]+'\ngit log <newest-published-vX.(Y-1)-tag> --format=%B | grep -oE '\\(#[0-9]+\\)$'\n```\nEither source alone misses PRs: a release body can omit a backport that its tag contains, and a\nbackport commit can name only the backport PR.\n\n### Pre-release (`X.Y.Z-{alpha,beta,rc}.N` after an earlier pre-release)\n**The tip is the tagged commit, not the release branch tip.** Resolve it from the remote and\nwrite this SHA in the docs PR body (§8) as what the note was checked against:\n```\ngit ls-remote --tags <remote> 'vX.Y.Z-beta.N*'\n```\nUse the `refs/tags/vX.Y.Z-beta.N` line (its `^{}` line if the tag is annotated), not the\nbuild-suffixed tags it also lists. PRs that land on `release/vX.Y` after the tag\n(`git log --oneline <tag>..<remote>/release/vX.Y`) belong to the next release, even if they are\nalready merged when you write the note.\n\nThe range `<prev-pre-release>..<tag>` usually mixes two kinds of commits: individual backports\n(`... [Backport release/vX.Y] (#NNNN)`) and squashed pick commits (`chore: backport selected\nchanges for ... (#NNNN)`). In both, the original PRs are the `(#NN)` at the end of body lines;\na backport commit can also carry prerequisite PRs. Collect the PR set, falling back to the\nsubject for commits whose body lists none:\n```\nfor c in $(git rev-list <prev-pre-release>..<tag>); do\n  prs=$(git log -1 --format=%b \"$c\" | grep -oE '\\(#[0-9]+\\)$')\n  [ -n \"$prs\" ] || prs=$(git log -1 --format=%s \"$c\" | grep -oE '\\(#[0-9]+\\)$')\n  echo \"$prs\"\ndone | tr -dc '0-9\\n' | sort -un\n```\nList the bullets under the **original** PRs (title and author of the PR merged to `main`), not\nthe backport PRs. Build them with patch (a) or (b) above; for (b), fetch\n`gh api repos/GreptimeTeam/greptimedb/pulls/<n> --jq '{number,title,author:.user.login,merged_at}'`,\ngroup by the `cliff.toml` parsers (first match wins), order by `merged_at`, and put `!` titles\nunder Breaking changes (§6).\n\nA squashed pick PR can also contain release-only commits that have no PR. List them:\n```\ngh api repos/GreptimeTeam/greptimedb/pulls/<squash-pr>/commits --paginate \\\n  --jq '.[].commit.message | split(\"\\n\")[0]' | grep -vE '\\(#[0-9]+\\)$'\n```\nMechanical ones (version bump, toolchain or lint adaptations; example:\n`fix: adapt release OptionMap iteration to Rust 1.98`) get no bullet. A release-only\ncommit that changes behavior gets a bullet in its group.\n\n> Flaky-network note for (a): `git cliff` fetches the repo's full commit history from the GitHub\n> API for author enrichment (paginates back thousands of commits, even for a tiny range). On a\n> flaky connection it can time out mid-pagination; it caches pages, so just **re-run until it\n> completes** — each retry resumes from the cache. If it keeps failing, fall back to (b).\n\n### Groups (all paths)\n`git cliff` drops PRs whose title isn't a conventional commit (`filter_unconventional`). List\nthem and add the user-facing ones by hand:\n```\ngit log --format=%s <range> | grep -E '\\(#[0-9]+\\)$' | grep -vE '^[a-z]+(\\([^)]*\\))?!?: '\n```\nUse only the groups in `cliff.toml`. `git cliff` prints a `### Build` section for `build:` commits\nbecause no parser matches them; move a user-facing `build(deps)` fix to Bug Fixes and the rest to\nMiscellaneous Tasks. Keep each bullet list free of blank lines; a blank line turns it into a\nloose list that renders with different spacing.\n\n## 4. Rebuild contributor sections after subtracting\n\n`cliff` computes `New Contributors` / `All Contributors` over the full range, so after removing\nlines, recompute from the **remaining** commit bullets:\n- **All Contributors** = sorted set of `@user` from remaining `* ... by [@user] ... in [#NN]`\n  lines (drop bots like `dependabot[bot]`).\n- **New Contributors** = drop any entry whose first-contribution PR was removed (their only\n  in-range PR shipped in a patch).\nDo this in the same script that subtracts the PRs.\n\nWhen the bullets are built by hand (§3 (b), pre-releases), `git cliff` computes no New\nContributors, and GitHub's generated notes don't either (they only see the backport PRs). An\nauthor is new if none of their merged PRs shipped before, i.e. none appears in the history of the\nbase tag:\n```\ngit log <base-tag> --format=%B | grep -oE '\\(#[0-9]+\\)$' | tr -dc '0-9\\n' | sort -u > /tmp/shipped.txt\ngh search prs --repo GreptimeTeam/greptimedb --author <user> --merged --limit 1000 \\\n  --json number --jq '.[].number' | sort -u > /tmp/author.txt\ncomm -12 /tmp/author.txt /tmp/shipped.txt   # empty => new contributor\n```\nRun it for every non-bot author in the release, and cite their earliest-merged PR in this\nrelease. \"Earliest merged PR is in range\" is not the same test: a contributor whose first PR\nmerged on `main` but was not picked into any release is still new when a later PR of theirs\nships.\n\n## 5. Verify\n\nCross-check the result against `git log <base>..<tip>`: no subtracted PR remains, and no\ngenuine new PR was dropped. Optionally drop the mechanical `chore: bump version to vX.Y.Z`\nline (ask the user; some prefer to keep it).\n\n## 6. Human-curated sections (insert after the `Release date:` line)\n\n**Style.** Follow the Google developer documentation style guide: sentence-case headings, active voice, present\ntense for behavior, American spelling, serial comma, code font for identifiers, bold only for UI\nnames. Keep it short:\n- Intro: two sentences at most, what the release covers and its main theme.\n- Highlight: one paragraph of up to four sentences, plus an example if it earns one (see below).\n- Upgrade note or breaking change: one bullet, one or two sentences: what changes, who is\n  affected, what to do.\n- Every sentence states a fact the reader acts on or checks. Delete sentences that restate the\n  heading or the previous sentence, transitions, and summaries.\n- No marketing words (`significantly`, `powerful`, `seamless`, `robust`, `enhanced`), no vague\n  verbs (`supports`, `handles`, `improves`; say what happens), no `several`/`many` when the number\n  is known. Keep the source's hedges; don't add new ones.\n\nExample:\n> Bad: This release significantly enhances write reliability by introducing a robust retry\n> mechanism that seamlessly handles transient failures.\n>\n> Good: In distributed clusters, the frontend retries a failed write up to three times with\n> exponential backoff. Set `max_retries = 0` to disable retries.\n\nMirror past release bodies (`gh release view v1.0.0 --repo GreptimeTeam/greptimedb`):\n- **Short intro** (see Style).\n- **`### 👍 Highlights`** — *few, deep* highlights, ranked by user impact. Fixes for silently\n  wrong query results, lost or invisible data, and security issues rank with new features and\n  can be highlights.\n  - Write a fix as its symptom and the affected deployments or usage, not the PR title. Example:\n    \"with no traffic, `errors / requests > 0.05` was true, so the alert fired\".\n  - State whether a feature is on by default. If an example config uses the default values, say\n    so.\n  - Give an example (SQL / TOML config) only where it helps readers act. Don't pick highlights,\n    or the example, by what is easy to demonstrate.\n  - One highlight covers one change or one user-visible problem. Don't merge unrelated PRs into\n    one entry because they touch the same component.\n  - Sources: the PR body's sections on impact, compatibility, and behavior changes, plus the\n    code and config it touches, not the title alone. Verify examples against the related docs\n    PR/page in the docs repo (`GreptimeTeam/docs`, often checked out locally),\n    `docs/reference/sql/*.md`, `config/*.example.toml`, `config/config.md`.\n  Do **not** mention implementation details, tiny features, or unfinished/experimental-but-not-ready\n  features. Always have the user review and edit the highlights; iterate.\n- **Dashboard** subsection — don't just bump the version. Read the dashboard PRs in the bundled\n  release (`gh release view <ver> --repo GreptimeTeam/dashboard`; then the linked PRs) and\n  describe the user-facing change. Use the names the UI shows, not the PR titles; check them in\n  the dashboard's locale strings at the bundled tag:\n  ```\n  gh api 'repos/GreptimeTeam/dashboard/contents/src/locale/en-US/menu.ts?ref=<dashboard-tag>' \\\n    --jq .content | base64 -d\n  ```\n  Screenshots need absolute `https://` URLs: the body is copied to the GitHub release, where\n  relative and site-root (`/img/...`) paths don't resolve.\n- **`### Upgrade notes`** and **`### Breaking changes`** — check every PR in the release for:\n  `!` titles; `Compatibility`, `Behavior change`, `Breaking` paragraphs in the body (often a\n  plain `Compatibility:` line, not a heading); unchecked \"API changes are backward compatible\" /\n  \"Schema or data changes are backward compatible\" boxes; new config options and their defaults;\n  changed defaults; persisted format changes; rolling-upgrade order; new hard limits. These\n  signals set reading order, not a filter: a limit can sit in plain prose with every box\n  checked, so read the body of every user-facing `feat`/`fix` PR. Examples of what is easy to\n  miss: a 512 MiB cap on decompressed request bodies (#9264); a default step for stepless\n  subqueries (#9481).\n  - **Upgrade notes**: steps operators take — upgrade order, index rebuilds, settings to review.\n  - **Breaking changes**: behavior that used to work and now errors or returns different\n    results. Put the `!` PR bullets here too, after these entries (see `v1.3.0-beta.1`).\n- **Benchmark results** (optional) — state the baseline version (often the previous formal\n  release, not the previous beta), data size, cache state, and hardware. Don't attribute results\n  to single PRs. Leave out results the benchmark marks as not for publication, and smoke-level\n  runs.\n\n## 7. Output\n\nWrite to `CHANGELOG-vX.Y.Z.md` (title `# vX.Y.Z`, which `--tag` produces) and **do not commit\nit**. The release runbook deletes it after the release + docs PR are done.\n\n## 8. Docs-repo blog variant + draft PR\n\nThe release note is also published as a blog post in **`GreptimeTeam/docs`**.\n- **Ask the user for their local `GreptimeTeam/docs` checkout path**; if they have none, offer\n  to clone (`git clone git@github.com:GreptimeTeam/docs.git <path>`).\n- File: `blog/release-X-Y-Z.md` (version with dashes; see `blog/release-1-0-0.md`). Pre-releases\n  use dashes throughout: `blog/release-1-3-0-beta-2.md`.\n- Content = docs frontmatter (below) followed by the GitHub release body, **keeping the\n  `# vX.Y.Z` H1 directly under the frontmatter**. The blog frontmatter has no `title:`, so\n  Docusaurus uses that H1 as the page title and sidebar label — omitting it makes the page\n  render as the filename (e.g. `release-1-1-0` instead of `v1.1.0`). Match the shape of\n  `blog/release-1-0-0.md`:\n  ```\n  ---\n  keywords: [release, GreptimeDB, changelog, vX.Y.Z]\n  description: GreptimeDB vX.Y.Z Changelog\n  date: YYYY-MM-DD\n  ---\n  # vX.Y.Z\n\n  Release date: ...\n  ```\n  The docs CI check `check-release-note-sync` requires the post minus its frontmatter to equal\n  the GitHub release body byte for byte, so the H1 goes on the line right after the closing\n  `---`, with no blank line.\n- The docs working tree may have unrelated WIP — **don't disturb it**: use a `git worktree` off\n  `origin/main`:\n  ```\n  git -C <docs> fetch origin main\n  git -C <docs> worktree add -b chore/X.Y.Z-release-note /tmp/docs-release-note origin/main\n  ```\n- **Read and follow the current docs repo PR template**\n  (`.github/pull_request_template.md`) from the checkout used for the release.\n  Fill in every required section; do not rely on hard-coded section names from an older\n  template. The checklist items are the author's to confirm: check the ones you did, and for one\n  that doesn't apply, check it and say why. For pre-releases, state the tagged SHA (§3) under\n  Verification.\n- **Build the site before opening the PR.** A new worktree has no `node_modules`. Install with\n  the pnpm version pinned in `package.json` (`packageManager`), as CI does:\n  ```\n  cd /tmp/docs-release-note\n  pm=$(jq -r .packageManager package.json)   # e.g. pnpm@8.6.0\n  npx -y \"$pm\" install --frozen-lockfile\n  npx -y \"$pm\" build\n  ```\n  If the build fails, fix it or stop; don't mark the PR ready for review.\n- Commit with sign-off, push, open a **draft** PR, then remove the worktree:\n  ```\n  git -C /tmp/docs-release-note add blog/release-X-Y-Z.md\n  git -C /tmp/docs-release-note commit -s -m \"docs: add X.Y.Z release note\"\n  git -C /tmp/docs-release-note push -u origin chore/X.Y.Z-release-note\n  gh pr create --draft --repo GreptimeTeam/docs --base main --head chore/X.Y.Z-release-note \\\n    --title \"docs: add X.Y.Z release note\" --body-file <template-filled body>\n  git -C <docs> worktree remove /tmp/docs-release-note\n  ```\n- Gotcha: `gh pr edit/create` may fail with an org-scope error if the token lacks `read:org`.\n  Edit the body via REST instead: `gh api repos/GreptimeTeam/docs/pulls/<n> -X PATCH -F body=@body.md`.\n\n<!-- File: .agents/skills/greptimedb-release/agents/openai.yaml (Tokens: ~61 | Category: anthropic-skill) -->\ninterface:\n  display_name: \"GreptimeDB Release\"\n  short_description: \"Publish a GreptimeDB release\"\n  default_prompt: \"Use $greptimedb-release to publish the next GreptimeDB release and coordinate the changelog, GitHub release, CI follow-up, and docs PR.\"\n\n<!-- File: .agents/skills/greptimedb-release/SKILL.md (Tokens: ~1622 | Category: anthropic-skill) -->\n---\nname: greptimedb-release\ndescription: Runbook for publishing a new GreptimeDB version (tag + GitHub release + docs release-note PR) on the upstream GreptimeTeam/greptimedb repo. Use when asked to \"release\" / \"publish\" a GreptimeDB version (e.g. v1.1.0, v1.0.3).\n---\n\n# GreptimeDB Release Runbook\n\nPublish a formal GreptimeDB release on **`GreptimeTeam/greptimedb`**. Always operate against\nthat repo (for `gh`, pass `--repo GreptimeTeam/greptimedb`). Changelog generation is a\nseparate, involved task — use the **`greptimedb-release-note`** skill for it.\n\nThis whole flow touches public infrastructure. Confirm with the user before the\noutward-facing step (creating the release, which creates the tag and triggers CI).\n\n## Prerequisites & remote\n\n- Tools: `gh` (check with `gh auth status`) and `git`. Changelog generation additionally\n  needs `git cliff` and Python — see the `greptimedb-release-note` skill.\n- **Resolve the remote first.** This runbook writes `<remote>` for the git remote pointing\n  at `GreptimeTeam/greptimedb`; substitute your actual name (often `upstream`, sometimes\n  `origin` on a direct clone):\n  ```\n  git remote -v | grep -i 'GreptimeTeam/greptimedb' | awk '{print $1}' | head -1\n  ```\n\n## 0. Inputs: version and branch\n\n- Ask the user for the **version** to release (e.g. `1.1.0`, `1.0.3`) and, if needed, the\n  **branch**.\n- **Branch is inferred from version**: `MAJOR.MINOR` → `release/v<MAJOR>.<MINOR>`.\n  - `v1.0.0 / v1.0.1 / v1.0.2` live on `release/v1.0`; `v1.1.0` lives on `release/v1.1`.\n  - Infer it, then double-check with the user.\n- **New minor/major (`X.Y.0`)** is cut from `main`. If `release/vX.Y` does not exist on\n  the remote, offer to create it from the intended `main` commit (with the user's consent):\n  `git push <remote> <commit>:refs/heads/release/vX.Y`.\n- **Patch (`X.Y.Z`, Z>0)** must use the existing `release/vX.Y` branch (it carries\n  cherry-picked commits).\n- Check what exists on the remote: `git ls-remote --heads <remote> 'release/*'`.\n\n## 1. Verify the Cargo version\n\nThe workspace version on the release branch **must** equal the version being released, or\nstop. **Fetch the branch first**, then read `FETCH_HEAD` directly — `git fetch <remote>\nrelease/vX.Y` only *opportunistically* updates the remote-tracking ref\n`<remote>/release/vX.Y` (and only when the remote has a matching configured refspec), so it\nmay be missing or stale (e.g. a custom `<remote>`, or a branch just created in §0). Reading\n`FETCH_HEAD` always reflects the tip just fetched:\n```\ngit fetch <remote> release/vX.Y\ngit show FETCH_HEAD:Cargo.toml | grep -A30 '\\[workspace.package\\]' | grep -m1 version\n```\n(For a fresh minor cut from main, `<remote>/main` and `release/vX.Y` are usually the same\ncommit.)\n\n## 2. Generate and curate the changelog\n\nUse the **`greptimedb-release-note`** skill. It produces `CHANGELOG-vX.Y.Z.md` (uncommitted)\nand the docs-blog variant. **Review the highlights with the user and iterate** before\npublishing.\n\n## 3. Create the GitHub release (creates the tag + triggers CI)\n\n**Do NOT pre-create the tag.** Creating the release creates the tag and fires the\ntag-push CI (`.github/workflows/release.yml`) that builds all binaries (**~hours**).\nConfirm with the user, then:\n```\ngh release create vX.Y.Z \\\n  --repo GreptimeTeam/greptimedb \\\n  --target release/vX.Y \\\n  --title \"Release vX.Y.Z\" \\\n  --notes-file CHANGELOG-vX.Y.Z.md \\\n  --prerelease\n```\n- Title convention: `Release vX.Y.Z`.\n- Always create as **`--prerelease`**: prerelease here is just a \"build in progress\" marker.\n  The CI clears it on success (see §5). (The user creating it on the web works too.)\n- Verify: `gh release view vX.Y.Z --repo GreptimeTeam/greptimedb --json name,tagName,isPrerelease,draft,targetCommitish`.\n\n## 4. Open the docs release-note PR (do not wait for CI)\n\nRight after triggering the release, open the docs draft PR (see the docs section of the\n`greptimedb-release-note` skill). It only needs the finalized changelog.\n\nThen **delete the local `CHANGELOG-vX.Y.Z.md`** (its content now lives in the release body\nand the docs blog post).\n\n## 5. After the CI build finishes (~hours)\n\nCI's `publish-github-release` action, for a tag matching `^vX.Y.Z$`, runs\n`ncipollo/release-action` with `allowUpdates: true`, **`prerelease=false`,\n`makeLatest=true`, `omitBody=true`** (so it keeps our changelog body but finalizes the\nflags). These flags are described from the current `.github/workflows/release.yml` —\n**verify against the workflow** if behavior differs. Verify success:\n```\ngh release view vX.Y.Z --repo GreptimeTeam/greptimedb --json isPrerelease,assets\n```\n**Latest handling is conditional on whether this is the newest version:**\n- **Releasing the latest version** (e.g. v1.1.0 when nothing newer exists) → CI's finalized\n  state (non-prerelease, latest) is correct; **do nothing**.\n- **Releasing a non-latest line** (e.g. a patch `v1.0.3` while `v1.1.0` is already latest) →\n  set it back to non-latest after CI finishes:\n  `gh release edit vX.Y.Z --repo GreptimeTeam/greptimedb --latest=false`.\n\n## 6. Rollback (only on failure, with double confirmation)\n\nShow the user exactly what will be removed first; never delete blindly.\n```\ngh release delete vX.Y.Z --repo GreptimeTeam/greptimedb   # remove the release\ngit push <remote> :refs/tags/vX.Y.Z                        # remove the tag\n```\n\n## Conventions / gotchas\n\n- Remotes: `<remote>` is whatever points at `GreptimeTeam/greptimedb` (see Prerequisites).\n  `gh` defaults can be unreliable in a repo with many remotes — always pass\n  `--repo GreptimeTeam/greptimedb`.\n- Release title is always `Release vX.Y.Z`.\n- When reasoning about \"previous version\", skip nightly / build-suffixed tags\n  (`*-nightly-*`, `vX.Y.Z-rc.N-<sha>-<date>-*`).\n</yakaai_skills>"}