[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6ACB2TYRC6SB5TQ351K2Q7Q":3,"skill-same:01M6ACB2TYRC6SB5TQ351K2Q7Q":73},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":23,"publishedAt":44,"linkOnly":45,"markdownContent":46,"markdownBody":47,"frontMatter":48,"refName":50,"commitSha":51,"skillDirectory":52,"forksCount":53,"repositoryHtmlUrl":54,"repositoryHomepageUrl":55,"ownerHtmlUrl":56,"ownerBlogUrl":57,"ownerDescription":6,"updatedAt":58,"install":59,"resources":72},"01M6ACB2TYRC6SB5TQ351K2Q7Q","prepare-providers-documentation",null,"Replace the manual commit-by-commit classification step in `breeze release-management prepare-provider-documentation` with AI-driven classification. For each provider with pending changes, analyze every PR (batched into one sub-agent per provider, not one per PR), pay special attention to potentially breaking changes by inspecting the actual diff, scope multi-provider PRs to the current provider's slice, ask the release manager when uncertain, and apply version bumps + changelog entries. Use during the regular provider release cycle as an alternative to the interactive breeze prompts.","apache\u002Fairflow","apache","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F47359?v=4","01M6AC83E6K4FDKK7HZ8WNMQKE","The Apache Software Foundation","01M6ACADDFF6RHG1DBAJXFG7E5",47442,"Apache-2.0","https:\u002F\u002Fgithub.com\u002Fapache\u002Fairflow\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fprepare-providers-documentation\u002FSKILL.md",[18],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43],"prepare","documentation","prepare providers documentation","Providers","when","commit","sub-agent","manager","pay","scope","as","attention","cycle","multi-provider","diff","ask","analyze","changelog","release","pr","2026-11-10 10:12:30",false,"---\nname: prepare-providers-documentation\ndescription: >\n  Replace the manual commit-by-commit classification step in\n  `breeze release-management prepare-provider-documentation` with AI-driven\n  classification. For each provider with pending changes, analyze every PR\n  (batched into one sub-agent per provider, not one per PR), pay special\n  attention to potentially breaking\n  changes by inspecting the actual diff, scope multi-provider PRs to the\n  current provider's slice, ask the release manager when uncertain, and\n  apply version bumps + changelog entries. Use during the regular provider\n  release cycle as an alternative to the interactive breeze prompts.\nlicense: Apache-2.0\n---\n\u003C!-- SPDX-License-Identifier: Apache-2.0\n     https:\u002F\u002Fwww.apache.org\u002Flicenses\u002FLICENSE-2.0 -->\n\n# Prepare Providers Documentation (AI-driven)\n\nThis skill replaces the manual commit-by-commit classification step that the\nrelease manager normally performs when running\n`breeze release-management prepare-provider-documentation`. Instead of asking\nthe release manager to type `d`\u002F`b`\u002F`f`\u002F`x`\u002F`m`\u002F`s`\u002F`v` for each commit, the\nskill drives the classification itself — inspecting every PR (with extra care\nfor potentially breaking changes), scoping multi-provider PRs to the slice\nthat touched the current provider, and asking the release manager only when\ngenuinely uncertain.\n\nThe skill keeps the existing breeze tooling as the source of truth for\ntemplate generation. Claude only owns the classification + version bump +\nchangelog entries; everything else (`__init__.py`, `README.rst`,\n`pyproject.toml`, `conf.py`, `get_provider_info.py`, `index.rst`) is still\nregenerated by `breeze release-management prepare-provider-documentation\n--reapply-templates-only`.\n\n> [!IMPORTANT]\n> This is a release-manager workflow. It mutates `provider.yaml` and\n> `changelog.rst` for many providers in one pass. Always run on a clean\n> working tree (or in a dedicated branch) and let the release manager review\n> the diff before committing.\n\n---\n\n## When to Use This Skill\n\nUse during the regular provider release cycle, in place of either of:\n\n```shell\nbreeze release-management prepare-provider-documentation\nbreeze release-management prepare-provider-documentation --incremental-update\n```\n\n…when the release manager wants Claude to classify the changes instead of\ndoing it by hand. The skill covers the same scope: classifying changes,\nbumping versions, generating changelog sections, reapplying templates, and\nfolding new commits into an already-prepared release PR (incremental\nupdate).\n\nTwo entry points:\n\n- **Initial run** — classify everything from scratch for a new release.\n  Follow Phases 1–5 below.\n- **Incremental update** — extend an existing release PR with commits that\n  landed on `main` since the changelog was first generated (typical when\n  rebasing a release PR before merging). Skip ahead to the\n  **Incremental Update** section after Phase 5.\n\nEither entry point runs in one of two **release shapes**. Establish which one\napplies before starting — the incremental flow behaves differently in each:\n\n- **Wave** (the default) — the regular cycle that releases *every* provider\n  with pending changes. The provider list is an **output** of the run, so it\n  may legitimately grow between the initial cut and the merge.\n- **Ad-hoc** — a release deliberately scoped to a fixed provider list (the\n  release manager named a subset, or set `DISTRIBUTIONS_LIST`). The provider\n  list is an **input** and must not grow on its own.\n\nAsk the release manager if it is not obvious, and record the answer —\nIncremental Phases 2 and 3.6 branch on it.\n\nDo **not** use this skill for:\n\n- `--only-min-version-update` runs (these don't need classification — just\n  run breeze directly).\n- Releasing from a non-`main` base branch unless you also pass the right\n  `--base-branch` to the breeze invocations described below.\n- Removing providers (state changes belong in a separate PR).\n\n---\n\n## Inputs You Need Before Starting\n\nAsk the release manager (and confirm by reading the answers back) for:\n\n1. **`RELEASE_DATE`** in `YYYY-MM-DD` (or `YYYY-MM-DD_NN`) format, e.g.\n   `2026-04-26`. This is what breeze stamps into\n   `providers\u002F.last_release_date.txt`.\n2. **Base branch** — defaults to `main`. Only override when releasing from a\n   provider-specific branch (e.g. `provider-cncf-kubernetes\u002Fv4-4`).\n3. **Subset of providers**, if any. By default, classify every provider that\n   has pending changes since its last release tag. If the release manager\n   wants a subset (or has set `DISTRIBUTIONS_LIST`), use that list.\n4. **Include flags**: whether to include `--include-not-ready-providers`\n   and\u002For `--include-removed-providers`.\n\nSet the environment for the session:\n\n```bash\nexport RELEASE_DATE=\u003Cdate>\n# Optional, scopes everything to a subset\nexport DISTRIBUTIONS_LIST=\"\u003Cprovider1> \u003Cprovider2> ...\"\n```\n\nMake sure the `apache-https-for-providers` git remote exists and is up to\ndate — running breeze the first time below will recreate and fetch it.\n\n---\n\n## Workflow\n\nThe skill runs in five phases. Mark tasks with `TaskCreate` for each phase\nand tick them off as you go — the release manager wants to see progress.\n\n### Phase 1 — Discover and pre-classify pending changes (deterministic)\n\nThe source of truth for \"what changed since last release\" is the same git\nquery breeze uses internally: commits between the latest release tag for that\nprovider (`providers-\u003Cid>\u002F\u003Cversion>`) and `apache-https-for-providers\u002F\u003Cbase-branch>`,\nrestricted to the provider's own folders.\n\nRun the **deterministic classifier** — it discovers every provider with pending\nchanges **and** pre-classifies each commit with hard-coded, high-confidence\nrules, flagging only the genuinely ambiguous ones as `needs_llm`. No random\nanswers, nothing to discard:\n\n```bash\nbreeze release-management classify-provider-changes \\\n    --base-branch main \\\n    --output-file \u002Ftmp\u002Fprovider-changes.json\n# scope to a subset by appending provider ids, e.g. ... amazon cncf.kubernetes\n```\n\nThe JSON it writes:\n\n```json\n{\n  \"base_branch\": \"main\",\n  \"providers\": {\n    \"amazon\": {\n      \"current_version\": \"9.29.0\",\n      \"commits\": [\n        {\"hash\": \"c2dbd7a75a\", \"pr\": \"67987\", \"subject\": \"Fix IDC domain S3 path resolution\",\n         \"classification\": \"needs_llm\", \"reason\": \"no high-confidence deterministic rule matched\"},\n        {\"hash\": \"abc123\", \"pr\": \"68087\", \"subject\": \"Bump the edge-ui-package-updates group ...\",\n         \"classification\": \"misc\", \"reason\": \"dependency bump (subject starts with 'Bump')\"}\n      ]\n    }\n  }\n}\n```\n\nHow to read it:\n\n- Providers under `providers` have pending changes (these need attention).\n- `classification ∈ {documentation, skip, misc}` are **decided by rules — take\n  them as-is**, no sub-agent needed (doc-only → `documentation`, test\u002Fexample\n  only → `skip`, `Bump …` dependency bump → `misc`).\n- `classification == needs_llm` → **Phase 3 decides** with a sub-agent. These are\n  the only commits that need LLM analysis.\n- A provider with a `note`\u002F`error` (e.g. a brand-new provider with no prior\n  release tag) → treat as an **initial release** and classify by hand.\n\n> [!NOTE]\n> The classifier is deliberately conservative: `Fix …`\u002F`Add …` subjects are\n> **not** auto-classified (an \"Add …\" can be a breaking change), so they come\n> back as `needs_llm`. The rules live in `classify_change_deterministically`\n> (`dev\u002Fbreeze\u002Fsrc\u002Fairflow_breeze\u002Fprepare_providers\u002Fprovider_documentation.py`).\n\nThe range the classifier reports is not fixed for the whole run — it widens once\na provider actually gets a version bump:\n\n> [!WARNING]\n> **A version bump supersedes the provider's doc-only marker — re-run discovery\n> after Phase 4a.** When `docs\u002F.latest-doc-only-change.txt` is present the\n> classifier starts the range at that commit, so anything older (earlier doc-only\n> changes, tooling churn) is hidden for as long as the provider has no pending\n> release. That is correct while nothing ships — but the moment the provider gets\n> a real version bump the marker no longer applies, and those commits are part of\n> what the release publishes.\n>\n> Re-run `classify-provider-changes` **after** bumping versions and fold in every\n> commit that newly appears; otherwise the release silently drops doc-only\n> entries that were only ever suppressed by the marker. The tell is a provider\n> whose commit count grows between two runs without any new merges. Observed in\n> the 2026-08-01 wave: `cohere` gained #69649, `dbt.cloud`\u002F`presto`\u002F`trino` gained\n> #69478, and `papermill` gained #68322 — all invisible in the first pass.\n\nThen regenerate the auto-generated build files (this does **no** classification,\nso nothing random is produced):\n\n```bash\nbreeze release-management prepare-provider-documentation \\\n    --reapply-templates-only --release-date \"$RELEASE_DATE\"\ngit checkout -- $(git diff --name-only -- '**\u002Fprovider.yaml' '**\u002Fchangelog.rst')\n```\n\nThis leaves the regenerated build files (`__init__.py`, `README.rst`,\n`pyproject.toml`, `conf.py`, `get_provider_info.py`, `index.rst`) in place and\ndiscards only the changelog\u002Fversion files Claude is about to rewrite itself.\n\n### Phase 2 — Per-provider commit list\n\nFor each provider in **Success** from Phase 1, get the same commit list that\nbreeze would have shown. From the repo root:\n\n```bash\nPROVIDER_ID=\u003Cdotted.id>      # e.g. amazon, cncf.kubernetes\nPROVIDER_PATH=$(echo \"$PROVIDER_ID\" | tr '.' '\u002F')   # folder path: cncf\u002Fkubernetes\nPROVIDER_TAG=$(echo \"$PROVIDER_ID\" | tr '.' '-')    # tag segment: cncf-kubernetes\n# Pick the latest *final* release tag. Two gotchas the tag pattern must handle:\n#  * dotted provider ids use HYPHENS in tag names (providers-cncf-kubernetes\u002F\u003Cver>),\n#    even though the source folder uses slashes — build the tag prefix from\n#    PROVIDER_TAG, not PROVIDER_PATH;\n#  * skip the sentinel upper-bound tags (providers-\u003Cid>\u002F99.98.0, \u002F99.99.0) and rc\n#    tags — git's default version sort orders \"1.2.0rc1\" AFTER \"1.2.0\", so a bare\n#    `head -n1` would otherwise select a sentinel or a release candidate.\nLAST_TAG=$(git tag --list \"providers-${PROVIDER_TAG}\u002F*\" --sort=-v:refname \\\n    | grep -vE '\u002F99\\.9[0-9]\\.' | grep -vE 'rc[0-9]+$' | head -n1)\ngit log --pretty=format:'%H %h %cd %s' --date=short \\\n    \"${LAST_TAG}..apache-https-for-providers\u002Fmain\" \\\n    -- \"providers\u002F${PROVIDER_PATH}\u002F\"\n```\n\n> [!WARNING]\n> This git query is a convenience for building the per-provider commit list, but\n> the **authoritative set is what breeze prints in the Phase 1 \"Commit\" tables**\n> for each provider. The tag-based range can still diverge from breeze when a\n> provider's most recent *final* tag is not the last actually-published release\n> (for example, a wave commit bumped the version on `main` but the published\n> baseline is older), which makes breeze include repo-wide commits this query\n> misses. When the two disagree, trust breeze's list and reconcile against it\n> before classifying.\n\nCapture the full hash, short hash, date, subject, and `#NNNN` PR number for\neach commit. Note that some old providers also have legacy paths under\n`airflow\u002Fproviders\u002F\u003Cid>\u002F` — include those when present (consult\n`provider_details.possible_old_provider_paths` semantics by checking the\nprovider's `provider.yaml` history if needed).\n\n### Phase 3 — Classify the PRs (inline, or batched per-provider sub-agents)\n\nFor each commit, classify it into one of:\n\n| Code | Meaning                                        | Version bump   |\n| ---- | ---------------------------------------------- | -------------- |\n| `d`  | Documentation-only                             | none (patch if combined) |\n| `b`  | Bug fix                                        | patch          |\n| `f`  | Feature                                        | minor          |\n| `x`  | Breaking change                                | major          |\n| `m`  | Misc (deps, refactors, internal only)          | patch          |\n| `s`  | Skip (test\u002FCI\u002Fexample only — no user impact)   | none           |\n| `v`  | Min Airflow version bump                       | minor (treated as misc + bump) |\n\n#### Take the deterministic classifications from Phase 1\n\n`classify-provider-changes` (Phase 1) already classified every commit it could\nwith hard-coded rules. Read `\u002Ftmp\u002Fprovider-changes.json` and:\n\n- Use any commit whose `classification` is `documentation`, `skip`, or `misc`\n  **as-is** — these map to `d`, `s`, `m` respectively; no sub-agent needed.\n- Only commits with `classification: needs_llm` go to a sub-agent (below).\n\nThe deterministic rules (doc-only → `d`, test\u002Fexample-only → `s`, `Bump …`\ndependency bump → `m`) are exactly the cheap cases — now computed once by\nbreeze (`classify_change_deterministically`) instead of re-derived here. If you\never need the min-Airflow-bump case (`v`), that one is still a `needs_llm`\njudgement: a sub-agent should flag it when a PR bumps the provider's minimum\nAirflow version.\n\n> [!NOTE]\n> A `needs_llm` commit can still be classified as `skip` by the LLM. The\n> deterministic classifier only catches test\u002Fexample-only changes by looking\n> at which files were modified in the commit. PRs that change build tooling,\n> packaging infrastructure, or breeze templates (files under `dev\u002Fbreeze\u002F`,\n> `scripts\u002F`, etc.) and only regenerate template-generated files in the\n> provider slice (e.g. `README.rst`, `index.rst`, `pyproject.toml`) are NOT\n> caught by the deterministic rules but should still be classified as `skip`\n> — they have zero user-facing impact on the provider package itself.\n\nThat rule cuts one way only. Read the next one before applying it.\n\n> [!WARNING]\n> The converse trap: a PR is **not** `skip` merely because its subject\n> describes tooling. `provider.yaml` is a **source** file, never a regenerated\n> one — anything it declares (`connection-types`, `conn-fields`,\n> `ui-field-behaviour`, `extra-links`, `hook-class-names`, dependencies) ships\n> to users through `get_provider_info.py`, so a change to it is `misc` at\n> minimum. `get_provider_info.py` moving *because `provider.yaml` moved* is\n> therefore not \"regenerated metadata\" in the sense above. Worked example:\n> `Fix conn-fields check crash for nested provider packages (#70224)` fixes a\n> prek check under `scripts\u002Fci\u002Fprek\u002F`, and in the same commit declares a new\n> `Verify SSL` connection-form field for `atlassian.jira` — `misc`, not `skip`.\n> Classify from the provider-scoped diff, never from the subject.\n\n#### Classify the `needs_llm` commits — batched per provider, not one agent per PR\n\nOnly the commits the classifier returned as `needs_llm` still need a sub-agent.\nClassification is the token-heavy part of this skill, so spend sub-agents\nsparingly. Do **not** spawn one sub-agent per PR — that is one agent per\ncommit and balloons to hundreds of agents on a normal release wave. Pick the\nsmallest fan-out that fits the volume:\n\n- **Few `needs_llm` commits remain (≲ 15 across all providers) → classify inline.**\n  Read each PR and its provider-scoped diff yourself, in this context. Spawn no\n  sub-agents at all.\n- **More than that → one sub-agent per provider.** Each agent classifies that\n  provider's *entire* remaining `needs_llm` list in a single pass. This is the\n  natural unit: multi-provider PRs are classified independently per provider\n  anyway (see Cross-Cutting Rules), and one provider-scoped agent amortizes\n  the breaking-change checklist across all of that provider's commits instead\n  of paying a fresh agent spin-up per commit. Only split a provider across\n  more than one agent when its remaining list is large (> ~25 commits) — chunk\n  it then. This keeps the sub-agent count at roughly the number of providers\n  with pending changes, not the number of commits.\n\nUse the `Explore` agent type — they need read-only access. Brief each\nsub-agent with its provider and the whole batch of commits it owns:\n\n```\nClassify a batch of Apache Airflow provider PRs for ONE provider.\n\nProvider:  \u003Cprovider-id>      (path: providers\u002F\u003Cprovider-path>\u002F)\nCommits to classify (\u003CN>) — one row per PR:\n  #\u003CNNNN>  \u003Cfull-hash>  \u003Csubject>\n  #\u003CMMMM>  \u003Cfull-hash>  \u003Csubject>\n  …(this provider's full remaining list)\n\nFor EACH commit above:\n1. Read the PR's title, body, and labels:\n   `gh pr view \u003CNNNN> --json title,body,labels,files`\n2. Read the diff for the slice of the PR that touched\n   providers\u002F\u003Cprovider-path>\u002F only:\n   `gh pr diff \u003CNNNN> -- 'providers\u002F\u003Cprovider-path>\u002F**'`\n   (When the PR touches multiple providers, you only care about the slice\n   for THIS provider — ignore the others when classifying.)\n3. Decide a single classification:\n   - documentation: only docs\u002Fcomments\u002Ftypos in the provider slice\n   - bugfix:        fixes incorrect behavior, no API changes\n   - feature:       adds new capability, parameter, operator, sensor, hook,\n                    or extends an existing one in a backwards-compatible way\n   - breaking:      see \"Breaking-change checklist\" below\n   - misc:          dependency bumps, internal refactors, type-hint\n                    cleanups, no user-visible behavior\n   - skip:          only tests\u002Fexamples\u002FCI for this provider's slice,\n                    OR changes that only touch build tooling, packaging\n                    infrastructure, or template-generated files (e.g.\n                    README.rst, index.rst, pyproject.toml regeneration,\n                    flit\u002Fsdist config, breeze templates) — even when\n                    those files live under the provider path. If the\n                    PR's actual code changes are entirely in dev\u002Fbreeze\u002F\n                    or similar tooling and the provider-scoped diff is\n                    limited to regenerated docs\u002Fmetadata, classify as\n                    skip. NEVER skip a slice that touches\n                    provider.yaml — that is a source file whose\n                    contents ship in get_provider_info.py\n                    (connection-types, conn-fields,\n                    ui-field-behaviour, extra-links, dependencies),\n                    so it is misc at minimum even when the PR's\n                    headline change is tooling.\n   - min_airflow_bump: explicitly bumps the minimum Airflow version pin\n4. Set BREAKING_RISK to \"maybe\" whenever the diff has any signal from the\n   breaking-change checklist below, even if you think the author intended\n   otherwise.\n\nOutput one row per commit and nothing else, in this exact pipe format\n(\u003CN> rows for \u003CN> commits):\n\n   #\u003CNNNN> | \u003Cdocumentation|bugfix|feature|breaking|misc|skip|min_airflow_bump> | \u003Chigh|medium|low> | \u003Cnone|maybe|yes> | \u003Cone-sentence justification>\n\nA change is only breaking if the thing it breaks was **released**. Removing,\nrenaming, or altering a symbol\u002Fbehavior that was *introduced in this same\nunreleased wave* (i.e. the feature was added in one pending commit and\nchanged in another, both after the provider's last release tag) is NOT a\nbreaking change — users never received the old form, so there is nothing to\nbreak. Before classifying any removal\u002Frename as breaking, confirm the affected\nsymbol existed at the last released version:\n`git show providers-\u003Cid>\u002F\u003Clast-version>:providers\u002F\u003Cpath>\u002F... | grep \u003Csymbol>`\n(or grep the last release tag). If it isn't there, treat the change as part of\ndelivering the new feature (feature\u002Fmisc), not breaking. A within-wave rename\nof a brand-new operator or plugin is feature-shaped, not a major bump.\n\nBreaking-change checklist (any of these → BREAKING_RISK >= maybe; usually\nbreaking unless clearly behind a deprecation shim) — **each item assumes the\naffected symbol\u002Fbehavior shipped in a released version, per the rule above**:\n  * Public class\u002Ffunction\u002Fmethod removed or renamed\n    in the **public interface** of the provider — i.e. files under\n    `providers\u002F\u003Cpath>\u002Fsrc\u002F**\u002F{hooks,operators,sensors,triggers,\n    notifications,decorators,executors}\u002F**`, the provider's\n    top-level package `__init__.py`, plus anything imported by\n    `provider.yaml` (`hook-class-names`, `extra-links`, etc.).\n    Internal helpers (e.g. `utils\u002F`, `_internal\u002F`, `pod_manager.py`,\n    or any module not re-exported from the package or referenced\n    in `provider.yaml`) are NOT breaking on their own. NOT in tests\u002F.\n  * Required parameter added to a public constructor or operator __init__\n  * Default value of a public parameter changed\n  * Return type or signature of a public method changed\n  * `extra_dejson` \u002F connection-form fields removed or renamed\n  * Behavior change in `execute()`, `poke()`, `get_conn()` that produces\n    different results for the same inputs\n  * Minimum Python or Airflow version bumped (separate: that's\n    min_airflow_bump unless the bump excludes a previously supported version\n    of a provider's hard dependency, in which case it's also breaking)\n  * Removed deprecation: a previously-deprecated symbol is now deleted\n  * Schema change in stored data (xcom, connection, asset metadata,\n    or the serialized state\u002Fcontext of a `BaseTrigger` subclass —\n    deferred tasks survive provider upgrades only if the trigger's\n    `serialize()` payload stays compatible)\n\nDo NOT trust the PR title alone — read the diff. A PR titled \"Refactor X\"\nthat removes a public method is breaking. A PR titled \"BREAKING: rename\nfoo\" that only renames a private symbol is not. A PR that renames a public\nclass introduced earlier *in this same unreleased wave* is not breaking\neither — the old name was never released (see the released-only rule above).\n```\n\nCollect every sub-agent's rows (and any you classified inline) into one\nclassification table for Phase 3.5.\n\n### Phase 3.5 — Confirm with the release manager\n\nPrint a per-provider summary in this exact format (so the release manager\ncan scan it quickly):\n\n```\nProvider: amazon\nCurrent version: 9.12.0\nMost-impactful change: feature → next version: 9.13.0\n\nCommits (12):\n  abc1234  d   high   docs: fix S3 example                                  #65000\n  def5678  b   high   Fix retry on transient SQS error                      #65010\n  9ab0123  f   high   Add wait_for_completion to AthenaOperator              #65020\n  4cd5678  x   med    Remove deprecated S3Hook.list_objects                  #65030  ⚠ BREAKING\n  7ef9012  m   high   Bump aiobotocore to 2.13                              #65040\n  ...\nUncertain: 2 commits below — please confirm:\n  4cd5678  x   med    Remove deprecated S3Hook.list_objects (#65030)\n    Why: list_objects is documented as deprecated since 8.0.0 but never\n    raised DeprecationWarning, so removal may surprise users.\n  abc4321  ?   low    \"Refactor Athena client\" (#65060)\n    Why: PR description says non-breaking but diff changes the default\n    region resolution from env to provider extras.\n```\n\nAlways escalate to the release manager when:\n\n- `CONFIDENCE: low` from any sub-agent.\n- `BREAKING_RISK: maybe` but the sub-agent classified as anything other than\n  `breaking`.\n- Same PR appears in multiple providers and got different classifications\n  across them — explain why and let the RM call it.\n- Most-impactful change is `breaking` (major bump): always reconfirm\n  explicitly before applying. Major bumps are never silent.\n\nIf the release manager corrects a classification, **save it** in your\nclassification table and re-derive the most-impactful change.\n\n### Phase 4 — Apply classifications\n\nFor each provider, in order:\n\n#### 4a. Bump the version in `provider.yaml`\n\nOpen `providers\u002F\u003Cprovider-path>\u002Fprovider.yaml`, find the `versions:` block,\nand prepend the new version. The bump rule (most-impactful classification\nacross all commits for this provider, computed in Phase 3.5):\n\n| Most-impactful           | Bump          |\n| ------------------------ | ------------- |\n| `breaking`               | major (X+1.0.0) |\n| `feature`                | minor (X.Y+1.0) |\n| `min_airflow_bump`       | minor (X.Y+1.0) |\n| `bugfix`                 | patch (X.Y.Z+1) |\n| `misc`                   | patch (X.Y.Z+1) |\n| `documentation` only     | no bump — handle as doc-only (see below) |\n| `skip` only              | no bump — nothing to do |\n\nAlso update `source-date-epoch:` to the current `int(time.time())`.\n\nFor **doc-only** providers, do not bump the version. Instead, write the\nlatest commit hash from the doc-only batch into\n`providers\u002F\u003Cprovider-path>\u002Fdocs\u002F.latest-doc-only-change.txt` (newline\nterminated). This is what breeze checks on the next release to know the\nprovider hasn't really changed.\n\n#### 4b. Write the changelog entry\n\nOpen `providers\u002F\u003Cprovider-path>\u002Fdocs\u002Fchangelog.rst`. Insert a new section\n*above* the most recent existing version section. The exact format must\nmatch `dev\u002Fbreeze\u002Fsrc\u002Fairflow_breeze\u002Ftemplates\u002FCHANGELOG_TEMPLATE.rst.jinja2`\n— don't paraphrase it. The skeleton:\n\n```rst\n\u003CNEW_VERSION>\n\u003Cdots matching length of NEW_VERSION>\n\n.. note::\n    This release of provider is only available for Airflow X.Y+ as explained in the\n    Apache Airflow providers support policy \u003Chttps:\u002F\u002Fgithub.com\u002Fapache\u002Fairflow\u002Fblob\u002Fmain\u002FPROVIDERS.rst#minimum-supported-version-of-airflow-for-community-managed-providers>_.\n\nBreaking changes\n~~~~~~~~~~~~~~~~\n\n* ``\u003Ccommit subject for breaking change> (#NNNN)``\n\nFeatures\n~~~~~~~~\n\n* ``\u003Ccommit subject for feature> (#NNNN)``\n\nBug Fixes\n~~~~~~~~~\n\n* ``\u003Ccommit subject for bugfix> (#NNNN)``\n\nMisc\n~~~~\n\n* ``\u003Ccommit subject for misc\u002Fmin_airflow_bump> (#NNNN)``\n\nDoc-only\n~~~~~~~~\n\n* ``\u003Ccommit subject for doc> (#NNNN)``\n\n.. Below changes are excluded from the changelog. Move them to\n   appropriate section above if needed. Do not delete the lines(!):\n   * ``\u003Ccommit subject for skip> (#NNNN)``\n```\n\nRules:\n\n- A `.. note::` block at the top of the version section (directly under the\n  `\u003Cdots>` underline, before the first `~~~` header) is used in two distinct\n  situations. Include it whenever **either** applies — combine the wording\n  into a single note, or stack two notes, when both do:\n  - **Airflow min-version bump** — when the bump was driven by a\n    `min_airflow_bump` (or by a `breaking` whose breaking aspect *is* the\n    Airflow min bump), use the support-policy wording shown in the skeleton.\n  - **Breaking change** — for **every** `breaking` classification (major\n    bump, including a `0.x` minor that ships a breaking change), add a note\n    explaining *what* breaks and *how users should adapt* (the migration\n    path). Write it from the PR description and the actual diff, not as a\n    restatement of the commit subject — the reader must learn how to react\n    without opening the PR. This mirrors the standing changelog convention\n    (\"only add notes … when there are some breaking changes and you want to\n    add an explanation to the users on how they are supposed to deal with\n    them\"). The bullet under `Breaking changes` still lists the commit\n    subject as usual; the note is in addition to it, not a replacement.\n- Drop a section entirely if it has no entries (e.g. no `Breaking changes`\n  section if there were none — don't leave an empty header).\n- The `.. Below changes are excluded ...` block at the end is required even\n  if empty. Lines under it use the indented `   * ``...``` form (three-space\n  indent, double backticks).\n- Subjects must be the original commit subject with backticks replaced by\n  single quotes (matches `message_without_backticks`). Don't paraphrase.\n- **Exception — rewrite subjects written in project-internal language.** The\n  don't-paraphrase rule keeps you honest about *what* shipped; it does not\n  oblige you to publish a subject the reader cannot act on. The changelog is\n  read by users, not by us. Rewrite the entry — keeping every `(#NNNN)` — when\n  the subject is only meaningful inside the project:\n  - **Spell out internal abbreviations.** `KPO`, `DFP`, `TI`, `RTIF`, `OL` and\n    friends are our shorthand. Write ``Add '--min-completed-minutes' to\n    'cleanup-pods' to prevent KubernetesPodOperator race condition (#70595)``,\n    not ``… to prevent KPO race condition (#70595)``.\n  - **Name what a grouped dependency bump actually changed.** Dependabot group\n    subjects such as ``Bump the fab-ui-package-updates group across 1 directory\n    with 3 updates (#70604)`` tell the reader nothing. Read the PR diff and list\n    the packages with their versions: ``Bump prettier to 3.9.6, stylelint to\n    17.14.1, webpack to 5.109.0 (#70604)``. A single-package bump still needs\n    its target version: ``Bump eslint to 10.8.0 (#70697)``.\n  - **Describe the user-visible effect, not the internal mechanic.** ``Remove\n    noqa:S101 from production code (#70378)`` names a lint directive; the reader\n    wants ``Mark asserts under 'TYPE_CHECKING' in 'DocumentLoaderOperator'\n    (#70378)``.\n  - **Drop our test vocabulary.** A system test is just an example Dag to the\n    user, so ``Remove hard-coded deferrable crawler run from example_glue system\n    test (#70206)`` becomes ``Remove hard-coded deferrable crawler run from\n    example_glue (#70206)``.\n\n  Rewrite the *wording*, never the *claim* — do not describe behaviour that did\n  not ship. When a subject is too vague to rewrite honestly, read the PR diff\n  before writing the entry.\n- **Exception — collapse within-wave \"add then rename\u002Frework\" chains into one\n  net entry.** When several pending commits are steps toward *one* net change —\n  a feature added in one PR and renamed or reworked in a later PR, both since\n  the last release (the released-only situation from Phase 3) — do **not** list\n  the intermediate steps as separate entries. A reader who never saw the\n  released intermediate form gets no context from ``Add X listener (#a)`` under\n  Features plus ``Rename X to Y (#b)`` under Misc. Write a **single** entry that\n  describes the **net user-facing change** and references **all** related PRs,\n  placed in the section of the most-impactful step. Real example: #68082 added\n  a Kafka listener and #70014 renamed it to the Kafka Event Producer in the same\n  wave → ``Add Kafka Event Producer publishing DagRun and TaskInstance\n  state-change events (#68082, #70014)`` under Features (and *no* separate Misc\n  \"Rename …\" line). This is the changelog counterpart of the unreleased-feature\n  classification rule: classify the rename as non-breaking (Phase 3) **and**\n  describe only what shipped, naming every PR involved.\n- **Capitalize the first letter of every entry**, not only after stripping a\n  Conventional Commit prefix. Contributors sometimes write a lowercase subject\n  (`derive keycloak oauth redirect_uri …`) or a pseudo-scope\n  (`cncf-kubernetes: fix …`); the changelog convention is a leading capital, so\n  render them as ``Derive keycloak oauth redirect_uri …`` \u002F\n  ``Cncf-kubernetes: fix …``.\n- **Strip Conventional Commit prefixes** before writing to the changelog.\n  If the subject starts with a prefix like `feat:`, `fix:`, `chore:`,\n  `docs:`, `refactor:`, `ci:`, `test:`, `perf:`, `build:`, or `style:`\n  (with or without a scope in parentheses, e.g. `fix(amazon):`), remove\n  the prefix and capitalize the first letter of the remaining text.\n  Example: `refactor: Fix _is_http_client_closed ...` →\n  `Fix _is_http_client_closed ...`. Airflow does not use Conventional\n  Commits and these prefixes should not appear in changelogs.\n- **Send no-PR release-tooling commits to the excluded block**, even when the\n  Phase 1 deterministic classifier labeled them `documentation`. Subjects like\n  ``Prepare … providers release\u002Fdocumentation …`` and ``Hide non-user-facing\n  entries from ad-hoc provider release notes`` (often with no `(#NNNN)` suffix\n  because they were committed directly) are release plumbing, not user-facing\n  changes — a `(#NNNN)`-less line in a visible section reads as a mistake. Put\n  them under the `.. Below changes are excluded …` block. breeze's deterministic\n  classification can even be inconsistent for the same commit across providers,\n  so normalize to excluded.\n- Always keep the `(#NNNN)` PR suffix (or, for a collapsed chain, the\n  comma-separated list of all involved PRs).\n- **Order entries within each section by merge order, newest first** — the exact\n  order `git log` printed them in Phase 1. `CHANGELOG_TEMPLATE.rst.jinja2`\n  iterates the changes without sorting, so that order *is* the format. It is\n  **not** descending PR number: a long-lived PR merged late carries a low number\n  and still belongs at the top (real example: `#64274` sits second in amazon\n  9.32.0's `Features`). Don't re-sort by PR number and don't group by theme.\n  The excluded block follows the same order. A collapsed chain — one entry\n  naming several PRs — sits at the position of its **first** commit, which is\n  when the change became relevant, not at the position of the later rework.\n- **Never adopt an entry a contributor pre-wrote at the top of\n  `changelog.rst`** — above the first version header — without checking its PR\n  number against `git log`. Those blocks are written before the PR merges, so\n  the number in them is a guess and is often wrong or nonexistent. Keep the\n  prose, replace the reference with the real merge commit's `(#NNNN)`.\n\n#### 4c. Regenerate templates with breeze\n\nOnce **all** providers have their `provider.yaml` and `changelog.rst`\nupdated, run:\n\n```bash\nbreeze release-management prepare-provider-documentation \\\n    --reapply-templates-only \\\n    --skip-git-fetch \\\n    --release-date \"$RELEASE_DATE\"\n```\n\nThis regenerates `__init__.py`, `README.rst`, `pyproject.toml`, `conf.py`,\n`get_provider_info.py`, and `index.rst` for every provider — picking up the\nnew versions you just wrote. It will not touch `changelog.rst`.\n\n> [!NOTE]\n> `commits.rst` per provider is also stable template content (the actual\n> commit list is rendered at doc-build time via the\n> `airflow-providers-commits` directive). It will be regenerated on the\n> next full release. No action needed here.\n\n#### 4d. Resolve `# use next version` inter-provider pins\n\nContributors can defer an inter-provider dependency bump by pinning it in\n`pyproject.toml` with a trailing `# use next version` comment, instead of\nhard-coding a version that does not exist yet. Now that the versions are\nbumped, resolve those pins:\n\n```bash\nbreeze release-management update-providers-next-version\n```\n\nThis rewrites every `# use next version` dependency to the just-bumped\nversion of the referenced provider and removes the comment. Pins whose\nreferenced provider already has a final release tag are left alone (the\n\"next version\" is then a future release); when only release candidate tags\nexist the pin is updated with a warning, revert it if that provider is in\nvote rather than re-cut in this release.\n\n> [!IMPORTANT]\n> **Run this every time, before opening the PR — even when you believe no\n> provider uses the comment** (the command is a safe no-op when none do).\n> Skipping it ships the wave with stale lower bounds on inter-provider\n> dependencies; once the PR is merged the only remedy is a separate\n> follow-up PR. This is the \"Update versions of dependent providers to the\n> next version\" step in `dev\u002FREADME_RELEASE_PROVIDERS.md` — it lives between\n> doc preparation and PR creation, so it is easy to forget when the skill\n> hands back to the regular release workflow.\n\nThe command only resolves pins an author marked. While classifying, also\nnote every PR that starts importing a symbol from another provider (most often\n`common.compat`) and check that the importing provider's floor is at least\nthe first release that shipped it, even when the importing provider is not in\nthis wave. If it is not, raise the floor in this PR and tell the release\nmanager. The recipe is in \"Update versions of dependent providers to the next\nversion\" in `dev\u002FREADME_RELEASE_PROVIDERS.md`.\n\n**Provider dependency-bump CI guard.** Every `>=` bump this produces (and any\ninter-provider `>=` bump made during the wave, e.g. a `breaking` provider that\ndependents must now require) trips the `check_provider_dependency_bumps`\nselective-check (`dev\u002Fbreeze\u002Fsrc\u002Fairflow_breeze\u002Futils\u002Fselective_checks.py`),\nwhich fails CI with *\"Provider dependency version bumps detected that should\nonly be performed by Release Managers!\"*. That guard exists to stop\n**contributors** from silently changing inter-provider `>=` floors; for a\nrelease wave the bumps are legitimate. The release PR **must carry the\n`allow provider dependency bump` label** to bypass it — every prior \"Prepare\nproviders release …\" PR carries this label. Tell the release manager to add\nthe label to the PR; the bumps are not a mistake to revert. Adding a label\ndoes not re-run CI (the workflow has no `labeled` trigger), so if CI already\nran without it, close and reopen the PR to start a run that sees the label.\n\n### Phase 5 — Validate\n\nRun the same checks the release manager would run:\n\n```bash\n# RST lint + license headers + ruff on Python files\nprek run --from-ref main --hook-stage pre-commit\n\n# Spot-check that provider.yaml versions parse\nbreeze release-management prepare-provider-documentation \\\n    --reapply-templates-only --skip-git-fetch \\\n    --release-date \"$RELEASE_DATE\"   # idempotent — should be a no-op diff\n```\n\nThen `git diff --stat` and walk the release manager through the diff\nprovider-by-provider:\n\n- Confirm the version in `provider.yaml` matches the bump rule.\n- Confirm `changelog.rst` has the right sections populated.\n- **Check for misplaced top-level note blocks.** Each provider's\n  `changelog.rst` has a standing `.. NOTE TO CONTRIBUTORS:` RST comment\n  near the top (above all version sections). If you detect any `.. note::`\n  directive that is NOT nested under a specific version section (i.e. it\n  appears before the first version header, or between the `Changelog`\n  heading and the first version), notify the release manager immediately —\n  it likely means a breaking-change or min-version note was accidentally\n  written at the wrong indentation level or position.\n\n  Two variants to check for, both seen in the 2026-08-01 wave:\n  - **Already misplaced on the base branch.** A contributor adds a note for\n    their own change directly under the `Changelog` header instead of inside\n    the version section it belongs to, so it renders page-wide. Move it into\n    the section that ships the change (`google` #70869).\n  - **Pushed out of place by your own prepend.** When a note was sitting above\n    the first version header and you prepend a new section, the note ends up\n    *between* your new excluded block and the previous version — still wrong,\n    but no longer above the first header, so a scan that only looks at the top\n    of the file misses it (`openai` #69506). Check **inside every section you\n    touched** that no `.. note::` appears after the\n    `.. Below changes are excluded …` marker; a note belongs directly under the\n    version underline, before the first `~~~` header.\n\n  The same applies to a bare `Breaking changes` \u002F `Features` \u002F … heading sitting\n  above the first version header: a contributor pre-wrote it, and it must be\n  folded into the new version section with its PR reference corrected (Phase 4b).\n- Confirm Phase 4d ran: no `# use next version` comment remains where the\n  referenced provider was bumped in this wave.\n- **If any inter-provider `>=` floor changed** (Phase 4d resolved a pin, or a\n  `breaking` provider forced a dependent to require its new major), tell the\n  release manager the PR needs the `allow provider dependency bump` label —\n  otherwise the `check_provider_dependency_bumps` CI check fails with\n  *\"Provider dependency version bumps detected that should only be performed\n  by Release Managers!\"*. `git diff` the changed `pyproject.toml` files for\n  `apache-airflow-providers-*` `>=` changes and list them for the RM.\n- **Reconcile every new section against `git log` — this gate is mandatory.** Run\n\n  ```bash\n  python3 dev\u002Fcheck_changelog_entries.py --fix\n  ```\n\n  It compares each provider's newest section against the commits actually being\n  released and exits non-zero on four defects that eyeballing the diff misses:\n\n  | Code | Meaning | Action |\n  | --- | --- | --- |\n  | `MISSING` | a released commit has no entry | classify and add it (Phase 3 + 4b) |\n  | `UNKNOWN` | an entry cites a PR outside the release range | replace with the real `(#NNNN)` |\n  | `SECTION` | heading is not one of the template's five | move the entry under a template heading |\n  | `ORDER` | entries are off merge order | repaired by `--fix` |\n\n  Re-run it after **any** rebase of the release branch. A rebase silently pulls\n  new provider commits into the release range without touching `changelog.rst`,\n  and that is exactly how a shipped change ends up undocumented. Only `ORDER`\n  is auto-repairable — resolve every other code by hand before handing off.\n- **Scan the new changelog sections for these entry defects** — grep the lines\n  you added: (1) a bullet whose text starts with a lowercase letter → capitalize\n  it (Phase 4b); (2) a bullet in a *visible* section (Features \u002F Bug Fixes \u002F\n  Misc \u002F Doc-only) with no `(#NNNN)` suffix → usually no-PR release-tooling that\n  belongs in the excluded block (Phase 4b); (3) an \"add then rename\" pair for\n  the same feature left as two separate entries → collapse into one net entry\n  naming both PRs (Phase 4b); (4) an internal abbreviation (`KPO`, `DFP`, `TI`,\n  `RTIF`, `OL`) or the phrase \"system test\" → rewrite in user-facing language\n  (Phase 4b); (5) a dependency bump that names no version, or a Dependabot group\n  subject of the form \"… group … with N updates\" → replace with the actual\n  packages and versions from the PR diff (Phase 4b). Reviewers reliably catch\n  all of these, so fix them before handing off.\n- Flag anything where Phase 3.5 had to escalate, so the RM can double-check.\n- **List every provider in this wave that is an initial release** (no prior\n  release tag — the Phase 1 `note`\u002F`error` case) and remind the release\n  manager that each needs an entry in the `apache\u002Fairflow-site` docs index:\n  open that PR before publishing the docs, merge it after.\n  `breeze workflow-run publish-docs --site-env live` fails while a provider is\n  neither in the index nor added by an open PR. Follow\n  [Update airflow-site index for new providers](..\u002F..\u002F..\u002Fdev\u002FREADME_RELEASE_PROVIDERS.md#update-airflow-site-index-for-new-providers).\n\nStop here. Do not commit, do not push — the release manager opens the PR\nthemselves following the regular release workflow in\n`dev\u002FREADME_RELEASE_PROVIDERS.md`. Make sure Phase 4d\n(`update-providers-next-version`) has been run before that PR is opened, and\nthat the PR carries the `allow provider dependency bump` label whenever any\ninter-provider `>=` floor changed (see Phase 4d).\n\n---\n\n## Incremental Update\n\nUse this flow when the release PR has already been opened (changelog and\nversion bumps applied via Phases 1–5) and the release manager rebases it\nto pick up commits that landed on `main` after the original classification.\nThis is the equivalent of `breeze release-management\nprepare-provider-documentation --incremental-update`, but driven by the\nsame AI classification logic as the initial run.\n\nOn a **wave**, \"extend\" is not only about the providers already in the PR: a\nprovider that had nothing pending when the wave was cut can pick up a\nuser-facing change before the PR merges, and the flow below is responsible for\nsurfacing it rather than letting the release ship without it.\n\n> [!IMPORTANT]\n> Run on the **release PR branch** *after* rebasing onto the latest base\n> branch. Do not start the incremental flow on a clean checkout — it needs\n> the prior classifications already written into `changelog.rst` to\n> diff against.\n\n### Incremental Phase 1 — Refresh the apache remote\n\n```bash\nbreeze release-management prepare-provider-documentation \\\n    --reapply-templates-only \\\n    --release-date \"$RELEASE_DATE\"\n```\n\nThis re-fetches `apache-https-for-providers\u002F\u003Cbase-branch>` and regenerates\nthe auto-generated build files for every provider — picking up any\nupstream template changes that landed since the original PR was opened.\nIt does **not** touch `provider.yaml` or `changelog.rst`.\n\n### Incremental Phase 2 — Detect unrecorded commits across **all** providers\n\n> [!IMPORTANT]\n> On a **wave**, sweep **every** provider — not just the ones already in the\n> release PR. A provider that had nothing pending when the wave was cut can\n> acquire its first user-facing change hours later, and iterating only over\n> providers that already have a new version section can never discover it: it\n> has no section to extend. That provider then stays invisible for the rest of\n> the release. This is an observed miss, not a hypothetical one — see the\n> worked example at the end of this phase.\n\nRe-run the deterministic classifier over the full provider set — the same\ncommand as Phase 1 of the initial run, with **no** provider ids appended:\n\n```bash\nbreeze release-management classify-provider-changes \\\n    --base-branch main \\\n    --output-file \u002Ftmp\u002Fprovider-changes.json\n```\n\nThen reduce its output to commits **not yet recorded** in the matching\nprovider's changelog. A commit is unrecorded when its `#NNNN` PR number\nappears nowhere in `providers\u002F\u003Cprovider-path>\u002Fdocs\u002Fchangelog.rst` — the same\npredicate breeze uses internally (see the `_generate_new_changelog` append\nbranch in\n`dev\u002Fbreeze\u002Fsrc\u002Fairflow_breeze\u002Fprepare_providers\u002Fprovider_documentation.py`):\n\n```bash\npython3 - \u003C\u003C'EOF'\nimport json, pathlib\n\ndata = json.loads(pathlib.Path(\"\u002Ftmp\u002Fprovider-changes.json\").read_text())\nfor provider_id, info in sorted(data[\"providers\"].items()):\n    base = pathlib.Path(\"providers\") \u002F provider_id.replace(\".\", \"\u002F\")\n    changelog = base \u002F \"docs\" \u002F \"changelog.rst\"\n    seen = changelog.read_text() if changelog.exists() else \"\"\n    doc_only = base \u002F \"docs\" \u002F \".latest-doc-only-change.txt\"\n    doc_hash = doc_only.read_text().strip() if doc_only.exists() else \"\"\n    for commit in info.get(\"commits\", []):\n        pr = commit.get(\"pr\")\n        # Substring match, NOT \"(#NNNN)\": a collapsed within-wave entry reads\n        # \"(#68082, #70014)\", which an exact-suffix match would report as new.\n        if pr and f\"#{pr}\" in seen:\n            continue\n        if not pr and commit[\"subject\"].replace(\"`\", \"'\") in seen:\n            continue\n        if doc_hash and commit[\"hash\"].startswith(doc_hash[:10]):\n            continue\n        print(f\"{provider_id}\\t{commit['hash'][:10]}\\t#{pr}\\t\"\n              f\"{commit['classification']}\\t{commit['subject']}\")\nEOF\n```\n\nSplit the result into two buckets. A provider is **already in the wave** iff\nits `provider.yaml` changed on this branch:\n\n```bash\ngit diff --name-only \u003Cbase-branch>..HEAD -- '**\u002Fprovider.yaml' \\\n  | sed 's|providers\u002F||; s|\u002Fprovider.yaml||' | tr '\u002F' '.' | sort\n```\n\n- **Bucket A — already in the wave.** Fold the unrecorded commits into the\n  existing version section: continue through Incremental Phases 3 → 3.5 → 4a.\n- **Bucket B — not in the wave.** The provider has unrecorded commits but no\n  new version section, so it is a candidate to *join*. Classify it\n  (Incremental Phase 3), then take it to **Incremental Phase 3.6**.\n- **Bucket C — in the wave but no longer warranting a release.** The sweep\n  above only finds commits *missing* from a wave; this bucket is the opposite\n  direction, and nothing else in the flow looks for it. A provider that review\n  has since concluded is internal-only or documentation-only still carries the\n  version bump and changelog section written by the initial run, so it would\n  still be built and uploaded to PyPI. Find these by re-reading the entries of\n  each in-wave provider's newest changelog section: if every entry is\n  `Doc-only`, or review reclassified the last remaining non-doc entry as\n  documentation, the provider should leave the wave.\n\n  Drop it back with the dedicated command rather than editing the files by\n  hand — it restores `provider.yaml` and `changelog.rst` to their released\n  state and writes the marker in one step:\n\n  ```bash\n  breeze release-management prepare-provider-documentation --mark-doc-only \u003Cprovider-id>\n  ```\n\n  Then commit `providers\u002F\u003Cprovider-path>\u002Fdocs\u002F.latest-doc-only-change.txt`.\n  **Ask the release manager before doing it** — taking a provider out of a wave\n  is their decision, and a `Misc` entry that merely *looks* internal may still\n  be something they want released.\n\nMost Bucket B rows are ordinary noise — repo-wide tooling and test commits\nthat correctly keep a provider out of the release. A Bucket B provider whose\nunrecorded commits are **all** `skip` stays out silently; escalate only when\nat least one classifies as something other than `skip`.\n\nIf a provider has zero unrecorded commits, skip it.\n\n> [!NOTE]\n> **Worked example — the miss this phase exists to prevent.** In the\n> 2026-07-22 wave, `atlassian.jira` had nothing pending when the wave was cut\n> at 22:15. PR #70224 merged at 03:41 the next morning, adding a `Verify SSL`\n> conn-field to `providers\u002Fatlassian\u002Fjira\u002Fprovider.yaml`. The branch was\n> rebased past that commit and an incremental fold-in ran — but because\n> `atlassian.jira` had no version section, the old per-wave-provider loop\n> never looked at it, and the provider was still missing from the release PR\n> a day later. The full sweep above surfaces it; Phase 3.6 asks about it.\n\n### Incremental Phase 3 — Classify the new commits\n\nSame logic as Phase 3 of the initial run — including the auto-classify\nheuristic for docs\u002Ftest-only changes and the batched classification (inline\nwhen few commits remain, otherwise one sub-agent per provider) with the\nbreaking-change checklist. Incremental runs usually have only a handful of new\ncommits, so prefer classifying them inline rather than spawning any sub-agent.\nThe output is a per-provider table mapping each new commit hash to a\nclassification.\n\n### Incremental Phase 3.5 — Decide whether to escalate the version bump\n\nCompute the most-impactful classification across **both** the existing\nclassified commits in the changelog **and** the new ones. If the most\nimpactful is now stronger than what's already in `provider.yaml`, the\nversion needs to be re-bumped. The escalation table:\n\n| Was bumped to | Now most-impactful is | Action                                  |\n| ------------- | --------------------- | --------------------------------------- |\n| patch         | `feature`             | re-bump to next minor (X.Y+1.0)         |\n| patch         | `min_airflow_bump`    | re-bump to next minor (X.Y+1.0)         |\n| patch \u002F minor | `breaking`            | re-bump to next major (X+1.0.0)         |\n| minor         | `feature`             | no change — already minor               |\n| anything      | `bugfix` or `misc`    | no change                               |\n\nA re-bump means: replace the prepended version in `provider.yaml` AND\nupdate the version header in `changelog.rst`'s new section to match.\n\n**Always confirm a re-bump with the release manager** — explicitly state\nthe old version, the new version, and which incoming commit forced the\nescalation. Don't silently re-bump.\n\n### Incremental Phase 3.6 — Ask before adding a provider to the wave\n\nEvery Bucket B provider (Incremental Phase 2) with at least one unrecorded\ncommit classified as something other than `skip` is a provider the release is\ncurrently missing. **Never add one silently, and never drop one silently —\nalways ask.** The release manager owns the scope of the release; your job is\nto make sure the choice is made deliberately rather than by omission.\n\nOn an **ad-hoc** release the answer is usually \"leave it out\" — the provider\nlist is a fixed input — but still surface it, so the release manager knows the\nchange exists and needs a later release.\n\nAsk once per provider. State what landed, why it is user-facing, when it\nlanded relative to the cut, and the version consequence of each option:\n\n> Provider `atlassian.jira` is **not** in this wave, but PR #70224 (\"Fix\n> conn-fields check crash for nested provider packages\", merged 2026-07-23\n> 03:41 — about 5h after the wave was cut) added a `Verify SSL` conn-field and\n> `ui-field-behaviour` to its `provider.yaml`. That is shipped metadata: it\n> changes the Jira connection form. Its other pending commits (#67978, #68991)\n> are test\u002Ftemplate-only.\n> **Add it to the wave** (3.3.4 → 3.3.5, most-impactful `misc`), or **leave it\n> out** and let the change ride the next release?\n\nRecord the answer. If the release manager says **add**, the provider follows\nthe *initial-run* application path rather than the append path — see\nIncremental Phase 4b.\n\n### Incremental Phase 4 — Apply the new entries\n\n#### 4a. Providers already in the wave — append to the existing section\n\nFor each new commit, insert into the existing latest-version section of\n`changelog.rst` under the right header:\n\n| Classification         | Section                |\n| ---------------------- | ---------------------- |\n| `breaking`             | `Breaking changes`     |\n| `feature`              | `Features`             |\n| `bugfix`               | `Bug Fixes`            |\n| `misc`                 | `Misc`                 |\n| `min_airflow_bump`     | `Misc`                 |\n| `documentation`        | `Doc-only`             |\n| `skip`                 | excluded block at end  |\n\nIf the section header doesn't exist yet (e.g. previously there were no\nbreaking changes, but a new commit introduced one), create the header\nabove the next existing section, matching the order in\n`CHANGELOG_TEMPLATE.rst.jinja2`:\n`Breaking changes` → `Features` → `Bug Fixes` → `Misc` → `Doc-only`.\n\nInsert each entry at its **merge-order position** within the section — new\ncommits are newer than everything already there, so they go at the *top*, not\nappended at the bottom. Appending is what drifts incremental providers off the\nformat; `check_changelog_entries.py --fix` repairs it either way, so run it\nafter this phase.\n\nIf you re-bumped the version in Incremental Phase 3.5, also add or remove the\n`.. note::` block about the Airflow min version requirement to match the\nnew bump kind.\n\nIf a new commit is classified `breaking`, add (or extend) a `.. note::` at the\ntop of the version section explaining what breaks and how users should adapt,\nexactly as in the breaking-change note rule in the initial run's Phase 4b.\n\n#### 4b. Providers joining the wave — apply the initial-run path\n\nA provider the release manager confirmed in Incremental Phase 3.6 has no new\nversion section yet, so there is nothing to append to. Run the steps of the\n**initial run's Phase 4** for that provider only:\n\n- **initial-run 4a** — prepend the new version to `provider.yaml` and refresh\n  `source-date-epoch`. Reuse the epoch the rest of the wave already carries\n  (`grep -h source-date-epoch` across the providers this branch bumped) so the\n  release stays on a single value rather than gaining a stray third one.\n- **initial-run 4b** — write a complete new version section in\n  `changelog.rst`: the classified commits under their headers, **every**\n  `skip` commit in the `.. Below changes are excluded …` block, and a\n  `.. note::` if the bump is breaking or a min-Airflow bump.\n- **initial-run 4c** — re-run `--reapply-templates-only` so the joining\n  provider's `__init__.py`, `README.rst`, `pyproject.toml`, `index.rst` pick\n  up the new version. Expect a diff for exactly that provider and no other.\n- **initial-run 4d** — re-run `update-providers-next-version`: a `# use next\n  version` pin pointing at the joining provider must now resolve.\n\n### Incremental Phase 5 — Validate\n\nSame as Phase 5 of the initial run plus an extra check: confirm there are\nno leftover \"Please review …\" markers from a prior interactive\n`breeze release-management prepare-provider-documentation\n--incremental-update` run. If any are present (someone ran the breeze\nincremental flow before invoking this skill), remove them as part of the\nfinal pass. Then walk the diff with the release manager.\n\nAlso confirm that **every Bucket B provider from Incremental Phase 2 was\neither added or explicitly declined** by the release manager — none dropped\nby omission. Re-run the Phase 2 sweep after applying; the only providers it\nshould still report are ones whose unrecorded commits are all `skip`, plus any\nthe release manager deliberately left out.\n\nIf the incremental run bumped a provider to a *new* version (Incremental\nPhase 3.5) or added one to the wave (Incremental Phase 3.6), re-run Phase 4d\n(`update-providers-next-version`) as well — a `# use next version` pin on that\nprovider must resolve to the freshly bumped version before the rebased PR is\npushed.\n\n---\n\n## Cross-Cutting Rules\n\n### PRs covering multiple providers\n\nWhen a single PR touches several providers (e.g.\n`Add Python 3.14 Support (#63520)` touches dozens), classify it\n**independently per provider**. The same PR can be `feature` in one provider\n(a real new capability) and `misc` in another (just a constraint bump in\n`pyproject.toml`). Always scope the diff inspection (whether inline or in a\nper-provider sub-agent) to the current provider's path:\n\n```bash\ngh pr diff \u003CNNNN> -- 'providers\u002F\u003Cprovider-path>\u002F**'\n```\n\nIf the per-provider classifications come back different, do NOT try to\n\"reconcile\" them — that's a feature, not a bug. The release manager wants\neach provider's changelog to reflect what changed in *that* provider.\n\n### Asking the release manager — phrasing\n\nWhen you ask, state your best guess and the alternative explicitly:\n\n> Provider `amazon`, commit `4cd5678` (\"Remove deprecated `S3Hook.list_objects`\"\n> #65030): I classified this as **breaking** because the symbol is removed\n> from the public API in `providers\u002Famazon\u002Fsrc\u002Fairflow\u002Fproviders\u002Famazon\u002Faws\u002Fhooks\u002Fs3.py`,\n> even though the PR description says \"deprecated since 8.0.0\". Confirm\n> **breaking** (major bump 9.x → 10.0.0) or override to **misc** (patch)?\n\nDon't ask vague yes\u002Fno questions (\"is this breaking?\"); always offer the\ntwo alternatives with the version-bump consequence.\n\n### Things you must NOT do silently\n\n- Bump major version without explicit confirmation from the release manager.\n- Add a provider to the wave — or leave one out — without asking\n  (Incremental Phase 3.6). Scope is the release manager's call, and a\n  provider dropped by omission is as much a silent decision as one added.\n- Reclassify a commit the RM already confirmed.\n- Skip commits that don't fit a category — flag them as `?` and ask.\n- Edit `commits.rst`, `index.rst`, `__init__.py`, `README.rst`,\n  `pyproject.toml`, `conf.py`, `get_provider_info.py` directly. Those are\n  template-generated by breeze.\n- Run `git add` or `git commit` — the release manager owns the PR.\n\n### When to give up and fall back to interactive breeze\n\nIf the per-provider commit count is huge (50+) **and** the sub-agents come\nback with `low` confidence on most of them (typically because the diffs\nrequire deep domain knowledge), tell the release manager you're stopping\nthe AI classification and recommend they run the regular interactive\n`breeze release-management prepare-provider-documentation` for that\nspecific provider. Don't try to power through guesswork — the wrong\nclassification at major-bump granularity is worse than a slower manual run.\n\n---\n\n## References\n\n- `dev\u002Fbreeze\u002Fsrc\u002Fairflow_breeze\u002Fprepare_providers\u002Fprovider_documentation.py`\n  — the breeze module this skill replaces (classification + changelog\n  generation). Read this when in doubt about format.\n- `dev\u002Fbreeze\u002Fsrc\u002Fairflow_breeze\u002Ftemplates\u002FCHANGELOG_TEMPLATE.rst.jinja2`\n  — exact format for the changelog section you write in Phase 4b.\n- `dev\u002FREADME_RELEASE_PROVIDERS.md` §\"Convert commits to changelog entries\n  and bump provider versions\" — the human workflow this skill automates.\n- `PROVIDERS.rst` §\"Upgrading minimum supported version of Airflow\" —\n  policy for `min_airflow_bump` classifications.\n","\u003C!-- SPDX-License-Identifier: Apache-2.0\n     https:\u002F\u002Fwww.apache.org\u002Flicenses\u002FLICENSE-2.0 -->\n\n# Prepare Providers Documentation (AI-driven)\n\nThis skill replaces the manual commit-by-commit classification step that the\nrelease manager normally performs when running\n`breeze release-management prepare-provider-documentation`. Instead of asking\nthe release manager to type `d`\u002F`b`\u002F`f`\u002F`x`\u002F`m`\u002F`s`\u002F`v` for each commit, the\nskill drives the classification itself — inspecting every PR (with extra care\nfor potentially breaking changes), scoping multi-provider PRs to the slice\nthat touched the current provider, and asking the release manager only when\ngenuinely uncertain.\n\nThe skill keeps the existing breeze tooling as the source of truth for\ntemplate generation. Claude only owns the classification + version bump +\nchangelog entries; everything else (`__init__.py`, `README.rst`,\n`pyproject.toml`, `conf.py`, `get_provider_info.py`, `index.rst`) is still\nregenerated by `breeze release-management prepare-provider-documentation\n--reapply-templates-only`.\n\n> [!IMPORTANT]\n> This is a release-manager workflow. It mutates `provider.yaml` and\n> `changelog.rst` for many providers in one pass. Always run on a clean\n> working tree (or in a dedicated branch) and let the release manager review\n> the diff before committing.\n\n---\n\n## When to Use This Skill\n\nUse during the regular provider release cycle, in place of either of:\n\n```shell\nbreeze release-management prepare-provider-documentation\nbreeze release-management prepare-provider-documentation --incremental-update\n```\n\n…when the release manager wants Claude to classify the changes instead of\ndoing it by hand. The skill covers the same scope: classifying changes,\nbumping versions, generating changelog sections, reapplying templates, and\nfolding new commits into an already-prepared release PR (incremental\nupdate).\n\nTwo entry points:\n\n- **Initial run** — classify everything from scratch for a new release.\n  Follow Phases 1–5 below.\n- **Incremental update** — extend an existing release PR with commits that\n  landed on `main` since the changelog was first generated (typical when\n  rebasing a release PR before merging). Skip ahead to the\n  **Incremental Update** section after Phase 5.\n\nEither entry point runs in one of two **release shapes**. Establish which one\napplies before starting — the incremental flow behaves differently in each:\n\n- **Wave** (the default) — the regular cycle that releases *every* provider\n  with pending changes. The provider list is an **output** of the run, so it\n  may legitimately grow between the initial cut and the merge.\n- **Ad-hoc** — a release deliberately scoped to a fixed provider list (the\n  release manager named a subset, or set `DISTRIBUTIONS_LIST`). The provider\n  list is an **input** and must not grow on its own.\n\nAsk the release manager if it is not obvious, and record the answer —\nIncremental Phases 2 and 3.6 branch on it.\n\nDo **not** use this skill for:\n\n- `--only-min-version-update` runs (these don't need classification — just\n  run breeze directly).\n- Releasing from a non-`main` base branch unless you also pass the right\n  `--base-branch` to the breeze invocations described below.\n- Removing providers (state changes belong in a separate PR).\n\n---\n\n## Inputs You Need Before Starting\n\nAsk the release manager (and confirm by reading the answers back) for:\n\n1. **`RELEASE_DATE`** in `YYYY-MM-DD` (or `YYYY-MM-DD_NN`) format, e.g.\n   `2026-04-26`. This is what breeze stamps into\n   `providers\u002F.last_release_date.txt`.\n2. **Base branch** — defaults to `main`. Only override when releasing from a\n   provider-specific branch (e.g. `provider-cncf-kubernetes\u002Fv4-4`).\n3. **Subset of providers**, if any. By default, classify every provider that\n   has pending changes since its last release tag. If the release manager\n   wants a subset (or has set `DISTRIBUTIONS_LIST`), use that list.\n4. **Include flags**: whether to include `--include-not-ready-providers`\n   and\u002For `--include-removed-providers`.\n\nSet the environment for the session:\n\n```bash\nexport RELEASE_DATE=\u003Cdate>\n# Optional, scopes everything to a subset\nexport DISTRIBUTIONS_LIST=\"\u003Cprovider1> \u003Cprovider2> ...\"\n```\n\nMake sure the `apache-https-for-providers` git remote exists and is up to\ndate — running breeze the first time below will recreate and fetch it.\n\n---\n\n## Workflow\n\nThe skill runs in five phases. Mark tasks with `TaskCreate` for each phase\nand tick them off as you go — the release manager wants to see progress.\n\n### Phase 1 — Discover and pre-classify pending changes (deterministic)\n\nThe source of truth for \"what changed since last release\" is the same git\nquery breeze uses internally: commits between the latest release tag for that\nprovider (`providers-\u003Cid>\u002F\u003Cversion>`) and `apache-https-for-providers\u002F\u003Cbase-branch>`,\nrestricted to the provider's own folders.\n\nRun the **deterministic classifier** — it discovers every provider with pending\nchanges **and** pre-classifies each commit with hard-coded, high-confidence\nrules, flagging only the genuinely ambiguous ones as `needs_llm`. No random\nanswers, nothing to discard:\n\n```bash\nbreeze release-management classify-provider-changes \\\n    --base-branch main \\\n    --output-file \u002Ftmp\u002Fprovider-changes.json\n# scope to a subset by appending provider ids, e.g. ... amazon cncf.kubernetes\n```\n\nThe JSON it writes:\n\n```json\n{\n  \"base_branch\": \"main\",\n  \"providers\": {\n    \"amazon\": {\n      \"current_version\": \"9.29.0\",\n      \"commits\": [\n        {\"hash\": \"c2dbd7a75a\", \"pr\": \"67987\", \"subject\": \"Fix IDC domain S3 path resolution\",\n         \"classification\": \"needs_llm\", \"reason\": \"no high-confidence deterministic rule matched\"},\n        {\"hash\": \"abc123\", \"pr\": \"68087\", \"subject\": \"Bump the edge-ui-package-updates group ...\",\n         \"classification\": \"misc\", \"reason\": \"dependency bump (subject starts with 'Bump')\"}\n      ]\n    }\n  }\n}\n```\n\nHow to read it:\n\n- Providers under `providers` have pending changes (these need attention).\n- `classification ∈ {documentation, skip, misc}` are **decided by rules — take\n  them as-is**, no sub-agent needed (doc-only → `documentation`, test\u002Fexample\n  only → `skip`, `Bump …` dependency bump → `misc`).\n- `classification == needs_llm` → **Phase 3 decides** with a sub-agent. These are\n  the only commits that need LLM analysis.\n- A provider with a `note`\u002F`error` (e.g. a brand-new provider with no prior\n  release tag) → treat as an **initial release** and classify by hand.\n\n> [!NOTE]\n> The classifier is deliberately conservative: `Fix …`\u002F`Add …` subjects are\n> **not** auto-classified (an \"Add …\" can be a breaking change), so they come\n> back as `needs_llm`. The rules live in `classify_change_deterministically`\n> (`dev\u002Fbreeze\u002Fsrc\u002Fairflow_breeze\u002Fprepare_providers\u002Fprovider_documentation.py`).\n\nThe range the classifier reports is not fixed for the whole run — it widens once\na provider actually gets a version bump:\n\n> [!WARNING]\n> **A version bump supersedes the provider's doc-only marker — re-run discovery\n> after Phase 4a.** When `docs\u002F.latest-doc-only-change.txt` is present the\n> classifier starts the range at that commit, so anything older (earlier doc-only\n> changes, tooling churn) is hidden for as long as the provider has no pending\n> release. That is correct while nothing ships — but the moment the provider gets\n> a real version bump the marker no longer applies, and those commits are part of\n> what the release publishes.\n>\n> Re-run `classify-provider-changes` **after** bumping versions and fold in every\n> commit that newly appears; otherwise the release silently drops doc-only\n> entries that were only ever suppressed by the marker. The tell is a provider\n> whose commit count grows between two runs without any new merges. Observed in\n> the 2026-08-01 wave: `cohere` gained #69649, `dbt.cloud`\u002F`presto`\u002F`trino` gained\n> #69478, and `papermill` gained #68322 — all invisible in the first pass.\n\nThen regenerate the auto-generated build files (this does **no** classification,\nso nothing random is produced):\n\n```bash\nbreeze release-management prepare-provider-documentation \\\n    --reapply-templates-only --release-date \"$RELEASE_DATE\"\ngit checkout -- $(git diff --name-only -- '**\u002Fprovider.yaml' '**\u002Fchangelog.rst')\n```\n\nThis leaves the regenerated build files (`__init__.py`, `README.rst`,\n`pyproject.toml`, `conf.py`, `get_provider_info.py`, `index.rst`) in place and\ndiscards only the changelog\u002Fversion files Claude is about to rewrite itself.\n\n### Phase 2 — Per-provider commit list\n\nFor each provider in **Success** from Phase 1, get the same commit list that\nbreeze would have shown. From the repo root:\n\n```bash\nPROVIDER_ID=\u003Cdotted.id>      # e.g. amazon, cncf.kubernetes\nPROVIDER_PATH=$(echo \"$PROVIDER_ID\" | tr '.' '\u002F')   # folder path: cncf\u002Fkubernetes\nPROVIDER_TAG=$(echo \"$PROVIDER_ID\" | tr '.' '-')    # tag segment: cncf-kubernetes\n# Pick the latest *final* release tag. Two gotchas the tag pattern must handle:\n#  * dotted provider ids use HYPHENS in tag names (providers-cncf-kubernetes\u002F\u003Cver>),\n#    even though the source folder uses slashes — build the tag prefix from\n#    PROVIDER_TAG, not PROVIDER_PATH;\n#  * skip the sentinel upper-bound tags (providers-\u003Cid>\u002F99.98.0, \u002F99.99.0) and rc\n#    tags — git's default version sort orders \"1.2.0rc1\" AFTER \"1.2.0\", so a bare\n#    `head -n1` would otherwise select a sentinel or a release candidate.\nLAST_TAG=$(git tag --list \"providers-${PROVIDER_TAG}\u002F*\" --sort=-v:refname \\\n    | grep -vE '\u002F99\\.9[0-9]\\.' | grep -vE 'rc[0-9]+$' | head -n1)\ngit log --pretty=format:'%H %h %cd %s' --date=short \\\n    \"${LAST_TAG}..apache-https-for-providers\u002Fmain\" \\\n    -- \"providers\u002F${PROVIDER_PATH}\u002F\"\n```\n\n> [!WARNING]\n> This git query is a convenience for building the per-provider commit list, but\n> the **authoritative set is what breeze prints in the Phase 1 \"Commit\" tables**\n> for each provider. The tag-based range can still diverge from breeze when a\n> provider's most recent *final* tag is not the last actually-published release\n> (for example, a wave commit bumped the version on `main` but the published\n> baseline is older), which makes breeze include repo-wide commits this query\n> misses. When the two disagree, trust breeze's list and reconcile against it\n> before classifying.\n\nCapture the full hash, short hash, date, subject, and `#NNNN` PR number for\neach commit. Note that some old providers also have legacy paths under\n`airflow\u002Fproviders\u002F\u003Cid>\u002F` — include those when present (consult\n`provider_details.possible_old_provider_paths` semantics by checking the\nprovider's `provider.yaml` history if needed).\n\n### Phase 3 — Classify the PRs (inline, or batched per-provider sub-agents)\n\nFor each commit, classify it into one of:\n\n| Code | Meaning                                        | Version bump   |\n| ---- | ---------------------------------------------- | -------------- |\n| `d`  | Documentation-only                             | none (patch if combined) |\n| `b`  | Bug fix                                        | patch          |\n| `f`  | Feature                                        | minor          |\n| `x`  | Breaking change                                | major          |\n| `m`  | Misc (deps, refactors, internal only)          | patch          |\n| `s`  | Skip (test\u002FCI\u002Fexample only — no user impact)   | none           |\n| `v`  | Min Airflow version bump                       | minor (treated as misc + bump) |\n\n#### Take the deterministic classifications from Phase 1\n\n`classify-provider-changes` (Phase 1) already classified every commit it could\nwith hard-coded rules. Read `\u002Ftmp\u002Fprovider-changes.json` and:\n\n- Use any commit whose `classification` is `documentation`, `skip`, or `misc`\n  **as-is** — these map to `d`, `s`, `m` respectively; no sub-agent needed.\n- Only commits with `classification: needs_llm` go to a sub-agent (below).\n\nThe deterministic rules (doc-only → `d`, test\u002Fexample-only → `s`, `Bump …`\ndependency bump → `m`) are exactly the cheap cases — now computed once by\nbreeze (`classify_change_deterministically`) instead of re-derived here. If you\never need the min-Airflow-bump case (`v`), that one is still a `needs_llm`\njudgement: a sub-agent should flag it when a PR bumps the provider's minimum\nAirflow version.\n\n> [!NOTE]\n> A `needs_llm` commit can still be classified as `skip` by the LLM. The\n> deterministic classifier only catches test\u002Fexample-only changes by looking\n> at which files were modified in the commit. PRs that change build tooling,\n> packaging infrastructure, or breeze templates (files under `dev\u002Fbreeze\u002F`,\n> `scripts\u002F`, etc.) and only regenerate template-generated files in the\n> provider slice (e.g. `README.rst`, `index.rst`, `pyproject.toml`) are NOT\n> caught by the deterministic rules but should still be classified as `skip`\n> — they have zero user-facing impact on the provider package itself.\n\nThat rule cuts one way only. Read the next one before applying it.\n\n> [!WARNING]\n> The converse trap: a PR is **not** `skip` merely because its subject\n> describes tooling. `provider.yaml` is a **source** file, never a regenerated\n> one — anything it declares (`connection-types`, `conn-fields`,\n> `ui-field-behaviour`, `extra-links`, `hook-class-names`, dependencies) ships\n> to users through `get_provider_info.py`, so a change to it is `misc` at\n> minimum. `get_provider_info.py` moving *because `provider.yaml` moved* is\n> therefore not \"regenerated metadata\" in the sense above. Worked example:\n> `Fix conn-fields check crash for nested provider packages (#70224)` fixes a\n> prek check under `scripts\u002Fci\u002Fprek\u002F`, and in the same commit declares a new\n> `Verify SSL` connection-form field for `atlassian.jira` — `misc`, not `skip`.\n> Classify from the provider-scoped diff, never from the subject.\n\n#### Classify the `needs_llm` commits — batched per provider, not one agent per PR\n\nOnly the commits the classifier returned as `needs_llm` still need a sub-agent.\nClassification is the token-heavy part of this skill, so spend sub-agents\nsparingly. Do **not** spawn one sub-agent per PR — that is one agent per\ncommit and balloons to hundreds of agents on a normal release wave. Pick the\nsmallest fan-out that fits the volume:\n\n- **Few `needs_llm` commits remain (≲ 15 across all providers) → classify inline.**\n  Read each PR and its provider-scoped diff yourself, in this context. Spawn no\n  sub-agents at all.\n- **More than that → one sub-agent per provider.** Each agent classifies that\n  provider's *entire* remaining `needs_llm` list in a single pass. This is the\n  natural unit: multi-provider PRs are classified independently per provider\n  anyway (see Cross-Cutting Rules), and one provider-scoped agent amortizes\n  the breaking-change checklist across all of that provider's commits instead\n  of paying a fresh agent spin-up per commit. Only split a provider across\n  more than one agent when its remaining list is large (> ~25 commits) — chunk\n  it then. This keeps the sub-agent count at roughly the number of providers\n  with pending changes, not the number of commits.\n\nUse the `Explore` agent type — they need read-only access. Brief each\nsub-agent with its provider and the whole batch of commits it owns:\n\n```\nClassify a batch of Apache Airflow provider PRs for ONE provider.\n\nProvider:  \u003Cprovider-id>      (path: providers\u002F\u003Cprovider-path>\u002F)\nCommits to classify (\u003CN>) — one row per PR:\n  #\u003CNNNN>  \u003Cfull-hash>  \u003Csubject>\n  #\u003CMMMM>  \u003Cfull-hash>  \u003Csubject>\n  …(this provider's full remaining list)\n\nFor EACH commit above:\n1. Read the PR's title, body, and labels:\n   `gh pr view \u003CNNNN> --json title,body,labels,files`\n2. Read the diff for the slice of the PR that touched\n   providers\u002F\u003Cprovider-path>\u002F only:\n   `gh pr diff \u003CNNNN> -- 'providers\u002F\u003Cprovider-path>\u002F**'`\n   (When the PR touches multiple providers, you only care about the slice\n   for THIS provider — ignore the others when classifying.)\n3. Decide a single classification:\n   - documentation: only docs\u002Fcomments\u002Ftypos in the provider slice\n   - bugfix:        fixes incorrect behavior, no API changes\n   - feature:       adds new capability, parameter, operator, sensor, hook,\n                    or extends an existing one in a backwards-compatible way\n   - breaking:      see \"Breaking-change checklist\" below\n   - misc:          dependency bumps, internal refactors, type-hint\n                    cleanups, no user-visible behavior\n   - skip:          only tests\u002Fexamples\u002FCI for this provider's slice,\n                    OR changes that only touch build tooling, packaging\n                    infrastructure, or template-generated files (e.g.\n                    README.rst, index.rst, pyproject.toml regeneration,\n                    flit\u002Fsdist config, breeze templates) — even when\n                    those files live under the provider path. If the\n                    PR's actual code changes are entirely in dev\u002Fbreeze\u002F\n                    or similar tooling and the provider-scoped diff is\n                    limited to regenerated docs\u002Fmetadata, classify as\n                    skip. NEVER skip a slice that touches\n                    provider.yaml — that is a source file whose\n                    contents ship in get_provider_info.py\n                    (connection-types, conn-fields,\n                    ui-field-behaviour, extra-links, dependencies),\n                    so it is misc at minimum even when the PR's\n                    headline change is tooling.\n   - min_airflow_bump: explicitly bumps the minimum Airflow version pin\n4. Set BREAKING_RISK to \"maybe\" whenever the diff has any signal from the\n   breaking-change checklist below, even if you think the author intended\n   otherwise.\n\nOutput one row per commit and nothing else, in this exact pipe format\n(\u003CN> rows for \u003CN> commits):\n\n   #\u003CNNNN> | \u003Cdocumentation|bugfix|feature|breaking|misc|skip|min_airflow_bump> | \u003Chigh|medium|low> | \u003Cnone|maybe|yes> | \u003Cone-sentence justification>\n\nA change is only breaking if the thing it breaks was **released**. Removing,\nrenaming, or altering a symbol\u002Fbehavior that was *introduced in this same\nunreleased wave* (i.e. the feature was added in one pending commit and\nchanged in another, both after the provider's last release tag) is NOT a\nbreaking change — users never received the old form, so there is nothing to\nbreak. Before classifying any removal\u002Frename as breaking, confirm the affected\nsymbol existed at the last released version:\n`git show providers-\u003Cid>\u002F\u003Clast-version>:providers\u002F\u003Cpath>\u002F... | grep \u003Csymbol>`\n(or grep the last release tag). If it isn't there, treat the change as part of\ndelivering the new feature (feature\u002Fmisc), not breaking. A within-wave rename\nof a brand-new operator or plugin is feature-shaped, not a major bump.\n\nBreaking-change checklist (any of these → BREAKING_RISK >= maybe; usually\nbreaking unless clearly behind a deprecation shim) — **each item assumes the\naffected symbol\u002Fbehavior shipped in a released version, per the rule above**:\n  * Public class\u002Ffunction\u002Fmethod removed or renamed\n    in the **public interface** of the provider — i.e. files under\n    `providers\u002F\u003Cpath>\u002Fsrc\u002F**\u002F{hooks,operators,sensors,triggers,\n    notifications,decorators,executors}\u002F**`, the provider's\n    top-level package `__init__.py`, plus anything imported by\n    `provider.yaml` (`hook-class-names`, `extra-links`, etc.).\n    Internal helpers (e.g. `utils\u002F`, `_internal\u002F`, `pod_manager.py`,\n    or any module not re-exported from the package or referenced\n    in `provider.yaml`) are NOT breaking on their own. NOT in tests\u002F.\n  * Required parameter added to a public constructor or operator __init__\n  * Default value of a public parameter changed\n  * Return type or signature of a public method changed\n  * `extra_dejson` \u002F connection-form fields removed or renamed\n  * Behavior change in `execute()`, `poke()`, `get_conn()` that produces\n    different results for the same inputs\n  * Minimum Python or Airflow version bumped (separate: that's\n    min_airflow_bump unless the bump excludes a previously supported version\n    of a provider's hard dependency, in which case it's also breaking)\n  * Removed deprecation: a previously-deprecated symbol is now deleted\n  * Schema change in stored data (xcom, connection, asset metadata,\n    or the serialized state\u002Fcontext of a `BaseTrigger` subclass —\n    deferred tasks survive provider upgrades only if the trigger's\n    `serialize()` payload stays compatible)\n\nDo NOT trust the PR title alone — read the diff. A PR titled \"Refactor X\"\nthat removes a public method is breaking. A PR titled \"BREAKING: rename\nfoo\" that only renames a private symbol is not. A PR that renames a public\nclass introduced earlier *in this same unreleased wave* is not breaking\neither — the old name was never released (see the released-only rule above).\n```\n\nCollect every sub-agent's rows (and any you classified inline) into one\nclassification table for Phase 3.5.\n\n### Phase 3.5 — Confirm with the release manager\n\nPrint a per-provider summary in this exact format (so the release manager\ncan scan it quickly):\n\n```\nProvider: amazon\nCurrent version: 9.12.0\nMost-impactful change: feature → next version: 9.13.0\n\nCommits (12):\n  abc1234  d   high   docs: fix S3 example                                  #65000\n  def5678  b   high   Fix retry on transient SQS error                      #65010\n  9ab0123  f   high   Add wait_for_completion to AthenaOperator              #65020\n  4cd5678  x   med    Remove deprecated S3Hook.list_objects                  #65030  ⚠ BREAKING\n  7ef9012  m   high   Bump aiobotocore to 2.13                              #65040\n  ...\nUncertain: 2 commits below — please confirm:\n  4cd5678  x   med    Remove deprecated S3Hook.list_objects (#65030)\n    Why: list_objects is documented as deprecated since 8.0.0 but never\n    raised DeprecationWarning, so removal may surprise users.\n  abc4321  ?   low    \"Refactor Athena client\" (#65060)\n    Why: PR description says non-breaking but diff changes the default\n    region resolution from env to provider extras.\n```\n\nAlways escalate to the release manager when:\n\n- `CONFIDENCE: low` from any sub-agent.\n- `BREAKING_RISK: maybe` but the sub-agent classified as anything other than\n  `breaking`.\n- Same PR appears in multiple providers and got different classifications\n  across them — explain why and let the RM call it.\n- Most-impactful change is `breaking` (major bump): always reconfirm\n  explicitly before applying. Major bumps are never silent.\n\nIf the release manager corrects a classification, **save it** in your\nclassification table and re-derive the most-impactful change.\n\n### Phase 4 — Apply classifications\n\nFor each provider, in order:\n\n#### 4a. Bump the version in `provider.yaml`\n\nOpen `providers\u002F\u003Cprovider-path>\u002Fprovider.yaml`, find the `versions:` block,\nand prepend the new version. The bump rule (most-impactful classification\nacross all commits for this provider, computed in Phase 3.5):\n\n| Most-impactful           | Bump          |\n| ------------------------ | ------------- |\n| `breaking`               | major (X+1.0.0) |\n| `feature`                | minor (X.Y+1.0) |\n| `min_airflow_bump`       | minor (X.Y+1.0) |\n| `bugfix`                 | patch (X.Y.Z+1) |\n| `misc`                   | patch (X.Y.Z+1) |\n| `documentation` only     | no bump — handle as doc-only (see below) |\n| `skip` only              | no bump — nothing to do |\n\nAlso update `source-date-epoch:` to the current `int(time.time())`.\n\nFor **doc-only** providers, do not bump the version. Instead, write the\nlatest commit hash from the doc-only batch into\n`providers\u002F\u003Cprovider-path>\u002Fdocs\u002F.latest-doc-only-change.txt` (newline\nterminated). This is what breeze checks on the next release to know the\nprovider hasn't really changed.\n\n#### 4b. Write the changelog entry\n\nOpen `providers\u002F\u003Cprovider-path>\u002Fdocs\u002Fchangelog.rst`. Insert a new section\n*above* the most recent existing version section. The exact format must\nmatch `dev\u002Fbreeze\u002Fsrc\u002Fairflow_breeze\u002Ftemplates\u002FCHANGELOG_TEMPLATE.rst.jinja2`\n— don't paraphrase it. The skeleton:\n\n```rst\n\u003CNEW_VERSION>\n\u003Cdots matching length of NEW_VERSION>\n\n.. note::\n    This release of provider is only available for Airflow X.Y+ as explained in the\n    Apache Airflow providers support policy \u003Chttps:\u002F\u002Fgithub.com\u002Fapache\u002Fairflow\u002Fblob\u002Fmain\u002FPROVIDERS.rst#minimum-supported-version-of-airflow-for-community-managed-providers>_.\n\nBreaking changes\n~~~~~~~~~~~~~~~~\n\n* ``\u003Ccommit subject for breaking change> (#NNNN)``\n\nFeatures\n~~~~~~~~\n\n* ``\u003Ccommit subject for feature> (#NNNN)``\n\nBug Fixes\n~~~~~~~~~\n\n* ``\u003Ccommit subject for bugfix> (#NNNN)``\n\nMisc\n~~~~\n\n* ``\u003Ccommit subject for misc\u002Fmin_airflow_bump> (#NNNN)``\n\nDoc-only\n~~~~~~~~\n\n* ``\u003Ccommit subject for doc> (#NNNN)``\n\n.. Below changes are excluded from the changelog. Move them to\n   appropriate section above if needed. Do not delete the lines(!):\n   * ``\u003Ccommit subject for skip> (#NNNN)``\n```\n\nRules:\n\n- A `.. note::` block at the top of the version section (directly under the\n  `\u003Cdots>` underline, before the first `~~~` header) is used in two distinct\n  situations. Include it whenever **either** applies — combine the wording\n  into a single note, or stack two notes, when both do:\n  - **Airflow min-version bump** — when the bump was driven by a\n    `min_airflow_bump` (or by a `breaking` whose breaking aspect *is* the\n    Airflow min bump), use the support-policy wording shown in the skeleton.\n  - **Breaking change** — for **every** `breaking` classification (major\n    bump, including a `0.x` minor that ships a breaking change), add a note\n    explaining *what* breaks and *how users should adapt* (the migration\n    path). Write it from the PR description and the actual diff, not as a\n    restatement of the commit subject — the reader must learn how to react\n    without opening the PR. This mirrors the standing changelog convention\n    (\"only add notes … when there are some breaking changes and you want to\n    add an explanation to the users on how they are supposed to deal with\n    them\"). The bullet under `Breaking changes` still lists the commit\n    subject as usual; the note is in addition to it, not a replacement.\n- Drop a section entirely if it has no entries (e.g. no `Breaking changes`\n  section if there were none — don't leave an empty header).\n- The `.. Below changes are excluded ...` block at the end is required even\n  if empty. Lines under it use the indented `   * ``...``` form (three-space\n  indent, double backticks).\n- Subjects must be the original commit subject with backticks replaced by\n  single quotes (matches `message_without_backticks`). Don't paraphrase.\n- **Exception — rewrite subjects written in project-internal language.** The\n  don't-paraphrase rule keeps you honest about *what* shipped; it does not\n  oblige you to publish a subject the reader cannot act on. The changelog is\n  read by users, not by us. Rewrite the entry — keeping every `(#NNNN)` — when\n  the subject is only meaningful inside the project:\n  - **Spell out internal abbreviations.** `KPO`, `DFP`, `TI`, `RTIF`, `OL` and\n    friends are our shorthand. Write ``Add '--min-completed-minutes' to\n    'cleanup-pods' to prevent KubernetesPodOperator race condition (#70595)``,\n    not ``… to prevent KPO race condition (#70595)``.\n  - **Name what a grouped dependency bump actually changed.** Dependabot group\n    subjects such as ``Bump the fab-ui-package-updates group across 1 directory\n    with 3 updates (#70604)`` tell the reader nothing. Read the PR diff and list\n    the packages with their versions: ``Bump prettier to 3.9.6, stylelint to\n    17.14.1, webpack to 5.109.0 (#70604)``. A single-package bump still needs\n    its target version: ``Bump eslint to 10.8.0 (#70697)``.\n  - **Describe the user-visible effect, not the internal mechanic.** ``Remove\n    noqa:S101 from production code (#70378)`` names a lint directive; the reader\n    wants ``Mark asserts under 'TYPE_CHECKING' in 'DocumentLoaderOperator'\n    (#70378)``.\n  - **Drop our test vocabulary.** A system test is just an example Dag to the\n    user, so ``Remove hard-coded deferrable crawler run from example_glue system\n    test (#70206)`` becomes ``Remove hard-coded deferrable crawler run from\n    example_glue (#70206)``.\n\n  Rewrite the *wording*, never the *claim* — do not describe behaviour that did\n  not ship. When a subject is too vague to rewrite honestly, read the PR diff\n  before writing the entry.\n- **Exception — collapse within-wave \"add then rename\u002Frework\" chains into one\n  net entry.** When several pending commits are steps toward *one* net change —\n  a feature added in one PR and renamed or reworked in a later PR, both since\n  the last release (the released-only situation from Phase 3) — do **not** list\n  the intermediate steps as separate entries. A reader who never saw the\n  released intermediate form gets no context from ``Add X listener (#a)`` under\n  Features plus ``Rename X to Y (#b)`` under Misc. Write a **single** entry that\n  describes the **net user-facing change** and references **all** related PRs,\n  placed in the section of the most-impactful step. Real example: #68082 added\n  a Kafka listener and #70014 renamed it to the Kafka Event Producer in the same\n  wave → ``Add Kafka Event Producer publishing DagRun and TaskInstance\n  state-change events (#68082, #70014)`` under Features (and *no* separate Misc\n  \"Rename …\" line). This is the changelog counterpart of the unreleased-feature\n  classification rule: classify the rename as non-breaking (Phase 3) **and**\n  describe only what shipped, naming every PR involved.\n- **Capitalize the first letter of every entry**, not only after stripping a\n  Conventional Commit prefix. Contributors sometimes write a lowercase subject\n  (`derive keycloak oauth redirect_uri …`) or a pseudo-scope\n  (`cncf-kubernetes: fix …`); the changelog convention is a leading capital, so\n  render them as ``Derive keycloak oauth redirect_uri …`` \u002F\n  ``Cncf-kubernetes: fix …``.\n- **Strip Conventional Commit prefixes** before writing to the changelog.\n  If the subject starts with a prefix like `feat:`, `fix:`, `chore:`,\n  `docs:`, `refactor:`, `ci:`, `test:`, `perf:`, `build:`, or `style:`\n  (with or without a scope in parentheses, e.g. `fix(amazon):`), remove\n  the prefix and capitalize the first letter of the remaining text.\n  Example: `refactor: Fix _is_http_client_closed ...` →\n  `Fix _is_http_client_closed ...`. Airflow does not use Conventional\n  Commits and these prefixes should not appear in changelogs.\n- **Send no-PR release-tooling commits to the excluded block**, even when the\n  Phase 1 deterministic classifier labeled them `documentation`. Subjects like\n  ``Prepare … providers release\u002Fdocumentation …`` and ``Hide non-user-facing\n  entries from ad-hoc provider release notes`` (often with no `(#NNNN)` suffix\n  because they were committed directly) are release plumbing, not user-facing\n  changes — a `(#NNNN)`-less line in a visible section reads as a mistake. Put\n  them under the `.. Below changes are excluded …` block. breeze's deterministic\n  classification can even be inconsistent for the same commit across providers,\n  so normalize to excluded.\n- Always keep the `(#NNNN)` PR suffix (or, for a collapsed chain, the\n  comma-separated list of all involved PRs).\n- **Order entries within each section by merge order, newest first** — the exact\n  order `git log` printed them in Phase 1. `CHANGELOG_TEMPLATE.rst.jinja2`\n  iterates the changes without sorting, so that order *is* the format. It is\n  **not** descending PR number: a long-lived PR merged late carries a low number\n  and still belongs at the top (real example: `#64274` sits second in amazon\n  9.32.0's `Features`). Don't re-sort by PR number and don't group by theme.\n  The excluded block follows the same order. A collapsed chain — one entry\n  naming several PRs — sits at the position of its **first** commit, which is\n  when the change became relevant, not at the position of the later rework.\n- **Never adopt an entry a contributor pre-wrote at the top of\n  `changelog.rst`** — above the first version header — without checking its PR\n  number against `git log`. Those blocks are written before the PR merges, so\n  the number in them is a guess and is often wrong or nonexistent. Keep the\n  prose, replace the reference with the real merge commit's `(#NNNN)`.\n\n#### 4c. Regenerate templates with breeze\n\nOnce **all** providers have their `provider.yaml` and `changelog.rst`\nupdated, run:\n\n```bash\nbreeze release-management prepare-provider-documentation \\\n    --reapply-templates-only \\\n    --skip-git-fetch \\\n    --release-date \"$RELEASE_DATE\"\n```\n\nThis regenerates `__init__.py`, `README.rst`, `pyproject.toml`, `conf.py`,\n`get_provider_info.py`, and `index.rst` for every provider — picking up the\nnew versions you just wrote. It will not touch `changelog.rst`.\n\n> [!NOTE]\n> `commits.rst` per provider is also stable template content (the actual\n> commit list is rendered at doc-build time via the\n> `airflow-providers-commits` directive). It will be regenerated on the\n> next full release. No action needed here.\n\n#### 4d. Resolve `# use next version` inter-provider pins\n\nContributors can defer an inter-provider dependency bump by pinning it in\n`pyproject.toml` with a trailing `# use next version` comment, instead of\nhard-coding a version that does not exist yet. Now that the versions are\nbumped, resolve those pins:\n\n```bash\nbreeze release-management update-providers-next-version\n```\n\nThis rewrites every `# use next version` dependency to the just-bumped\nversion of the referenced provider and removes the comment. Pins whose\nreferenced provider already has a final release tag are left alone (the\n\"next version\" is then a future release); when only release candidate tags\nexist the pin is updated with a warning, revert it if that provider is in\nvote rather than re-cut in this release.\n\n> [!IMPORTANT]\n> **Run this every time, before opening the PR — even when you believe no\n> provider uses the comment** (the command is a safe no-op when none do).\n> Skipping it ships the wave with stale lower bounds on inter-provider\n> dependencies; once the PR is merged the only remedy is a separate\n> follow-up PR. This is the \"Update versions of dependent providers to the\n> next version\" step in `dev\u002FREADME_RELEASE_PROVIDERS.md` — it lives between\n> doc preparation and PR creation, so it is easy to forget when the skill\n> hands back to the regular release workflow.\n\nThe command only resolves pins an author marked. While classifying, also\nnote every PR that starts importing a symbol from another provider (most often\n`common.compat`) and check that the importing provider's floor is at least\nthe first release that shipped it, even when the importing provider is not in\nthis wave. If it is not, raise the floor in this PR and tell the release\nmanager. The recipe is in \"Update versions of dependent providers to the next\nversion\" in `dev\u002FREADME_RELEASE_PROVIDERS.md`.\n\n**Provider dependency-bump CI guard.** Every `>=` bump this produces (and any\ninter-provider `>=` bump made during the wave, e.g. a `breaking` provider that\ndependents must now require) trips the `check_provider_dependency_bumps`\nselective-check (`dev\u002Fbreeze\u002Fsrc\u002Fairflow_breeze\u002Futils\u002Fselective_checks.py`),\nwhich fails CI with *\"Provider dependency version bumps detected that should\nonly be performed by Release Managers!\"*. That guard exists to stop\n**contributors** from silently changing inter-provider `>=` floors; for a\nrelease wave the bumps are legitimate. The release PR **must carry the\n`allow provider dependency bump` label** to bypass it — every prior \"Prepare\nproviders release …\" PR carries this label. Tell the release manager to add\nthe label to the PR; the bumps are not a mistake to revert. Adding a label\ndoes not re-run CI (the workflow has no `labeled` trigger), so if CI already\nran without it, close and reopen the PR to start a run that sees the label.\n\n### Phase 5 — Validate\n\nRun the same checks the release manager would run:\n\n```bash\n# RST lint + license headers + ruff on Python files\nprek run --from-ref main --hook-stage pre-commit\n\n# Spot-check that provider.yaml versions parse\nbreeze release-management prepare-provider-documentation \\\n    --reapply-templates-only --skip-git-fetch \\\n    --release-date \"$RELEASE_DATE\"   # idempotent — should be a no-op diff\n```\n\nThen `git diff --stat` and walk the release manager through the diff\nprovider-by-provider:\n\n- Confirm the version in `provider.yaml` matches the bump rule.\n- Confirm `changelog.rst` has the right sections populated.\n- **Check for misplaced top-level note blocks.** Each provider's\n  `changelog.rst` has a standing `.. NOTE TO CONTRIBUTORS:` RST comment\n  near the top (above all version sections). If you detect any `.. note::`\n  directive that is NOT nested under a specific version section (i.e. it\n  appears before the first version header, or between the `Changelog`\n  heading and the first version), notify the release manager immediately —\n  it likely means a breaking-change or min-version note was accidentally\n  written at the wrong indentation level or position.\n\n  Two variants to check for, both seen in the 2026-08-01 wave:\n  - **Already misplaced on the base branch.** A contributor adds a note for\n    their own change directly under the `Changelog` header instead of inside\n    the version section it belongs to, so it renders page-wide. Move it into\n    the section that ships the change (`google` #70869).\n  - **Pushed out of place by your own prepend.** When a note was sitting above\n    the first version header and you prepend a new section, the note ends up\n    *between* your new excluded block and the previous version — still wrong,\n    but no longer above the first header, so a scan that only looks at the top\n    of the file misses it (`openai` #69506). Check **inside every section you\n    touched** that no `.. note::` appears after the\n    `.. Below changes are excluded …` marker; a note belongs directly under the\n    version underline, before the first `~~~` header.\n\n  The same applies to a bare `Breaking changes` \u002F `Features` \u002F … heading sitting\n  above the first version header: a contributor pre-wrote it, and it must be\n  folded into the new version section with its PR reference corrected (Phase 4b).\n- Confirm Phase 4d ran: no `# use next version` comment remains where the\n  referenced provider was bumped in this wave.\n- **If any inter-provider `>=` floor changed** (Phase 4d resolved a pin, or a\n  `breaking` provider forced a dependent to require its new major), tell the\n  release manager the PR needs the `allow provider dependency bump` label —\n  otherwise the `check_provider_dependency_bumps` CI check fails with\n  *\"Provider dependency version bumps detected that should only be performed\n  by Release Managers!\"*. `git diff` the changed `pyproject.toml` files for\n  `apache-airflow-providers-*` `>=` changes and list them for the RM.\n- **Reconcile every new section against `git log` — this gate is mandatory.** Run\n\n  ```bash\n  python3 dev\u002Fcheck_changelog_entries.py --fix\n  ```\n\n  It compares each provider's newest section against the commits actually being\n  released and exits non-zero on four defects that eyeballing the diff misses:\n\n  | Code | Meaning | Action |\n  | --- | --- | --- |\n  | `MISSING` | a released commit has no entry | classify and add it (Phase 3 + 4b) |\n  | `UNKNOWN` | an entry cites a PR outside the release range | replace with the real `(#NNNN)` |\n  | `SECTION` | heading is not one of the template's five | move the entry under a template heading |\n  | `ORDER` | entries are off merge order | repaired by `--fix` |\n\n  Re-run it after **any** rebase of the release branch. A rebase silently pulls\n  new provider commits into the release range without touching `changelog.rst`,\n  and that is exactly how a shipped change ends up undocumented. Only `ORDER`\n  is auto-repairable — resolve every other code by hand before handing off.\n- **Scan the new changelog sections for these entry defects** — grep the lines\n  you added: (1) a bullet whose text starts with a lowercase letter → capitalize\n  it (Phase 4b); (2) a bullet in a *visible* section (Features \u002F Bug Fixes \u002F\n  Misc \u002F Doc-only) with no `(#NNNN)` suffix → usually no-PR release-tooling that\n  belongs in the excluded block (Phase 4b); (3) an \"add then rename\" pair for\n  the same feature left as two separate entries → collapse into one net entry\n  naming both PRs (Phase 4b); (4) an internal abbreviation (`KPO`, `DFP`, `TI`,\n  `RTIF`, `OL`) or the phrase \"system test\" → rewrite in user-facing language\n  (Phase 4b); (5) a dependency bump that names no version, or a Dependabot group\n  subject of the form \"… group … with N updates\" → replace with the actual\n  packages and versions from the PR diff (Phase 4b). Reviewers reliably catch\n  all of these, so fix them before handing off.\n- Flag anything where Phase 3.5 had to escalate, so the RM can double-check.\n- **List every provider in this wave that is an initial release** (no prior\n  release tag — the Phase 1 `note`\u002F`error` case) and remind the release\n  manager that each needs an entry in the `apache\u002Fairflow-site` docs index:\n  open that PR before publishing the docs, merge it after.\n  `breeze workflow-run publish-docs --site-env live` fails while a provider is\n  neither in the index nor added by an open PR. Follow\n  [Update airflow-site index for new providers](..\u002F..\u002F..\u002Fdev\u002FREADME_RELEASE_PROVIDERS.md#update-airflow-site-index-for-new-providers).\n\nStop here. Do not commit, do not push — the release manager opens the PR\nthemselves following the regular release workflow in\n`dev\u002FREADME_RELEASE_PROVIDERS.md`. Make sure Phase 4d\n(`update-providers-next-version`) has been run before that PR is opened, and\nthat the PR carries the `allow provider dependency bump` label whenever any\ninter-provider `>=` floor changed (see Phase 4d).\n\n---\n\n## Incremental Update\n\nUse this flow when the release PR has already been opened (changelog and\nversion bumps applied via Phases 1–5) and the release manager rebases it\nto pick up commits that landed on `main` after the original classification.\nThis is the equivalent of `breeze release-management\nprepare-provider-documentation --incremental-update`, but driven by the\nsame AI classification logic as the initial run.\n\nOn a **wave**, \"extend\" is not only about the providers already in the PR: a\nprovider that had nothing pending when the wave was cut can pick up a\nuser-facing change before the PR merges, and the flow below is responsible for\nsurfacing it rather than letting the release ship without it.\n\n> [!IMPORTANT]\n> Run on the **release PR branch** *after* rebasing onto the latest base\n> branch. Do not start the incremental flow on a clean checkout — it needs\n> the prior classifications already written into `changelog.rst` to\n> diff against.\n\n### Incremental Phase 1 — Refresh the apache remote\n\n```bash\nbreeze release-management prepare-provider-documentation \\\n    --reapply-templates-only \\\n    --release-date \"$RELEASE_DATE\"\n```\n\nThis re-fetches `apache-https-for-providers\u002F\u003Cbase-branch>` and regenerates\nthe auto-generated build files for every provider — picking up any\nupstream template changes that landed since the original PR was opened.\nIt does **not** touch `provider.yaml` or `changelog.rst`.\n\n### Incremental Phase 2 — Detect unrecorded commits across **all** providers\n\n> [!IMPORTANT]\n> On a **wave**, sweep **every** provider — not just the ones already in the\n> release PR. A provider that had nothing pending when the wave was cut can\n> acquire its first user-facing change hours later, and iterating only over\n> providers that already have a new version section can never discover it: it\n> has no section to extend. That provider then stays invisible for the rest of\n> the release. This is an observed miss, not a hypothetical one — see the\n> worked example at the end of this phase.\n\nRe-run the deterministic classifier over the full provider set — the same\ncommand as Phase 1 of the initial run, with **no** provider ids appended:\n\n```bash\nbreeze release-management classify-provider-changes \\\n    --base-branch main \\\n    --output-file \u002Ftmp\u002Fprovider-changes.json\n```\n\nThen reduce its output to commits **not yet recorded** in the matching\nprovider's changelog. A commit is unrecorded when its `#NNNN` PR number\nappears nowhere in `providers\u002F\u003Cprovider-path>\u002Fdocs\u002Fchangelog.rst` — the same\npredicate breeze uses internally (see the `_generate_new_changelog` append\nbranch in\n`dev\u002Fbreeze\u002Fsrc\u002Fairflow_breeze\u002Fprepare_providers\u002Fprovider_documentation.py`):\n\n```bash\npython3 - \u003C\u003C'EOF'\nimport json, pathlib\n\ndata = json.loads(pathlib.Path(\"\u002Ftmp\u002Fprovider-changes.json\").read_text())\nfor provider_id, info in sorted(data[\"providers\"].items()):\n    base = pathlib.Path(\"providers\") \u002F provider_id.replace(\".\", \"\u002F\")\n    changelog = base \u002F \"docs\" \u002F \"changelog.rst\"\n    seen = changelog.read_text() if changelog.exists() else \"\"\n    doc_only = base \u002F \"docs\" \u002F \".latest-doc-only-change.txt\"\n    doc_hash = doc_only.read_text().strip() if doc_only.exists() else \"\"\n    for commit in info.get(\"commits\", []):\n        pr = commit.get(\"pr\")\n        # Substring match, NOT \"(#NNNN)\": a collapsed within-wave entry reads\n        # \"(#68082, #70014)\", which an exact-suffix match would report as new.\n        if pr and f\"#{pr}\" in seen:\n            continue\n        if not pr and commit[\"subject\"].replace(\"`\", \"'\") in seen:\n            continue\n        if doc_hash and commit[\"hash\"].startswith(doc_hash[:10]):\n            continue\n        print(f\"{provider_id}\\t{commit['hash'][:10]}\\t#{pr}\\t\"\n              f\"{commit['classification']}\\t{commit['subject']}\")\nEOF\n```\n\nSplit the result into two buckets. A provider is **already in the wave** iff\nits `provider.yaml` changed on this branch:\n\n```bash\ngit diff --name-only \u003Cbase-branch>..HEAD -- '**\u002Fprovider.yaml' \\\n  | sed 's|providers\u002F||; s|\u002Fprovider.yaml||' | tr '\u002F' '.' | sort\n```\n\n- **Bucket A — already in the wave.** Fold the unrecorded commits into the\n  existing version section: continue through Incremental Phases 3 → 3.5 → 4a.\n- **Bucket B — not in the wave.** The provider has unrecorded commits but no\n  new version section, so it is a candidate to *join*. Classify it\n  (Incremental Phase 3), then take it to **Incremental Phase 3.6**.\n- **Bucket C — in the wave but no longer warranting a release.** The sweep\n  above only finds commits *missing* from a wave; this bucket is the opposite\n  direction, and nothing else in the flow looks for it. A provider that review\n  has since concluded is internal-only or documentation-only still carries the\n  version bump and changelog section written by the initial run, so it would\n  still be built and uploaded to PyPI. Find these by re-reading the entries of\n  each in-wave provider's newest changelog section: if every entry is\n  `Doc-only`, or review reclassified the last remaining non-doc entry as\n  documentation, the provider should leave the wave.\n\n  Drop it back with the dedicated command rather than editing the files by\n  hand — it restores `provider.yaml` and `changelog.rst` to their released\n  state and writes the marker in one step:\n\n  ```bash\n  breeze release-management prepare-provider-documentation --mark-doc-only \u003Cprovider-id>\n  ```\n\n  Then commit `providers\u002F\u003Cprovider-path>\u002Fdocs\u002F.latest-doc-only-change.txt`.\n  **Ask the release manager before doing it** — taking a provider out of a wave\n  is their decision, and a `Misc` entry that merely *looks* internal may still\n  be something they want released.\n\nMost Bucket B rows are ordinary noise — repo-wide tooling and test commits\nthat correctly keep a provider out of the release. A Bucket B provider whose\nunrecorded commits are **all** `skip` stays out silently; escalate only when\nat least one classifies as something other than `skip`.\n\nIf a provider has zero unrecorded commits, skip it.\n\n> [!NOTE]\n> **Worked example — the miss this phase exists to prevent.** In the\n> 2026-07-22 wave, `atlassian.jira` had nothing pending when the wave was cut\n> at 22:15. PR #70224 merged at 03:41 the next morning, adding a `Verify SSL`\n> conn-field to `providers\u002Fatlassian\u002Fjira\u002Fprovider.yaml`. The branch was\n> rebased past that commit and an incremental fold-in ran — but because\n> `atlassian.jira` had no version section, the old per-wave-provider loop\n> never looked at it, and the provider was still missing from the release PR\n> a day later. The full sweep above surfaces it; Phase 3.6 asks about it.\n\n### Incremental Phase 3 — Classify the new commits\n\nSame logic as Phase 3 of the initial run — including the auto-classify\nheuristic for docs\u002Ftest-only changes and the batched classification (inline\nwhen few commits remain, otherwise one sub-agent per provider) with the\nbreaking-change checklist. Incremental runs usually have only a handful of new\ncommits, so prefer classifying them inline rather than spawning any sub-agent.\nThe output is a per-provider table mapping each new commit hash to a\nclassification.\n\n### Incremental Phase 3.5 — Decide whether to escalate the version bump\n\nCompute the most-impactful classification across **both** the existing\nclassified commits in the changelog **and** the new ones. If the most\nimpactful is now stronger than what's already in `provider.yaml`, the\nversion needs to be re-bumped. The escalation table:\n\n| Was bumped to | Now most-impactful is | Action                                  |\n| ------------- | --------------------- | --------------------------------------- |\n| patch         | `feature`             | re-bump to next minor (X.Y+1.0)         |\n| patch         | `min_airflow_bump`    | re-bump to next minor (X.Y+1.0)         |\n| patch \u002F minor | `breaking`            | re-bump to next major (X+1.0.0)         |\n| minor         | `feature`             | no change — already minor               |\n| anything      | `bugfix` or `misc`    | no change                               |\n\nA re-bump means: replace the prepended version in `provider.yaml` AND\nupdate the version header in `changelog.rst`'s new section to match.\n\n**Always confirm a re-bump with the release manager** — explicitly state\nthe old version, the new version, and which incoming commit forced the\nescalation. Don't silently re-bump.\n\n### Incremental Phase 3.6 — Ask before adding a provider to the wave\n\nEvery Bucket B provider (Incremental Phase 2) with at least one unrecorded\ncommit classified as something other than `skip` is a provider the release is\ncurrently missing. **Never add one silently, and never drop one silently —\nalways ask.** The release manager owns the scope of the release; your job is\nto make sure the choice is made deliberately rather than by omission.\n\nOn an **ad-hoc** release the answer is usually \"leave it out\" — the provider\nlist is a fixed input — but still surface it, so the release manager knows the\nchange exists and needs a later release.\n\nAsk once per provider. State what landed, why it is user-facing, when it\nlanded relative to the cut, and the version consequence of each option:\n\n> Provider `atlassian.jira` is **not** in this wave, but PR #70224 (\"Fix\n> conn-fields check crash for nested provider packages\", merged 2026-07-23\n> 03:41 — about 5h after the wave was cut) added a `Verify SSL` conn-field and\n> `ui-field-behaviour` to its `provider.yaml`. That is shipped metadata: it\n> changes the Jira connection form. Its other pending commits (#67978, #68991)\n> are test\u002Ftemplate-only.\n> **Add it to the wave** (3.3.4 → 3.3.5, most-impactful `misc`), or **leave it\n> out** and let the change ride the next release?\n\nRecord the answer. If the release manager says **add**, the provider follows\nthe *initial-run* application path rather than the append path — see\nIncremental Phase 4b.\n\n### Incremental Phase 4 — Apply the new entries\n\n#### 4a. Providers already in the wave — append to the existing section\n\nFor each new commit, insert into the existing latest-version section of\n`changelog.rst` under the right header:\n\n| Classification         | Section                |\n| ---------------------- | ---------------------- |\n| `breaking`             | `Breaking changes`     |\n| `feature`              | `Features`             |\n| `bugfix`               | `Bug Fixes`            |\n| `misc`                 | `Misc`                 |\n| `min_airflow_bump`     | `Misc`                 |\n| `documentation`        | `Doc-only`             |\n| `skip`                 | excluded block at end  |\n\nIf the section header doesn't exist yet (e.g. previously there were no\nbreaking changes, but a new commit introduced one), create the header\nabove the next existing section, matching the order in\n`CHANGELOG_TEMPLATE.rst.jinja2`:\n`Breaking changes` → `Features` → `Bug Fixes` → `Misc` → `Doc-only`.\n\nInsert each entry at its **merge-order position** within the section — new\ncommits are newer than everything already there, so they go at the *top*, not\nappended at the bottom. Appending is what drifts incremental providers off the\nformat; `check_changelog_entries.py --fix` repairs it either way, so run it\nafter this phase.\n\nIf you re-bumped the version in Incremental Phase 3.5, also add or remove the\n`.. note::` block about the Airflow min version requirement to match the\nnew bump kind.\n\nIf a new commit is classified `breaking`, add (or extend) a `.. note::` at the\ntop of the version section explaining what breaks and how users should adapt,\nexactly as in the breaking-change note rule in the initial run's Phase 4b.\n\n#### 4b. Providers joining the wave — apply the initial-run path\n\nA provider the release manager confirmed in Incremental Phase 3.6 has no new\nversion section yet, so there is nothing to append to. Run the steps of the\n**initial run's Phase 4** for that provider only:\n\n- **initial-run 4a** — prepend the new version to `provider.yaml` and refresh\n  `source-date-epoch`. Reuse the epoch the rest of the wave already carries\n  (`grep -h source-date-epoch` across the providers this branch bumped) so the\n  release stays on a single value rather than gaining a stray third one.\n- **initial-run 4b** — write a complete new version section in\n  `changelog.rst`: the classified commits under their headers, **every**\n  `skip` commit in the `.. Below changes are excluded …` block, and a\n  `.. note::` if the bump is breaking or a min-Airflow bump.\n- **initial-run 4c** — re-run `--reapply-templates-only` so the joining\n  provider's `__init__.py`, `README.rst`, `pyproject.toml`, `index.rst` pick\n  up the new version. Expect a diff for exactly that provider and no other.\n- **initial-run 4d** — re-run `update-providers-next-version`: a `# use next\n  version` pin pointing at the joining provider must now resolve.\n\n### Incremental Phase 5 — Validate\n\nSame as Phase 5 of the initial run plus an extra check: confirm there are\nno leftover \"Please review …\" markers from a prior interactive\n`breeze release-management prepare-provider-documentation\n--incremental-update` run. If any are present (someone ran the breeze\nincremental flow before invoking this skill), remove them as part of the\nfinal pass. Then walk the diff with the release manager.\n\nAlso confirm that **every Bucket B provider from Incremental Phase 2 was\neither added or explicitly declined** by the release manager — none dropped\nby omission. Re-run the Phase 2 sweep after applying; the only providers it\nshould still report are ones whose unrecorded commits are all `skip`, plus any\nthe release manager deliberately left out.\n\nIf the incremental run bumped a provider to a *new* version (Incremental\nPhase 3.5) or added one to the wave (Incremental Phase 3.6), re-run Phase 4d\n(`update-providers-next-version`) as well — a `# use next version` pin on that\nprovider must resolve to the freshly bumped version before the rebased PR is\npushed.\n\n---\n\n## Cross-Cutting Rules\n\n### PRs covering multiple providers\n\nWhen a single PR touches several providers (e.g.\n`Add Python 3.14 Support (#63520)` touches dozens), classify it\n**independently per provider**. The same PR can be `feature` in one provider\n(a real new capability) and `misc` in another (just a constraint bump in\n`pyproject.toml`). Always scope the diff inspection (whether inline or in a\nper-provider sub-agent) to the current provider's path:\n\n```bash\ngh pr diff \u003CNNNN> -- 'providers\u002F\u003Cprovider-path>\u002F**'\n```\n\nIf the per-provider classifications come back different, do NOT try to\n\"reconcile\" them — that's a feature, not a bug. The release manager wants\neach provider's changelog to reflect what changed in *that* provider.\n\n### Asking the release manager — phrasing\n\nWhen you ask, state your best guess and the alternative explicitly:\n\n> Provider `amazon`, commit `4cd5678` (\"Remove deprecated `S3Hook.list_objects`\"\n> #65030): I classified this as **breaking** because the symbol is removed\n> from the public API in `providers\u002Famazon\u002Fsrc\u002Fairflow\u002Fproviders\u002Famazon\u002Faws\u002Fhooks\u002Fs3.py`,\n> even though the PR description says \"deprecated since 8.0.0\". Confirm\n> **breaking** (major bump 9.x → 10.0.0) or override to **misc** (patch)?\n\nDon't ask vague yes\u002Fno questions (\"is this breaking?\"); always offer the\ntwo alternatives with the version-bump consequence.\n\n### Things you must NOT do silently\n\n- Bump major version without explicit confirmation from the release manager.\n- Add a provider to the wave — or leave one out — without asking\n  (Incremental Phase 3.6). Scope is the release manager's call, and a\n  provider dropped by omission is as much a silent decision as one added.\n- Reclassify a commit the RM already confirmed.\n- Skip commits that don't fit a category — flag them as `?` and ask.\n- Edit `commits.rst`, `index.rst`, `__init__.py`, `README.rst`,\n  `pyproject.toml`, `conf.py`, `get_provider_info.py` directly. Those are\n  template-generated by breeze.\n- Run `git add` or `git commit` — the release manager owns the PR.\n\n### When to give up and fall back to interactive breeze\n\nIf the per-provider commit count is huge (50+) **and** the sub-agents come\nback with `low` confidence on most of them (typically because the diffs\nrequire deep domain knowledge), tell the release manager you're stopping\nthe AI classification and recommend they run the regular interactive\n`breeze release-management prepare-provider-documentation` for that\nspecific provider. Don't try to power through guesswork — the wrong\nclassification at major-bump granularity is worse than a slower manual run.\n\n---\n\n## References\n\n- `dev\u002Fbreeze\u002Fsrc\u002Fairflow_breeze\u002Fprepare_providers\u002Fprovider_documentation.py`\n  — the breeze module this skill replaces (classification + changelog\n  generation). Read this when in doubt about format.\n- `dev\u002Fbreeze\u002Fsrc\u002Fairflow_breeze\u002Ftemplates\u002FCHANGELOG_TEMPLATE.rst.jinja2`\n  — exact format for the changelog section you write in Phase 4b.\n- `dev\u002FREADME_RELEASE_PROVIDERS.md` §\"Convert commits to changelog entries\n  and bump provider versions\" — the human workflow this skill automates.\n- `PROVIDERS.rst` §\"Upgrading minimum supported version of Airflow\" —\n  policy for `min_airflow_bump` classifications.\n",{"name":5,"license":15,"description":49},"Replace the manual commit-by-commit classification step in `breeze release-management prepare-provider-documentation` with AI-driven classification. For each provider with pending changes, analyze every PR (batched into one sub-agent per provider, not one per PR), pay special attention to potentially breaking changes by inspecting the actual diff, scope multi-provider PRs to the current provider's slice, ask the release manager when uncertain, and apply version bumps + changelog entries. Use during the regular provider release cycle as an alternative to the interactive breeze prompts.\n","main","84a2037827b7da4db45a4899b607fa4b1e4f809e",".agents\u002Fskills\u002Fprepare-providers-documentation",18343,"https:\u002F\u002Fgithub.com\u002Fapache\u002Fairflow","https:\u002F\u002Fairflow.apache.org\u002F","https:\u002F\u002Fgithub.com\u002Fapache","https:\u002F\u002Fwww.apache.org\u002F","2026-11-11 11:20:17",{"source":8,"skillName":5,"commands":60,"prompt":70,"zipAvailable":71},[61,64,67],{"tool":62,"command":63},"npx","npx skills add apache\u002Fairflow --skill prepare-providers-documentation",{"tool":65,"command":66},"bunx","bunx skills add apache\u002Fairflow --skill prepare-providers-documentation",{"tool":68,"command":69},"pnpm","pnpm dlx skills add apache\u002Fairflow --skill prepare-providers-documentation","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fapache\u002Fairflow\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fprepare-providers-documentation\u002FSKILL.md",true,[],[74,103,125,151,173,194],{"skillNo":75,"name":76,"nameZh":6,"description":77,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":78,"categories":79,"keywords":88,"publishedAt":44},"01M6ACBAC3S7GHPX3EB91CA2K5","sql-reporting","Conventions and review steps for writing analytics SQL against the warehouse. Use whenever the task involves querying tables, building a report, or aggregating metrics.","https:\u002F\u002Fgithub.com\u002Fapache\u002Fairflow\u002Fblob\u002Fmain\u002Fproviders\u002Fcommon\u002Fai\u002Fsrc\u002Fairflow\u002Fproviders\u002Fcommon\u002Fai\u002Fexample_dags\u002Fskills\u002Fsql-reporting\u002FSKILL.md",[80,83],{"categoryNo":81,"categoryName":82,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000010","后端与接口",{"categoryNo":84,"categoryName":85,"taxonomyType":86,"taxonomyTypeDesc":87},"01JBSEEDCAT000000000000033","数据分析师","PROFESSION","适用职业",[89,90,91,92,93,94,95,96,97,98,99,100,101,102],"sql reporting","sql","reporting","writing","review","report","analytics","metrics","tables","warehouse","for","use","task","conventions",{"skillNo":104,"name":105,"nameZh":6,"description":106,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":107,"categories":108,"keywords":109,"publishedAt":44},"01M6ACB7P9C6TV0J1FGWW5HECF","aip-tracker","Track Airflow Improvement Proposal (AIP) implementation progress by comparing Confluence specs against codebase evidence. Use when asked to assess, report on, or compare AIP status.","https:\u002F\u002Fgithub.com\u002Fapache\u002Fairflow\u002Fblob\u002Fmain\u002Fproviders\u002Fcommon\u002Fai\u002Fsrc\u002Fairflow\u002Fproviders\u002Fcommon\u002Fai\u002Fexample_dags\u002Fskills\u002Faip-tracker\u002FSKILL.md",[],[110,111,112,113,94,114,115,28,116,117,118,119,120,100,121,122,123,124],"aip tracker","aip","tracker","airflow","status","proposal","track","progress","evidence","improvement","assess","compare","codebase","implementation","confluence",{"skillNo":126,"name":127,"nameZh":6,"description":128,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":129,"categories":130,"keywords":131,"publishedAt":44},"01M6ACB55GGWPTWFKM3BVQS4W5","upgrade-fab-provider","Upgrade the pinned Flask-AppBuilder (FAB) dependency in the Apache Airflow FAB provider (`providers\u002Ffab\u002F`). Bumps the exact `flask-appbuilder==` pin and its mirror constant, regenerates `uv.lock`, drives the `test_fab_alignment.py` drift tripwire, reviews the vendored security-manager `override.py` against the new upstream FAB, and conditionally re-vendors static assets \u002F DB migrations. Use when asked to \"upgrade FAB\", \"bump flask-appbuilder\", or move the FAB provider to a newer Flask-AppBuilder release.","https:\u002F\u002Fgithub.com\u002Fapache\u002Fairflow\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fupgrade-fab-provider\u002FSKILL.md",[],[132,133,134,135,113,9,136,42,137,138,139,140,141,142,143,144,145,146,28,147,148,149,150],"upgrade fab provider","upgrade","provider","test","apache airflow","security","alignment","reviews","db","bump","static","re","move","new","uv","drift","tripwire","lock","dependency",{"skillNo":152,"name":153,"nameZh":6,"description":154,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":155,"categories":156,"keywords":157,"publishedAt":44},"01M6ACAZ1M5SJQH1SHFFY7CXQF","airflow-translations","Add or update translations for the Apache Airflow UI. Guides through setting up locales, scaffolding translation files, translating with locale-specific guidelines, and validating results. Use when working with i18n tasks in airflow-core\u002Fsrc\u002Fairflow\u002Fui\u002Fpublic\u002Fi18n\u002Flocales\u002F.","https:\u002F\u002Fgithub.com\u002Fapache\u002Fairflow\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fairflow-translations\u002FSKILL.md",[],[113,158,159,160,161,9,136,162,163,164,165,166,167,168,169,170,171,28,172,99],"airflow translations","core","guidelines","tasks","update","translation","scaffolding","up","files","guides","i18n","src","ui","add","locale",{"skillNo":174,"name":175,"nameZh":6,"description":176,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":177,"categories":178,"keywords":179,"publishedAt":44},"01M6ACAWMSPCZCQMCJ0WPVTFQ9","airflow-publish-changes","Prepare or carry out authorized publication of Apache Airflow changes, including remote checks, pre-push verification, PR creation, and tracking deferred work. Also use when planning these steps or managing contribution issues.","https:\u002F\u002Fgithub.com\u002Fapache\u002Fairflow\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fairflow-publish-changes\u002FSKILL.md",[],[113,180,181,182,183,43,184,185,186,9,136,24,187,188,28,189,190,191,100,192,193],"airflow publish changes","publish","changes","push","planning","issues","verification","creation","remote","work","pr creation","Publication","contribution","tracking",{"skillNo":195,"name":196,"nameZh":6,"description":197,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":198,"categories":199,"keywords":200,"publishedAt":44},"01M6ACAT3JW1XZD3Z9EPP4H68M","airflow-pr-draft-summary","Draft Apache Airflow commit messages, PR titles and descriptions, or GitHub comments and reviews. Also use for release-note decisions when preparing a contribution.","https:\u002F\u002Fgithub.com\u002Fapache\u002Fairflow\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fairflow-pr-draft-summary\u002FSKILL.md",[],[43,201,113,202,203,204,205,29,9,206,136,42,139,207,28,208,99,100,209,210,192],"pr draft summary","airflow pr draft summary","pr draft","draft","summary","github","messages","commit messages","decisions","comments"]