diff --git a/.release-please-manifest.json b/.release-please-manifest.json index f1c1e58..bcd0522 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.5.0" + ".": "0.6.0" } diff --git a/CHANGELOG.md b/CHANGELOG.md index f0f8a27..04a8e18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,101 @@ # Changelog +## [0.6.0](https://github.com/magnus919/agent-skills/compare/v0.5.0...v0.6.0) (2026-08-03) + + +### Features + +* add CNCF Landscape technology selection skill ([622f341](https://github.com/magnus919/agent-skills/commit/622f341ea467616174592e036c5d3fa8f121ae03)) +* add comic chat PNG renderer skill ([2494a66](https://github.com/magnus919/agent-skills/commit/2494a663952f32d5807f900804bafc05aa417352)) +* add lightweight test-hardening path to neckbeard ([#230](https://github.com/magnus919/agent-skills/issues/230)) ([36d3fa8](https://github.com/magnus919/agent-skills/commit/36d3fa837e928951aa17bfc7ec4dc772f47becc6)) +* **agent-production-operations:** add agent production operations bundle ([#229](https://github.com/magnus919/agent-skills/issues/229)) ([ac1ad5d](https://github.com/magnus919/agent-skills/commit/ac1ad5dbd905fe8de6ea3298896b56d845f30fc7)), closes [#201](https://github.com/magnus919/agent-skills/issues/201) +* binary-analysis skill with Ghidra backend ([0d4a365](https://github.com/magnus919/agent-skills/commit/0d4a3652e4ccb3c25869f5baca52a29f08ddbf44)) +* **binary-analysis:** implement CLI skeleton with argparse, JSON envelope, and exit codes ([e9397bb](https://github.com/magnus919/agent-skills/commit/e9397bbb542b195e6c9741805bad491a13ff9ef5)) +* **bundles:** define bundle manifests and lifecycle capability matrix ([#203](https://github.com/magnus919/agent-skills/issues/203)) ([#231](https://github.com/magnus919/agent-skills/issues/231)) ([aa893e3](https://github.com/magnus919/agent-skills/commit/aa893e3ec20594da4982f645a31b2675f0abdcf8)) +* **capacity-and-cost-engineering:** add capacity-and-cost-engineering skill ([#199](https://github.com/magnus919/agent-skills/issues/199)) ([#224](https://github.com/magnus919/agent-skills/issues/224)) ([a471888](https://github.com/magnus919/agent-skills/commit/a4718886761af0f90438cfd7c14f2460f4dfff89)) +* **ci:** add linting, formatting, coverage, and security configs ([476d7e1](https://github.com/magnus919/agent-skills/commit/476d7e11b02f2bee65a2368adff02d9366376791)) +* **ci:** add mypy, radon, and deptry to CI pipeline ([bfe05ef](https://github.com/magnus919/agent-skills/commit/bfe05ef18aed5f90e17b2da4b725d1a4d565bfe1)) +* **conditional-customer-success:** add conditional customer success skill ([#192](https://github.com/magnus919/agent-skills/issues/192)) ([#218](https://github.com/magnus919/agent-skills/issues/218)) ([1dd564e](https://github.com/magnus919/agent-skills/commit/1dd564e3d9eb8d73129332658d8b7504beeb0f89)) +* **decompile:** implement decompile command with pseudocode, address map, and diagnostics ([3e4f07a](https://github.com/magnus919/agent-skills/commit/3e4f07a34cf14550802005c111f91eb76aa710b9)) +* **doctor-bootstrap:** implement doctor, bootstrap, and version commands ([55c1451](https://github.com/magnus919/agent-skills/commit/55c145185fd88d51267f5fa776d67207546fd874)) +* **domain-foundation:** implement canonical domain model with entities, enums, schemas, errors, and selectors ([3481663](https://github.com/magnus919/agent-skills/commit/3481663595a097a8cb5b1d9fa87db74024ff7813)) +* **fake-backend:** implement FakeAdapter with controllable backend testing ([6184673](https://github.com/magnus919/agent-skills/commit/6184673f3585da829d5b64feaba1a6154f16ace4)) +* fix remaining agent readiness signals for level 5 ([865a1e9](https://github.com/magnus919/agent-skills/commit/865a1e90c6bb6bdb23c2b182be2fddbf26fd0c17)) +* **fix-coverage-config:** add .coveragerc with placeholder module omissions and exclude plain-text output helpers ([86b248e](https://github.com/magnus919/agent-skills/commit/86b248e89a2425fdff520be48eb6aa042f4c6579)) +* **fix-fake-adapter-env:** add BINARY_FAKE_* env var support to FakeAdapter ([c408259](https://github.com/magnus919/agent-skills/commit/c408259a7269da8528c250961e6f52b0875df66b)) +* **fix-missing-tests:** add register_binary(), clamp warnings in JSON envelope, and new tests ([50e48de](https://github.com/magnus919/agent-skills/commit/50e48deeb7b318b630a6fe4f645e9a34de176246)) +* **function-queries:** implement functions, disassemble, and bytes CLI commands ([3cf347b](https://github.com/magnus919/agent-skills/commit/3cf347b06035f6651585686966d867e43e19a5d7)) +* **ghidra-adapter:** implement GhidraAdapter skeleton with PyGhidra bridge ([9e8234f](https://github.com/magnus919/agent-skills/commit/9e8234f70f8df1d77b6ca6484f6942bc42f4739e)) +* **implementation-planning:** add implementation planning skill ([#217](https://github.com/magnus919/agent-skills/issues/217)) ([f82515e](https://github.com/magnus919/agent-skills/commit/f82515e85d6620e962a93d6341be9d0474b83215)), closes [#186](https://github.com/magnus919/agent-skills/issues/186) +* **import-analyze:** implement binary import, analyze, and metadata commands ([f4e254a](https://github.com/magnus919/agent-skills/commit/f4e254ac6703005fcdb702b63aa64dbaaaa0dfa4)) +* improve agent readiness with dev tooling, CI checks, and tests ([6b44d6f](https://github.com/magnus919/agent-skills/commit/6b44d6f490481de100667d900066024f88deafe3)) +* **incident-learning:** add incident-learning skill ([#226](https://github.com/magnus919/agent-skills/issues/226)) ([4f14ce3](https://github.com/magnus919/agent-skills/commit/4f14ce3df6e447848392dd477de13a9b5531c7d8)), closes [#200](https://github.com/magnus919/agent-skills/issues/200) +* **json-contracts:** implement JSON contract consistency across all commands ([801a5be](https://github.com/magnus919/agent-skills/commit/801a5be65f2478dd8a04677be1c0f7a90093cd75)) +* **migration-engineering:** add migration-engineering skill ([#222](https://github.com/magnus919/agent-skills/issues/222)) ([652521a](https://github.com/magnus919/agent-skills/commit/652521a09e16f222ada2a74ce6a7ed760396dc91)) +* **neckbeard:** issue-to-PR delivery workflow ([#181](https://github.com/magnus919/agent-skills/issues/181)-[#185](https://github.com/magnus919/agent-skills/issues/185)) ([#208](https://github.com/magnus919/agent-skills/issues/208)) ([4a4958b](https://github.com/magnus919/agent-skills/commit/4a4958b6761bcdb4382770e4096b92f5a644fcf7)) +* **privacy-engineering:** add privacy-engineering skill ([#202](https://github.com/magnus919/agent-skills/issues/202)) ([#225](https://github.com/magnus919/agent-skills/issues/225)) ([6f429a9](https://github.com/magnus919/agent-skills/commit/6f429a91b1a41505c512fe529dd10d67f3b141eb)) +* **product-adoption:** add product adoption skill ([#214](https://github.com/magnus919/agent-skills/issues/214)) ([11a6595](https://github.com/magnus919/agent-skills/commit/11a6595d1ca9539fb97e118fdd17fb5026788905)) +* **product-analytics-and-measurement:** add product analytics and measurement skill ([#213](https://github.com/magnus919/agent-skills/issues/213)) ([6b650b7](https://github.com/magnus919/agent-skills/commit/6b650b722c2657a45c2f97630bca9ca72e687fcf)) +* **product-experimentation:** add product experimentation skill ([#212](https://github.com/magnus919/agent-skills/issues/212)) ([0b32a96](https://github.com/magnus919/agent-skills/commit/0b32a96521e72b21fa5a387da271f19365ce8c0f)) +* **product-lifecycle-learning:** add product lifecycle learning skill ([#194](https://github.com/magnus919/agent-skills/issues/194)) ([#219](https://github.com/magnus919/agent-skills/issues/219)) ([2c247a1](https://github.com/magnus919/agent-skills/commit/2c247a17473edce9b41ccd90f59f01e42d22a7ca)) +* **product-lifecycle:** add thin product-lifecycle bundle ([#227](https://github.com/magnus919/agent-skills/issues/227)) ([46b92aa](https://github.com/magnus919/agent-skills/commit/46b92aa489891e45be0bf3afe3d69021394c1737)), closes [#187](https://github.com/magnus919/agent-skills/issues/187) +* **product-operations-and-governance:** add product operations and governance skill ([#216](https://github.com/magnus919/agent-skills/issues/216)) ([7756266](https://github.com/magnus919/agent-skills/commit/77562664b87cb3d556e154600735b5a1eb863a95)), closes [#193](https://github.com/magnus919/agent-skills/issues/193) +* **product-roadmapping-and-portfolio:** add product roadmapping and portfolio skill ([#215](https://github.com/magnus919/agent-skills/issues/215)) ([5d95dd6](https://github.com/magnus919/agent-skills/commit/5d95dd6a303303244af42ca821e998a4454efbd3)), closes [#189](https://github.com/magnus919/agent-skills/issues/189) +* **production-excellence:** add thin production-excellence bundle ([#228](https://github.com/magnus919/agent-skills/issues/228)) ([8c05a07](https://github.com/magnus919/agent-skills/commit/8c05a076bb1a29700bdadc246f2bf7a93faff34c)) +* **production-readiness:** add production-readiness skill ([#221](https://github.com/magnus919/agent-skills/issues/221)) ([2183c22](https://github.com/magnus919/agent-skills/commit/2183c221352a939636c278b54e10087b6fe31aa9)) +* **project-lifecycle:** implement all project lifecycle commands and state machine ([756b50e](https://github.com/magnus919/agent-skills/commit/756b50e285cfa038de4731aeb6204b737885804a)) +* **project-system:** implement workspace, manifest, locking, and cache ([defe46c](https://github.com/magnus919/agent-skills/commit/defe46c1386d4c518318e95ba557a2ad96f74a33)) +* **qa-methodology:** add 2 stdlib-only CLI scripts and colocated pytest suite ([974fde0](https://github.com/magnus919/agent-skills/commit/974fde017cf6760d491caec7f4c0c79bb1b54b04)) +* **qa-methodology:** add 3 new traditional QA references ([ff69b09](https://github.com/magnus919/agent-skills/commit/ff69b0982f036543392352c635ecbefff78893da)) +* **qa-methodology:** add 5 fillable templates and 3 assets ([3325787](https://github.com/magnus919/agent-skills/commit/332578766db926695a31adc24060aa6d0665abe8)) +* **qa-methodology:** add modern agentic references (ai-code-quality-gates, agentic-eval-design) ([aa8c027](https://github.com/magnus919/agent-skills/commit/aa8c027a6e2d0ed8fba3af93f3bd7ce6dfe1e3de)) +* **qa-methodology:** add modern QA career levels and SDET engineering references ([9b10ff5](https://github.com/magnus919/agent-skills/commit/9b10ff546ae968b6df9909ae6f7192c148543d33)) +* **qa-methodology:** add mutation-guided test hardening evidence workflow ([8bd042e](https://github.com/magnus919/agent-skills/commit/8bd042eae5a851419c3f6324e4709eb894a8e36f)), closes [#209](https://github.com/magnus919/agent-skills/issues/209) +* **qa-methodology:** add schema-v1 evals with 7 output-quality cases ([107d6d1](https://github.com/magnus919/agent-skills/commit/107d6d11631f70e957b16605ca0bdf4a098e833d)) +* **qa-methodology:** rebuild as thin-index QA/SDET skill bundle ([726de89](https://github.com/magnus919/agent-skills/commit/726de898e8d8b81b9cfb2a13902f38d0cf8c63a0)) +* **qa-methodology:** rebuild SKILL.md as thin index and rewrite README.md ([8b0220b](https://github.com/magnus919/agent-skills/commit/8b0220b75b62907e9c954cc7dc60584ba077b616)) +* **qa-methodology:** refresh 5 traditional QA support references ([5bad13a](https://github.com/magnus919/agent-skills/commit/5bad13af568567529071f65d4cd756ac8492a1c8)) +* **qa-methodology:** refresh traditional QA core references and split monolithic file ([966edcf](https://github.com/magnus919/agent-skills/commit/966edcf0c75e2eb64f4320e1f455d64776928ba5)) +* **reference-queries:** implement xrefs, callers, callees, and callgraph CLI commands ([93b0431](https://github.com/magnus919/agent-skills/commit/93b04315a9cdfe987d7390aff947aae5df59dd4d)) +* **release-engineering:** add release engineering skill bundle ([818d8ca](https://github.com/magnus919/agent-skills/commit/818d8cafa2b435b1556fdafa8603aa70f39cf01d)) +* **reporting:** implement export-report and audit commands ([010a308](https://github.com/magnus919/agent-skills/commit/010a3086aa373afc82edb4a0947ee8cfee16be4a)) +* **resilience-and-recovery:** add resilience-and-recovery skill ([#223](https://github.com/magnus919/agent-skills/issues/223)) ([c032580](https://github.com/magnus919/agent-skills/commit/c032580197f2f370d4f96895fafc09b36c21f4dd)) +* **safety-hardening:** implement path security, output limits, memory limits, and report containment ([e62cf47](https://github.com/magnus919/agent-skills/commit/e62cf47a9b2b01905bfbf0864be7fe13365c7908)) +* **search-trace:** implement search and trace commands with cross-area integration flows ([1ead735](https://github.com/magnus919/agent-skills/commit/1ead735513a00bfb3b8dc5a93a82cfb074e30072)) +* **security-rules:** implement suspicious-apis and capability-map commands ([d005b9a](https://github.com/magnus919/agent-skills/commit/d005b9aadaa2e7404d4718a7a55877a40c000f20)) +* **skill-content:** write SKILL.md and README.md for binary-analysis skill ([72d8462](https://github.com/magnus919/agent-skills/commit/72d8462f073cb20cdeb8dc6b48c08c9515bfc431)) +* **skill-evals:** write 6 eval cases for binary-analysis skill ([f79124d](https://github.com/magnus919/agent-skills/commit/f79124d360ae4212816bf595a7ccff9425252a90)) +* **skill-references:** write 11 reference files for binary-analysis skill ([a799e0f](https://github.com/magnus919/agent-skills/commit/a799e0f7eb46d48867f0ca9cff5b94c0b22d21f7)) +* **structural-queries:** implement sections, entrypoints, imports, exports, symbols, and strings CLI commands ([7df699b](https://github.com/magnus919/agent-skills/commit/7df699ba20d94d09479104caafbad403962269db)) +* **triage-diagnostics:** implement triage and diagnostics commands ([0e05960](https://github.com/magnus919/agent-skills/commit/0e05960dd6c53ffa2b07b929469ffa5a22c61ec8)) +* **worker:** implement optional local worker with start/stop/status commands and one-shot fallback ([5e8f1d3](https://github.com/magnus919/agent-skills/commit/5e8f1d3aafbb5f06a2aa7898cd16a4d390db7012)) + + +### Bug Fixes + +* **ci:** add binary-analysis to deptry extend_exclude ([8ea4053](https://github.com/magnus919/agent-skills/commit/8ea4053e01826008582b9e942fdecae7c3932361)) +* **ci:** add sys.path setup in test __init__.py for unittest discover ([4c7f5c4](https://github.com/magnus919/agent-skills/commit/4c7f5c42f49cdc9eba4c537a47f6fe2352b20ded)) +* **ci:** add sys.path setup to all test sub-package __init__.py files ([f9354df](https://github.com/magnus919/agent-skills/commit/f9354df2870a97fe85d15e66ad1df1de94afc3f7)) +* **ci:** add sys.path setup to individual binary-analysis test files ([736859e](https://github.com/magnus919/agent-skills/commit/736859eafa9ab7c40d0291479a6d87f4a6895055)) +* **ci:** prevent sys.modules cross-contamination between test directories in check-artifacts ([96f0bd4](https://github.com/magnus919/agent-skills/commit/96f0bd4e46c8023cd5035aba2299df70562c2837)) +* **ci:** use full path for deptry binary-analysis exclude ([b56183b](https://github.com/magnus919/agent-skills/commit/b56183bbabed58089bf79e3d571433e8fe6dd217)) +* permit removal of eval-covered skills ([687d7d7](https://github.com/magnus919/agent-skills/commit/687d7d7c33510b7496c50052161596f34ad58eae)) +* **product-strategy:** correct stale RICE reference ([#211](https://github.com/magnus919/agent-skills/issues/211)) ([8d30f22](https://github.com/magnus919/agent-skills/commit/8d30f22cb89508b2982c36b8a627a79944f5f2b6)) +* **release-engineering:** address review findings in DORA asset, cadence, link, metrics script ([2b90656](https://github.com/magnus919/agent-skills/commit/2b906565d443bf7d460cfe1a8f5d5c258844de51)) +* **release-engineering:** align 0.x version bumps with Release Please's minor-feature policy +* **release-engineering:** validate Keep a Changelog and Release Please formats +* relocate binary analysis skill ([6387f7f](https://github.com/magnus919/agent-skills/commit/6387f7f4752a2dd8fd7a92a7e640ec6b62c11698)) +* require dated commitments for CoS tasks ([857dfc6](https://github.com/magnus919/agent-skills/commit/857dfc6e1cecccf94ec241a89be20bca3a539fb3)) +* **security-ship:** ensure diagnostics have both recoverable values and add pagination cursors to security commands ([d04af37](https://github.com/magnus919/agent-skills/commit/d04af37602eae8304e4c089076c047a0eae8fb9c)) +* **security-ship:** fix --limit routing, truncation warnings, and clean confirmation order ([7b0109b](https://github.com/magnus919/agent-skills/commit/7b0109b328c097e6c40c1ff07ac3ce4c01e98aa2)) +* **security-ship:** fix three scrutiny blocking issues ([a20adde](https://github.com/magnus919/agent-skills/commit/a20adde298209480b49279aca84a5b6e3c54457e)) +* state CoS task-capture boundary ([f285316](https://github.com/magnus919/agent-skills/commit/f285316206258471343c66ea9bfaf15f07f3bfbc)) + + +### Reverts + +* restore clean base64 import in helpers.py ([746df95](https://github.com/magnus919/agent-skills/commit/746df95150e7ec092c2d37daa8261f4c3d12004a)) + ## [0.5.0](https://github.com/magnus919/agent-skills/compare/v0.4.0...v0.5.0) (2026-07-27) diff --git a/release-engineering/README.md b/release-engineering/README.md index a0757b2..8ed89df 100644 --- a/release-engineering/README.md +++ b/release-engineering/README.md @@ -15,7 +15,7 @@ It also covers the parts of release work that quietly break teams: versioning an | `references/` | 15 dense topic files: role-and-career, skills-competency-model, release-process-models, cd-and-pipeline-stages, progressive-delivery, change-governance-and-compliance, readiness-and-quality-gates, rollback-and-recovery, versioning-and-artifacts, feature-flag-lifecycle, monorepo-polyrepo-release, toolchain-landscape, supply-chain-security, metrics-and-dora, release-operations-and-triage | | `templates/` | 6 fillable templates: release-plan, release-readiness-checklist, rollback-runbook, release-notes, change-governance-record, hotfix-emergency-release-plan | | `assets/` | 4 quick-reference files: dora-metrics-reference, versioning-decision-table, deployment-strategy-matrix, release-toolchain-cheatsheet | -| `scripts/` | 5 Python CLIs: version_bump (next-SemVer from conventional commits), semver_check (validate/compare/sort), changelog_check (Keep a Changelog validator), dora_metrics (five-metric computation), release_plan_scaffold (plan generator) | +| `scripts/` | 5 Python CLIs: version_bump (next-SemVer from conventional commits, including the documented 0.x policy), semver_check (validate/compare/sort), changelog_check (Keep a Changelog and Release Please validator), dora_metrics (five-metric computation), release_plan_scaffold (plan generator) | | `evals/` | Schema-v1 output-quality eval manifest (8 cases) | ## Quick Start @@ -30,6 +30,8 @@ Validate a changelog before it ships: ```bash python3 release-engineering/scripts/changelog_check.py CHANGELOG.md +# Or select Release Please's linked-header format explicitly: +python3 release-engineering/scripts/changelog_check.py CHANGELOG.md --format release-please ``` Compute the five DORA metrics from deployment and commit event data: diff --git a/release-engineering/SKILL.md b/release-engineering/SKILL.md index 73c8227..a535ed6 100644 --- a/release-engineering/SKILL.md +++ b/release-engineering/SKILL.md @@ -93,7 +93,7 @@ Senior-to-principal release engineering methodology: designing and operating the | `assets/release-toolchain-cheatsheet.md` | Quick tool lookup by category — one-liner and when-to-pick per tool | | `scripts/version_bump.py` | Computing the next SemVer from conventional commits or a git range | | `scripts/semver_check.py` | Validating, comparing, or sorting strict SemVer versions | -| `scripts/changelog_check.py` | Validating a CHANGELOG.md against Keep a Changelog | +| `scripts/changelog_check.py` | Validating Keep a Changelog or Release Please CHANGELOG.md files | | `scripts/dora_metrics.py` | Computing the five DORA metrics from deployment and commit event data | | `scripts/release_plan_scaffold.py` | Scaffolding a release plan document from flags or a git range | | `evals/evals.json` | Running output-quality evals for this skill (schema v1, 8 cases) | @@ -107,9 +107,9 @@ Senior-to-principal release engineering methodology: designing and operating the | Script | Invocation | Purpose | |--------|-----------|---------| -| version_bump | `python3 scripts/version_bump.py --current-version 1.4.0 --git-range v1.4.0..HEAD` | Computes the next SemVer from Conventional Commits — breaking → major, feat → minor, fix → patch; optional prerelease tag | +| version_bump | `python3 scripts/version_bump.py --current-version 1.4.0 --git-range v1.4.0..HEAD` | Computes the next SemVer from Conventional Commits — breaking → major (minor in 0.x), feat → minor, fix → patch; optional prerelease tag | | semver_check | `python3 scripts/semver_check.py --check 1.2.3-beta.1` | Validates strict SemVer 2.0.0, compares two versions, or sorts a list | -| changelog_check | `python3 scripts/changelog_check.py CHANGELOG.md` | Validates a changelog against Keep a Changelog — headers, dates, change types, link references | +| changelog_check | `python3 scripts/changelog_check.py CHANGELOG.md [--format auto|keep-a-changelog|release-please]` | Validates supported changelog headers, dates, sections, bullets, and links; reports selected/detected format | | dora_metrics | `python3 scripts/dora_metrics.py --events events.json` | Computes all five DORA metrics from deployment and commit event data | | release_plan_scaffold | `python3 scripts/release_plan_scaffold.py --version 2.0.0 --owner alice --output plan.md` | Scaffolds a release plan document with filled placeholders from flags or a git range | diff --git a/release-engineering/assets/versioning-decision-table.md b/release-engineering/assets/versioning-decision-table.md index 0e2b5f2..bf9efa5 100644 --- a/release-engineering/assets/versioning-decision-table.md +++ b/release-engineering/assets/versioning-decision-table.md @@ -36,9 +36,14 @@ | `BREAKING CHANGE:` footer, or `feat!` / `fix!` | MAJOR | `feat!: drop v1 API` | | `feat` | MINOR | `feat(auth): add refresh tokens` | | `fix` | PATCH | `fix(api): retry on 429` | -| `perf`, `refactor`, `docs`, `test`, `chore`, `ci`, `build`, `style` | No bump (or PATCH, if you choose) | `docs: update readme` | +| `perf`, `refactor`, `docs`, `test`, `chore`, `ci`, `build`, `style` | PATCH in this skill | `docs: update readme` | -> **Gotcha —** the bump is decided by the highest-priority type in the release range: one `BREAKING CHANGE` forces a major even if the rest are fixes. Automate with the `version_bump.py` script. +For `0.y.z`, this skill's Release Please-compatible policy maps both +`feat` and breaking changes to MINOR (`0.5.0` -> `0.6.0`), while fixes and +other changes remain PATCH. At `1.0.0` and later, normal SemVer priority +applies. + +> **Gotcha —** the bump is decided by the highest-priority type in the release range: one `BREAKING CHANGE` forces a MAJOR at 1.0.0+, or a MINOR bump in 0.x, even if the rest are fixes. Automate with the `version_bump.py` script. ## Prerelease and Build Metadata Rules diff --git a/release-engineering/references/versioning-and-artifacts.md b/release-engineering/references/versioning-and-artifacts.md index b0b4c14..a1295de 100644 --- a/release-engineering/references/versioning-and-artifacts.md +++ b/release-engineering/references/versioning-and-artifacts.md @@ -42,7 +42,7 @@ Two rules that commonly surprise: numeric identifiers compare **numerically** (` `0.y.z` means **initial development**: the spec explicitly states that "anything may change at any time" and the public API should not be considered stable. Practical consequences: - `0.1.0` → `0.2.0` usually signals breaking changes in pre-1.0 libraries — the `1.0.0` MAJOR convention is effectively deferred. -- Many tools map `feat` commits to MINOR even in 0.x, which can silently bump `0.3.0` → `0.4.0` for an additive change. Decide and document your 0.x policy; a common choice is "breaking changes bump MINOR until 1.0." +- This skill's default Release Please-compatible policy maps `feat` and breaking commits to MINOR in 0.x (`0.5.0` → `0.6.0`); fixes and other commit types remain PATCH (`0.5.0` → `0.5.1`). This keeps pre-1.0 release lines meaningful while preserving normal SemVer behavior at 1.0+. - Consumers pinning `0.x` with caret ranges (`^0.3.1`) get **no automatic updates** in most package managers (npm, for example, treats caret on `0.x` as `>=0.3.1 <0.4.0`), which is exactly the behavior you want for a pre-stable API. Declare a policy so both humans and automation agree on what a `0.x` bump means: @@ -50,7 +50,7 @@ Declare a policy so both humans and automation agree on what a `0.x` bump means: | Policy | Rule | Consequence | |--------|------|-------------| | **Strict** | Breaking changes bump MAJOR even in 0.x | Version jumps `0.3.0` → `1.0.0` early; signals commitment before the API is ready | -| **Deferred** (most common) | Breaking changes bump MINOR until 1.0 | `0.3.0` → `0.4.0`; 1.0 arrives when the API stabilizes | +| **Deferred** (skill default) | Features and breaking changes bump MINOR until 1.0; fixes and other changes bump PATCH | `0.5.0` → `0.6.0` for `feat`, `0.5.0` → `0.5.1` for `fix`; 1.0 arrives when the API stabilizes | | **Tooling-default** | Whatever your release tool computes from commits | Usually `feat`→MINOR; document that pre-1.0 MINOR may break | > **Gotcha — The 0.x trap:** A 1.0.0 release is a promise about API stability. If your library has public consumers, treat 1.0.0 as a deliberate commitment — and conversely, do not stay in `0.x` forever because bumping to 1.0 feels risky; consumers already treat `0.x` as unstable either way. @@ -138,7 +138,7 @@ Consistency matters more than the exact mapping: if `perf` bumps PATCH in one re Automated bump tooling reads this history: **semantic-release** analyzes commits and performs version + changelog + publish with no human gate; **release-please** generates a release PR (version bump + changelog) that a human merges; **git-cliff** generates the changelog from commits without publishing. For monorepo specifics (per-package vs combined releases), see [monorepo-polyrepo-release.md](./monorepo-polyrepo-release.md). -## Changelogs (Keep a Changelog) +## Changelogs (Keep a Changelog and Release Please) [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) is the de-facto human changelog format, designed for **people, not machines** (machines read commits). Core conventions: @@ -169,7 +169,7 @@ A minimal conforming structure: [2.1.0]: https://github.com/acme/app/compare/v2.0.0...v2.1.0 ``` -The `scripts/changelog_check.py` in this skill validates this shape: required `# Changelog` title, `[Unreleased]` section, strict-SemVer date-stamped version headers, allowed change types, and consistent reference links. +The `scripts/changelog_check.py` in this skill validates this shape with `--format keep-a-changelog` (or safe `--format auto` detection). It also validates Release Please output with linked dated headers such as `## [0.6.0](https://github.com/example/proj/compare/v0.5.0...v0.6.0) (2026-08-03)`, conventional `###` sections such as `Features`, `Bug Fixes`, and `Reverts`, and `*` bullets with inline links. Release Please section labels are configurable. Release Please files do not use an `Unreleased` section; select that format explicitly in CI when auto-detection is not appropriate. ## Artifact Immutability and Promotion diff --git a/release-engineering/scripts/changelog_check.py b/release-engineering/scripts/changelog_check.py index 3a0c51b..b5797ea 100644 --- a/release-engineering/scripts/changelog_check.py +++ b/release-engineering/scripts/changelog_check.py @@ -1,9 +1,21 @@ #!/usr/bin/env python3 -"""Validate a CHANGELOG.md against the Keep a Changelog conventions. +"""Validate Keep a Changelog or Release Please CHANGELOG.md files. Checks performed: - - The first non-empty line is the `# Changelog` title. - - A `## [Unreleased]` section exists. +Keep a Changelog validation requires the first non-empty line to be the +`# Changelog` title, an `## [Unreleased]` section, dated version headers, +allowed change types, and reference links. Release Please validation accepts +linked dated headers such as `## [1.2.0](https://...) (2026-08-03)`, conventional +changelog sections such as `Features`, `Bug Fixes`, and `Reverts`, and star +bullets; it rejects an `Unreleased` section. Release Please section labels are +configurable, so the validator checks that non-empty `###` headings contain the +bullets. + +With `--format auto` (the default), a valid Release Please header selects the +Release Please validator; otherwise the strict Keep a Changelog validator is +used. Explicit `--format` selection is available for CI gates. + +Keep a Changelog checks: - Version headers use the form `## [X.Y.Z] - YYYY-MM-DD` with a strict SemVer version (pre-releases allowed) and a valid ISO-8601 date (YYYY-MM-DD or YYYY-MM); the optional `[YANKED]` marker is allowed. @@ -18,7 +30,7 @@ Checks performed: Each problem is reported with its line number. Exit 0 when the file is clean, exit 1 when any problem is found. -Arguments: [changelog.md] (default: CHANGELOG.md), --json. +Arguments: [changelog.md] (default: CHANGELOG.md), --format, --json. Exit codes: 0 changelog is valid @@ -37,10 +49,15 @@ UNRELEASED_HEADER_RE = re.compile(r"^##\s+\[Unreleased\](\s+\[YANKED\])?\s*$") VERSION_HEADER_RE = re.compile( r"^##\s+\[([^\]]+)\]\s*-\s*(\d{4}-\d{2}-\d{2}|\d{4}-\d{2})(\s+\[YANKED\])?\s*$" ) +RELEASE_PLEASE_HEADER_RE = re.compile( + r"^##\s+\[([^\]]+)\]\(([^)]+)\)\s+\((\d{4}-\d{2}-\d{2})\)\s*$" +) BULLET_RE = re.compile(r"^-\s+(\S+)") +STAR_BULLET_RE = re.compile(r"^\*\s+(\S+)") LINK_REF_RE = re.compile(r"^\[([^\]]+)\]:\s+(\S+)") CHANGE_TYPES = ("Added", "Changed", "Deprecated", "Removed", "Fixed", "Security") +FORMATS = ("auto", "keep-a-changelog", "release-please") SEMVER_RE = re.compile( r"^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)" @@ -55,10 +72,8 @@ def parse_args(argv=None): parser = argparse.ArgumentParser( prog="changelog_check.py", description=( - "Validate a CHANGELOG.md against the Keep a Changelog " - "conventions: title, [Unreleased] section, strict SemVer version " - "headers with valid dates, allowed change-type bullets, and " - "consistent reference links." + "Validate a CHANGELOG.md as Keep a Changelog or Release Please; " + "auto-detect the format unless explicitly selected." ), epilog=( "Exit codes: 0 valid, 1 problems found / file unreadable, " @@ -66,6 +81,7 @@ def parse_args(argv=None): "Examples:\n" " changelog_check.py\n" " changelog_check.py CHANGELOG.md\n" + " changelog_check.py CHANGELOG.md --format release-please\n" " changelog_check.py CHANGELOG.md --json\n" ), ) @@ -76,6 +92,16 @@ def parse_args(argv=None): metavar="CHANGELOG.md", help="Path to the changelog file (default: CHANGELOG.md).", ) + parser.add_argument( + "--format", + choices=FORMATS, + default="auto", + dest="format", + help=( + "Changelog format: auto (default), keep-a-changelog, or " + "release-please." + ), + ) parser.add_argument( "--json", action="store_true", @@ -107,7 +133,15 @@ def valid_date(text): return False -def check_changelog(text): +def detect_format(text): + """Detect Release Please only from its unambiguous linked header shape.""" + for line in text.splitlines(): + if RELEASE_PLEASE_HEADER_RE.match(line.rstrip()): + return "release-please" + return "keep-a-changelog" + + +def check_keep_a_changelog(text): """Validate changelog text; returns (valid, problems). problems is a list of {"line": int, "message": str} dicts. @@ -252,6 +286,104 @@ def check_changelog(text): return len(problems) == 0, problems +def check_release_please(text): + """Validate the Release Please changelog format.""" + problems = [] + lines = text.splitlines() + non_empty = [i for i, line in enumerate(lines) if line.strip()] + if not non_empty: + return False, [{"line": 1, "message": "file is empty"}] + + if not TITLE_RE.match(lines[non_empty[0]].strip()): + problems.append( + { + "line": non_empty[0] + 1, + "message": "expected '# Changelog' title as the first non-empty line", + } + ) + + in_section = False + subsection = None + release_count = 0 + for idx, raw in enumerate(lines): + line = raw.rstrip() + stripped = line.strip() + lineno = idx + 1 + + if line.startswith("## "): + in_section = False + subsection = None + if stripped == "## [Unreleased]" or stripped.startswith("## [Unreleased]"): + problems.append( + {"line": lineno, "message": "Release Please files must not contain an '## [Unreleased]' section"} + ) + continue + match = RELEASE_PLEASE_HEADER_RE.match(stripped) + if not match: + problems.append( + { + "line": lineno, + "message": "malformed Release Please version header (expected '## [X.Y.Z]() (YYYY-MM-DD)')", + } + ) + continue + version, url, date_text = match.groups() + release_count += 1 + in_section = True + if not valid_semver(version): + problems.append( + {"line": lineno, "message": "version '{}' is not strict SemVer".format(version)} + ) + if not url.startswith(("https://", "http://")): + problems.append( + {"line": lineno, "message": "Release Please header link must be an http(s) URL"} + ) + if not valid_date(date_text): + problems.append( + {"line": lineno, "message": "invalid release date '{}' in header".format(date_text)} + ) + continue + + if not in_section: + continue + if line.startswith("### "): + subsection = stripped[len("### "):].strip() + if not subsection: + problems.append( + { + "line": lineno, + "message": "Release Please subsection headings must not be empty", + } + ) + continue + if stripped.startswith("*"): + if not STAR_BULLET_RE.match(stripped): + problems.append( + {"line": lineno, "message": "malformed Release Please bullet (expected '* ')"} + ) + elif subsection is None: + problems.append( + {"line": lineno, "message": "Release Please bullets must be under a subsection heading"} + ) + continue + if stripped.startswith("-"): + problems.append( + {"line": lineno, "message": "Release Please bullets must use '*' rather than '-'"} + ) + + if release_count == 0: + problems.append({"line": 1, "message": "missing Release Please version section"}) + return len(problems) == 0, problems + + +def check_changelog(text, format="auto"): + """Validate text using an explicit format or safe auto-detection.""" + selected = detect_format(text) if format == "auto" else format + if selected == "release-please": + return check_release_please(text) + return check_keep_a_changelog(text) + + def main(argv=None): """Entry point.""" args = parse_args(argv) @@ -262,13 +394,17 @@ def main(argv=None): print("error: cannot read '{}': {}".format(args.changelog, exc), file=sys.stderr) return 1 - valid, problems = check_changelog(text) + detected_format = detect_format(text) + selected_format = detected_format if args.format == "auto" else args.format + valid, problems = check_changelog(text, selected_format) if args.json_output: print( json.dumps( { "file": args.changelog, + "format": selected_format, + "detected_format": detected_format, "valid": valid, "problem_count": len(problems), "problems": problems, @@ -278,7 +414,9 @@ def main(argv=None): ) else: if not problems: - print("valid: {} conforms to Keep a Changelog".format(args.changelog)) + print("valid: {} conforms to {} (detected: {})".format( + args.changelog, selected_format, detected_format + )) else: for problem in problems: print( diff --git a/release-engineering/scripts/version_bump.py b/release-engineering/scripts/version_bump.py index 20e13ad..51456fd 100644 --- a/release-engineering/scripts/version_bump.py +++ b/release-engineering/scripts/version_bump.py @@ -11,8 +11,9 @@ Bump rules (Conventional Commits 1.0.0): - BREAKING CHANGE footer, or a `!` after the type/scope -> MAJOR - feat -> MINOR - fix and all other types -> PATCH - - 0.x versions (initial development): MAJOR bumps become MINOR, - MINOR bumps become PATCH (the public API is not yet stable). + - 0.x versions (initial development): MAJOR and MINOR bumps both become + MINOR under the documented Release Please-compatible policy, while + PATCH bumps remain PATCH. Pre-release handling (--pre-release alpha|beta|rc): - If the current version is already a pre-release with the SAME tag @@ -194,7 +195,7 @@ def bump_core(current, level): return (major + 1, 0, 0) if level == "minor": if major == 0: - return (major, minor, patch + 1) + return (major, minor + 1, 0) return (major, minor + 1, 0) return (major, minor, patch + 1) diff --git a/release-engineering/tests/test_changelog_check.py b/release-engineering/tests/test_changelog_check.py index 3ea8cfa..1609d88 100644 --- a/release-engineering/tests/test_changelog_check.py +++ b/release-engineering/tests/test_changelog_check.py @@ -1,7 +1,7 @@ """Tests for changelog_check.py. -Covers: Keep a Changelog validation (title, [Unreleased], version headers, -dates, change types, reference links), --json output, and exit codes. +Covers: Keep a Changelog and Release Please validation, format selection, +--json output, and exit codes. Discoverable by both pytest and unittest (unittest.TestCase classes). """ @@ -38,6 +38,21 @@ All notable changes to this project will be documented in this file. [1.1.0]: https://github.com/example/proj/releases/tag/v1.1.0 """ +VALID_RELEASE_PLEASE = """\ +# Changelog + +## [0.6.0](https://github.com/magnus919/agent-skills/compare/v0.5.0...v0.6.0) (2026-08-03) + +### Features +* add a feature ([abc123](https://github.com/example/proj/commit/abc123)) + +### Bug Fixes +* fix a bug ([def456](https://github.com/example/proj/commit/def456)) + +### Reverts +* revert an earlier change ([fedcba](https://github.com/example/proj/commit/fedcba)) +""" + def run_changelog(args, cwd=None): """Run changelog_check.py with given args, return (returncode, stdout, stderr).""" @@ -144,6 +159,36 @@ class TestChangelogCheckValid(unittest.TestCase): finally: os.unlink(path) + def test_release_please_format_valid(self): + """Release Please's dated linked headers and star bullets pass.""" + path = write_changelog(VALID_RELEASE_PLEASE) + try: + rc, out, _ = run_changelog([path]) + self.assertEqual(rc, 0) + self.assertIn("release-please", out.lower()) + finally: + os.unlink(path) + + def test_release_please_explicit_format_valid(self): + """The Release Please format can be selected explicitly.""" + path = write_changelog(VALID_RELEASE_PLEASE) + try: + rc, out, _ = run_changelog([path, "--format", "release-please"]) + self.assertEqual(rc, 0) + self.assertIn("release-please", out.lower()) + finally: + os.unlink(path) + + def test_release_please_custom_section_valid(self): + """Custom Release Please section names remain valid.""" + content = VALID_RELEASE_PLEASE.replace("### Features", "### Documentation") + path = write_changelog(content) + try: + rc, _, _ = run_changelog([path, "--format", "release-please"]) + self.assertEqual(rc, 0) + finally: + os.unlink(path) + class TestChangelogCheckProblems(unittest.TestCase): """Each Keep a Changelog violation is reported with exit 1.""" @@ -256,6 +301,32 @@ class TestChangelogCheckProblems(unittest.TestCase): finally: os.unlink(path) + def test_malformed_release_please_header_exit_1(self): + """A Release Please header without its compare link is invalid.""" + content = VALID_RELEASE_PLEASE.replace( + "## [0.6.0](https://github.com/magnus919/agent-skills/compare/v0.5.0...v0.6.0) (2026-08-03)", + "## [0.6.0] (2026-08-03)", + ) + path = write_changelog(content) + try: + rc, out, _ = run_changelog([path, "--format", "release-please"]) + self.assertEqual(rc, 1) + self.assertIn("header", out.lower()) + finally: + os.unlink(path) + + def test_release_please_rejects_unreleased_section(self): + """Release Please files must not contain Keep a Changelog Unreleased.""" + path = write_changelog(VALID_RELEASE_PLEASE.replace( + "# Changelog\n", "# Changelog\n\n## [Unreleased]\n" + )) + try: + rc, out, _ = run_changelog([path, "--format", "release-please"]) + self.assertEqual(rc, 1) + self.assertIn("unreleased", out.lower()) + finally: + os.unlink(path) + class TestChangelogCheckExitCodes(unittest.TestCase): """Exit codes for missing files and usage errors.""" @@ -300,6 +371,29 @@ class TestChangelogCheckJsonOutput(unittest.TestCase): self.assertTrue(data["valid"]) self.assertEqual(data["problem_count"], 0) self.assertEqual(data["problems"], []) + self.assertEqual(data["format"], "keep-a-changelog") + finally: + os.unlink(path) + + def test_json_release_please_reports_format(self): + """JSON reports the selected and detected Release Please format.""" + path = write_changelog(VALID_RELEASE_PLEASE) + try: + rc, out, _ = run_changelog([path, "--json"]) + self.assertEqual(rc, 0) + data = json.loads(out) + self.assertEqual(data["format"], "release-please") + self.assertEqual(data["detected_format"], "release-please") + finally: + os.unlink(path) + + def test_explicit_keep_format_rejects_release_please(self): + """Explicit Keep a Changelog validation remains strict.""" + path = write_changelog(VALID_RELEASE_PLEASE) + try: + rc, out, _ = run_changelog([path, "--format", "keep-a-changelog"]) + self.assertEqual(rc, 1) + self.assertIn("unreleased", out.lower()) finally: os.unlink(path) diff --git a/release-engineering/tests/test_version_bump.py b/release-engineering/tests/test_version_bump.py index 1a5d6b2..2020a13 100644 --- a/release-engineering/tests/test_version_bump.py +++ b/release-engineering/tests/test_version_bump.py @@ -114,15 +114,15 @@ class TestVersionBumpBumpRules(unittest.TestCase): finally: os.unlink(commits) - def test_zero_major_feat_bumps_patch(self): - """On a 0.x version, feat bumps patch.""" + def test_zero_major_feat_bumps_minor(self): + """On a 0.x version, feat follows Release Please and bumps minor.""" commits = write_temp_file("feat: add helper\n") try: rc, out, _ = run_version_bump( - ["--current-version", "0.2.0", "--commits-file", commits] + ["--current-version", "0.5.0", "--commits-file", commits] ) self.assertEqual(rc, 0) - self.assertIn("0.2.1", out) + self.assertIn("0.6.0", out) finally: os.unlink(commits)