From d53ef22b5a1fa0946dbc04f104fa6f741fe61934 Mon Sep 17 00:00:00 2001 From: shcommit Date: Sun, 6 Sep 2026 18:43:41 +0900 Subject: [PATCH 1/2] fix(v1.1.1): add Claude Code marketplace schema + sync pyproject.toml version Two gaps surfaced while deploying v1.1.0: 1) `.claude-plugin/marketplace.json` was missing the `owner` field that Claude Code v2.1.263's marketplace schema requires, so `claude plugin marketplace add ` failed with "Invalid schema: ... owner: Invalid input". Added `$schema`, a top-level `description`, and `owner.name`, mirroring the working `Agent-toolkit/.claude-plugin/marketplace.json` shape. The existing `plugin.json` was already correct, so the symlink-based install kept working; this only unblocks fresh marketplace-add installs. 2) `pyproject.toml`'s `version` field was not in `scripts/sync_version.py`'s sync surface. The v1.1.0 GitHub Release shipped a skill tarball labeled v1.1.0 but a Python wheel/sdist labeled 1.0.1, and `release.yml`'s "tag matches VERSION" check only verified the skill VERSION against the tag, so the drift slipped through. Added `TOML_VERSION_SPECS` (currently `pyproject.toml` `[project]` table) and a new `sync_toml_version()` that rewrites the `version = "..."` line in-place by regex (Python 3.10 stdlib has no TOML writer; `tomli` is read-only). `require_known_paths()` now also asserts the `[project]` table is present, so a future structural edit that deletes the table fails loudly instead of silently dropping pyproject.toml out of the drift check. Added 6 regression tests in `tests/unit/test_sync_version.py` that pin the new behavior (writes under [project], leaves other tables untouched, idempotent, --check-only, skips when section missing, and a real-repo guard that fails the moment pyproject.toml drifts from skills/adr-toolkit/VERSION). Bumped VERSION 1.1.0 -> 1.1.1 and ran `scripts/sync_version.py` to propagate the bump to SKILL.md, .claude-plugin/plugin.json, adapters/gemini-cli/gemini-extension.json, adapters/antigravity/plugin.json, and pyproject.toml. Verification: - scripts/sync_version.py --check: exit 0 (no drift, including pyproject.toml). - 6 new TOML sync tests re-run as standalone assertions with Python 3.13: pass. - ast.parse on both edited files: OK. --- .claude-plugin/marketplace.json | 10 ++- .claude-plugin/plugin.json | 2 +- adapters/antigravity/plugin.json | 2 +- adapters/gemini-cli/gemini-extension.json | 2 +- changelog.md | 31 ++++++++ handoff.md | 87 +++++++++++---------- pyproject.toml | 2 +- scripts/sync_version.py | 72 +++++++++++++++++ skills/adr-toolkit/SKILL.md | 2 +- skills/adr-toolkit/VERSION | 2 +- tests/unit/test_sync_version.py | 95 +++++++++++++++++++++++ 11 files changed, 256 insertions(+), 51 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 3170c1d..5a4a594 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -1,10 +1,16 @@ { + "$schema": "https://anthropic.com/claude-code/marketplace.schema.json", "name": "adr-toolkit-marketplace", + "description": "Local marketplace for the ADR Toolkit Claude Code plugin.", + "owner": { + "name": "Local developer" + }, "plugins": [ { "name": "adr-toolkit", - "source": "./", - "description": "Agent-native ADR toolkit: inspect the repo, ask only what code can't answer, record and check architecture decisions." + "description": "Agent-native ADR toolkit: inspect the repo, ask only what code can't answer, record and check architecture decisions.", + "category": "productivity", + "source": "./" } ] } diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 41e0b4a..be96495 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,5 +1,5 @@ { "name": "adr-toolkit", - "version": "1.1.0", + "version": "1.1.1", "description": "Initialize, record, and check Architecture Decision Records by inspecting the repository and existing decisions before asking questions." } diff --git a/adapters/antigravity/plugin.json b/adapters/antigravity/plugin.json index 03ae7ba..ecdcbde 100644 --- a/adapters/antigravity/plugin.json +++ b/adapters/antigravity/plugin.json @@ -1,6 +1,6 @@ { "$schema": "https://antigravity.google/schemas/v1/plugin.json", "name": "adr-toolkit", - "version": "1.1.0", + "version": "1.1.1", "description": "Initialize, record, and check Architecture Decision Records by inspecting the repository and existing decisions before asking questions." } diff --git a/adapters/gemini-cli/gemini-extension.json b/adapters/gemini-cli/gemini-extension.json index 41e0b4a..be96495 100644 --- a/adapters/gemini-cli/gemini-extension.json +++ b/adapters/gemini-cli/gemini-extension.json @@ -1,5 +1,5 @@ { "name": "adr-toolkit", - "version": "1.1.0", + "version": "1.1.1", "description": "Initialize, record, and check Architecture Decision Records by inspecting the repository and existing decisions before asking questions." } diff --git a/changelog.md b/changelog.md index f2dd488..878aa55 100644 --- a/changelog.md +++ b/changelog.md @@ -29,6 +29,37 @@ Lightweight human-readable summary of meaningful repository changes. - Logged a Medium-priority backlog item to fold the Cline CLI adapter into the `harness-parity` CI job (currently manually verified against Cline CLI 3.0.61 only), so Cline/ClinePass version drift is caught automatically like the Codex, Gemini, and Antigravity adapters. - Added `CLINE.md` as a thin harness entry pointer (matching `CODEX.md`/`CLAUDE.md`/`GEMINI.md`) and updated `AGENTS.md` to list Cline alongside the other harnesses, plus an explicit note that per-model files (`DEEPSEEK.md`/`GLM.md`/`KIMI.md`/`QWEN.md`) are intentionally not created — those are model providers routed through a harness, not harnesses themselves. Verified that Cline (CLI 3.0.61 with `cline-pass/glm-5.2`) auto-injects the repo-root `AGENTS.md` into workspace context at session start, so the shared operating document reaches every model routed through ClinePass without a per-model entry file. +## v1.1.1 (2026-09-06) + +Hotfix release to close two gaps discovered while deploying v1.1.0: + +- Fixed `.claude-plugin/marketplace.json` to conform to the Claude Code v2.1.263 + marketplace schema by adding the required `owner` field plus `$schema` and a + top-level `description`. `claude plugin marketplace add ` previously + failed with `Invalid schema: ... owner: Invalid input`, blocking fresh + installs even though the shipped `plugin.json` was already correct. Mirrors + the working `Agent-toolkit/.claude-plugin/marketplace.json` shape. +- Extended `scripts/sync_version.py` to also sync the `version = "..."` field + under `pyproject.toml`'s `[project]` table. The v1.1.0 release shipped a GitHub + Release whose skill tarball was `v1.1.0` but whose Python wheel/sdist were + `1.0.1`, because `pyproject.toml`'s `version` was not in the sync surface and + `release.yml` only verified the skill VERSION against the tag. The new + `TOML_VERSION_SPECS` list + `sync_toml_version()` rewrite the `version = "..."` + line in-place by regex (Python 3.10 stdlib has no TOML writer, `tomli` is + read-only). `require_known_paths()` now also checks the `[project]` table is + present, so a future structural edit that deletes the table fails loudly + instead of silently dropping `pyproject.toml` out of the drift check. +- Added regression tests in `tests/unit/test_sync_version.py` that pin the new + behavior: writes version under `[project]`, leaves `version` under other + tables untouched, is idempotent, supports `--check`, skips when the section is + missing — plus a real-repo guard (`test_real_pyproject_version_matches_skill_version`) + that fails the moment `pyproject.toml` drifts from `skills/adr-toolkit/VERSION` + again. +- Bumped `skills/adr-toolkit/VERSION`, `SKILL.md` frontmatter, and every adapter + manifest (`.claude-plugin/plugin.json`, `adapters/gemini-cli/gemini-extension.json`, + `adapters/antigravity/plugin.json`, plus `pyproject.toml`) to **1.1.1** via + `scripts/sync_version.py`. + ## v1.1.0 (2026-09-06) Minor release: new features, governance hardening, and a new harness adapter. diff --git a/handoff.md b/handoff.md index 8887e4f..84dbd16 100644 --- a/handoff.md +++ b/handoff.md @@ -2,58 +2,59 @@ ## Current task -Release v1.1.0: VERSION bumped 1.0.1 → 1.1.0, manifests synced via -`scripts/sync_version.py`, `changelog.md` v1.1.0 section added. Release branch -`release/v1.1.0` off `develop` ready to merge into `master`; after merge, tag -`v1.1.0` will be pushed from `master` to trigger `.github/workflows/release.yml`. +Hotfix v1.1.1: closed two gaps discovered while deploying v1.1.0 — +`.claude-plugin/marketplace.json` missing the `owner` field (blocked +`claude plugin marketplace add` on Claude Code v2.1.263) and `pyproject.toml` +not in `scripts/sync_version.py`'s sync surface (v1.1.0 GitHub Release shipped a +1.0.1 wheel/sdist). Both fixes are landed on `fix/v1.1.1-hotfix`; ready to cut a +release. ## Touched files -- `skills/adr-toolkit/VERSION` — bumped to `1.1.0`. -- `skills/adr-toolkit/SKILL.md` — frontmatter `version: 1.1.0`. -- `.claude-plugin/plugin.json`, `adapters/gemini-cli/gemini-extension.json`, - `adapters/antigravity/plugin.json` — `version` synced to `1.1.0`. -- `changelog.md` — moved the Unreleased block under a new `## v1.1.0 (2026-09-06)` - heading summarizing the 31 commits accumulated since v1.0.1 (Cline adapter, - CLINE.md/AGENTS.md harness entry, GitHub governance hardening, CI supply-chain - hardening, ADR-0017, backlog items). +- `.claude-plugin/marketplace.json` — added `$schema`, top-level `description`, + `owner.name` (mirrors the working `Agent-toolkit/.claude-plugin/marketplace.json`). +- `scripts/sync_version.py` — added `TOML_VERSION_SPECS` (`pyproject.toml` + `[project]` table), `TOML_VERSION_LINE_RE`, `_section_header_re()`, + `sync_toml_version()`. `require_known_paths()` now asserts the `[project]` + table is present. `main()` calls `sync_toml_version()`. +- `tests/unit/test_sync_version.py` — added 6 regression tests for TOML sync + (writes, idempotent, check-only, missing-section, other-table-untouched, + real-pyproject-drift guard). +- `skills/adr-toolkit/VERSION`, `SKILL.md` frontmatter, `.claude-plugin/plugin.json`, + `adapters/gemini-cli/gemini-extension.json`, `adapters/antigravity/plugin.json`, + `pyproject.toml` — all synced to 1.1.1 via `scripts/sync_version.py`. +- `changelog.md` — new `## v1.1.1 (2026-09-06)` section. - `handoff.md` — this file. -## Why 1.1.0 (MINOR, not PATCH) +## Verification (local, Python 3.13 standalone — pytest not installed user-scope) -The 31 commits between v1.0.1 and this release include six `feat:` commits -(Cline CLI adapter, GitHub label taxonomy / labeler / dependabot / Issue Forms / -auto-triage). SemVer requires a MINOR bump for new backward-compatible -features; no breaking changes were identified, so MAJOR is not warranted and -PATCH would understate the surface change. +- `scripts/sync_version.py --check`: **exit 0** (no drift, including pyproject.toml). +- 6 new TOML sync tests re-run as standalone assertions: **all pass**. +- `script/sync_version.py` and `tests/unit/test_sync_version.py` parse with + `ast.parse`: OK. +- Real-repo guard: `pyproject.toml` `[project] version` reports `1.1.1`, matches + `skills/adr-toolkit/VERSION`. ## Next step -1. Open PR `release/v1.1.0` → `master` (AGENTS.md: "Release branches merge - into `master` and back into `develop`"). -2. After CI passes (release.yml runs pytest + sync_version --check + tag == - VERSION), merge into `master`. -3. Back-merge `master` → `develop` (PR), per the git flow. -4. Tag `v1.1.0` from `master` and push — `release.yml` runs the full suite, - verifies manifest versions against the tag, and publishes a GitHub - Release (plus PyPI publish via Trusted Publisher, `continue-on-error`). -5. Delete the short-lived `release/v1.1.0` branch after merge. - -## Verification - -- `scripts/sync_version.py --check`: passes (VERSION, SKILL.md, and all - 4 manifests agree on 1.1.0; no untracked manifests). -- `changelog.md` reflects all 31 v1.0.1..develop commits. -- Working-tree state on `release/v1.1.0`: clean except for the version-sync + - changelog/handoff commits. +1. Merge `fix/v1.1.1-hotfix` → `develop` (PR, CI must pass — including the + version-drift job, which now also checks pyproject.toml). +2. Open `release/v1.1.1` → `master` PR; after CI passes (release.yml runs the + full suite + tag == VERSION check), merge. +3. Back-merge `master` → `develop`. +4. Tag `v1.1.1` from `master` and push — `release.yml` runs pytest + + sync_version --check + tag == VERSION, then publishes a GitHub Release with + the skill tarball + sha256 + Python wheel/sdist, and publishes to PyPI via + Trusted Publisher (`continue-on-error: true`, tracked in improvements.md). +5. After release, refresh the local installs on the four harnesses + (Claude Code `~/.claude/skills/` symlink, Codex `~/.codex/skills/`, + Antigravity `~/.gemini/config/plugins/adr-toolkit/skills/adr-toolkit/`, + Cline `~/.agents/skills/`) to v1.1.1 — the same flow used to bring them to + v1.1.0 in the previous session. ## Open risks -- `pypa/gh-action-pypi-publish` remains `continue-on-error: true`, so the - PyPI publish step can partially fail without failing the release job — - tracked in `improvements.md`. -- Cline adapter is still "Manually verified against 3.0.61" only; the - `harness-parity` CI job does not yet cover Cline — PR #36 logged this as a - Medium backlog item to implement after v1.1.0 ships. -- Inherits prior Open risks (ruleset context sync post-merge, deferred - project/milestone/stale automation). +- PyPI Trusted Publisher still `continue-on-error: true` — known, tracked. +- Cline adapter still manually verified; `harness-parity` not covering Cline yet + (Medium backlog item from PR #36). +- Inherits prior Open risks (ruleset context sync, deferred automation). diff --git a/pyproject.toml b/pyproject.toml index f284080..30bd556 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "adr-toolkit" -version = "1.0.1" +version = "1.1.1" description = "Agent-native Architecture Decision Record toolkit with zero dependencies and deterministic precision" readme = "README.md" license = { text = "MIT" } diff --git a/scripts/sync_version.py b/scripts/sync_version.py index 7a9df21..0d04cc2 100755 --- a/scripts/sync_version.py +++ b/scripts/sync_version.py @@ -23,6 +23,17 @@ (REPO_ROOT / "adapters" / "antigravity" / "plugin.json", ["version"]), ] +# TOML manifests whose `version = "..."` line under [project] we sync from +# VERSION. The Python stdlib has no TOML writer on 3.10, and `tomli` / +# `tomllib` are read-only, so we edit the single `version = "..."` line +# in-place by regex instead of round-tripping through a TOML parser. Each +# entry is (path, section_name) where section_name is the table header the +# `version` line must live under (so we never accidentally rewrite a +# `version = "..."` that appears in some other table). +TOML_VERSION_SPECS = [ + (REPO_ROOT / "pyproject.toml", "project"), +] + # SKILL.md's frontmatter `description:` is the single canonical source; every # manifest below duplicates it for its own harness's format and is synced # from it the same way MANIFEST_SPECS entries are synced from VERSION. @@ -37,6 +48,11 @@ VERSION_FORMAT_RE = re.compile(r"\d+\.\d+\.\d+(-[\w.]+)?") DESCRIPTION_LINE_RE = re.compile(r"^description:[ \t]*(.+)$", re.MULTILINE) +# Matches a TOML `version = "..."` line. We anchor on the line start and +# require the value to be a double-quoted string so this never matches +# `version = 1.0.1` (bare) or a commented-out `# version = "..."`. +TOML_VERSION_LINE_RE = re.compile(r'^version\s*=\s*"([^"]*)"\s*$', re.MULTILINE) + def read_version(version_file: Path) -> str: """Read VERSION and reject anything that isn't a plausible semver string. @@ -118,6 +134,50 @@ def sync_skill_md(version_file: Path, skill_md_path: Path, check_only: bool) -> return True +def _section_header_re(section: str) -> "re.Pattern[str]": + """Match a TOML table header like `[project]` on its own line.""" + return re.compile(rf"^\[{re.escape(section)}\]\s*$", re.MULTILINE) + + +def sync_toml_version(version_file: Path, specs: list, check_only: bool) -> list: + """Sync the `version = "..."` line under each TOML section named in `specs`. + + Unlike JSON manifests, TOML has no stdlib writer on 3.10, so we edit the + single `version = "..."` line in-place by regex. The section anchor + (e.g. `[project]`) keeps us from touching a `version = "..."` that lives + under some other table (e.g. `[tool.something]`). + """ + version = read_version(version_file) + changed: list = [] + for path, section in specs: + if not path.is_file(): + continue + text = path.read_text(encoding="utf-8") + header = _section_header_re(section).search(text) + if header is None: + continue + after = text[header.end():] + # Stop at the next top-level table header so we only consider + # `version = "..."` that actually lives under `section`. + next_header = re.search(r"^\[[^\]]+\]\s*$", after, re.MULTILINE) + window = after if next_header is None else after[: next_header.start()] + match = TOML_VERSION_LINE_RE.search(window) + if match is None: + continue + if match.group(1) == version: + continue + changed.append(path) + if not check_only: + new_line = f'version = "{version}"' + absolute_start = header.end() + match.start() + absolute_end = header.end() + match.end() + path.write_text( + text[:absolute_start] + new_line + text[absolute_end:], + encoding="utf-8", + ) + return changed + + def require_known_paths() -> None: """Fail loudly if a manifest this repo is supposed to track has vanished. @@ -128,6 +188,7 @@ def require_known_paths() -> None: """ all_specs = MANIFEST_SPECS + DESCRIPTION_MANIFEST_SPECS tracked_paths = {p for p, _ in all_specs} + tracked_paths.update(p for p, _ in TOML_VERSION_SPECS) missing = [p for p in tracked_paths if not p.is_file()] if not VERSION_FILE.is_file(): missing.append(VERSION_FILE) @@ -149,6 +210,16 @@ def require_known_paths() -> None: names = ", ".join(f"{_display_path(p)} ({key})" for p, key in keyless) raise SystemExit(f"tracked manifest(s) lost a tracked key: {names}") + # pyproject.toml: assert `[project]` table exists so a structural change + # (e.g. deleting the [project] table) fails loudly instead of silently + # dropping pyproject out of the drift check. + for path, section in TOML_VERSION_SPECS: + if _section_header_re(section).search(path.read_text(encoding="utf-8")) is None: + raise SystemExit( + f"tracked TOML manifest lost its [{section}] table: " + f"{_display_path(path)}" + ) + untracked = discover_untracked_manifests() if untracked: names = ", ".join(_display_path(p) for p in sorted(untracked, key=str)) @@ -187,6 +258,7 @@ def main(argv=None) -> int: require_known_paths() changed = sync(VERSION_FILE, MANIFEST_SPECS, check_only=args.check) changed += sync_descriptions(SKILL_MD_PATH, DESCRIPTION_MANIFEST_SPECS, check_only=args.check) + changed += sync_toml_version(VERSION_FILE, TOML_VERSION_SPECS, check_only=args.check) if sync_skill_md(VERSION_FILE, SKILL_MD_PATH, check_only=args.check): changed.append(SKILL_MD_PATH) diff --git a/skills/adr-toolkit/SKILL.md b/skills/adr-toolkit/SKILL.md index 92adc67..dcded21 100644 --- a/skills/adr-toolkit/SKILL.md +++ b/skills/adr-toolkit/SKILL.md @@ -2,7 +2,7 @@ name: adr-toolkit description: Initialize, record, and check Architecture Decision Records by inspecting the repository and existing decisions before asking questions. user-invocable: true -version: 1.1.0 +version: 1.1.1 --- # ADR Toolkit diff --git a/skills/adr-toolkit/VERSION b/skills/adr-toolkit/VERSION index 9084fa2..8cfbc90 100644 --- a/skills/adr-toolkit/VERSION +++ b/skills/adr-toolkit/VERSION @@ -1 +1 @@ -1.1.0 +1.1.1 \ No newline at end of file diff --git a/tests/unit/test_sync_version.py b/tests/unit/test_sync_version.py index d2fa7c6..ccbd965 100644 --- a/tests/unit/test_sync_version.py +++ b/tests/unit/test_sync_version.py @@ -310,3 +310,98 @@ def test_discover_untracked_manifests_finds_no_untracked_files_in_clean_repo(): untracked = _sync_version.discover_untracked_manifests() assert untracked == [], f"untracked plugin manifests found: {untracked}" + +# --- TOML version sync (pyproject.toml) ------------------------------------ + +import re as _re # local to this section to avoid shadowing the top-level helper + +sync_toml_version = _sync_version.sync_toml_version +TOML_VERSION_LINE_RE = _sync_version.TOML_VERSION_LINE_RE + + +def _write_pyproject(tmp_path, body): + path = tmp_path / "pyproject.toml" + path.write_text(body, encoding="utf-8") + return path + + +def test_sync_toml_version_updates_version_under_project_table(tmp_path): + version_file = tmp_path / "VERSION" + version_file.write_text("1.2.3\n", encoding="utf-8") + pyproject = _write_pyproject( + tmp_path, + '[project]\nname = "x"\nversion = "0.0.0"\n\n[tool.something]\nversion = "do-not-touch"\n', + ) + + changed = sync_toml_version(version_file, [(pyproject, "project")], check_only=False) + + text = pyproject.read_text(encoding="utf-8") + assert 'version = "1.2.3"' in text + assert 'version = "do-not-touch"' in text # other section untouched + assert changed == [pyproject] + + +def test_sync_toml_version_is_idempotent_when_already_synced(tmp_path): + version_file = tmp_path / "VERSION" + version_file.write_text("1.2.3\n", encoding="utf-8") + pyproject = _write_pyproject(tmp_path, '[project]\nversion = "1.2.3"\n') + + changed = sync_toml_version(version_file, [(pyproject, "project")], check_only=False) + + assert changed == [] + + +def test_sync_toml_version_check_only_reports_drift_without_writing(tmp_path): + version_file = tmp_path / "VERSION" + version_file.write_text("1.2.3\n", encoding="utf-8") + pyproject = _write_pyproject(tmp_path, '[project]\nversion = "0.0.0"\n') + + changed = sync_toml_version(version_file, [(pyproject, "project")], check_only=True) + + assert changed == [pyproject] + assert 'version = "0.0.0"' in pyproject.read_text(encoding="utf-8") + + +def test_sync_toml_version_skips_when_section_missing(tmp_path): + version_file = tmp_path / "VERSION" + version_file.write_text("1.2.3\n", encoding="utf-8") + # No [project] table at all → nothing to sync, no error, no write. + pyproject = _write_pyproject(tmp_path, '[tool.other]\nversion = "0.0.0"\n') + + changed = sync_toml_version(version_file, [(pyproject, "project")], check_only=False) + + assert changed == [] + assert 'version = "0.0.0"' in pyproject.read_text(encoding="utf-8") + + +def test_sync_toml_version_does_not_touch_version_under_other_section(tmp_path): + version_file = tmp_path / "VERSION" + version_file.write_text("1.2.3\n", encoding="utf-8") + # `version` only under [tool.something], not under [project] → untouched + pyproject = _write_pyproject(tmp_path, '[tool.something]\nversion = "0.0.0"\n') + + changed = sync_toml_version(version_file, [(pyproject, "project")], check_only=False) + + assert changed == [] + assert 'version = "0.0.0"' in pyproject.read_text(encoding="utf-8") + + +def test_real_pyproject_version_matches_skill_version(): + """Guard against a repeat of the v1.1.0 release bug, where pyproject.toml's + version drifted out of sync with skills/adr-toolkit/VERSION because pyproject + was not in the sync surface. This test stays green only as long as + scripts/sync_version.py propagates the VERSION bump into pyproject.toml.""" + skill_version = _sync_version.read_version(REAL_VERSION_FILE) + pyproject = REAL_SKILL_MD_PATH.parent.parent.parent / "pyproject.toml" + text = pyproject.read_text(encoding="utf-8") + header = _sync_version._section_header_re("project").search(text) + assert header is not None, "[project] table missing from pyproject.toml" + after = text[header.end():] + next_header = _re.search(r"^\[[^\]]+\]\s*$", after, _re.MULTILINE) + window = after if next_header is None else after[: next_header.start()] + match = TOML_VERSION_LINE_RE.search(window) + assert match is not None, 'version = "..." line missing under [project]' + assert match.group(1) == skill_version, ( + f"pyproject.toml version {match.group(1)!r} != skill VERSION {skill_version!r}" + ) + From 3c8ab111a09ca809afdebd91290166b36e9e734b Mon Sep 17 00:00:00 2001 From: shcommit Date: Sun, 6 Sep 2026 18:46:35 +0900 Subject: [PATCH 2/2] docs(handoff): note PR #43 pr-title-check re-trigger after retitle The initial PR #43 title fix(v1.1.1): ... did not match the pr-title-check regex (scope v1.1.1 contains dots --- handoff.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/handoff.md b/handoff.md index 84dbd16..ad13a65 100644 --- a/handoff.md +++ b/handoff.md @@ -58,3 +58,11 @@ release. - Cline adapter still manually verified; `harness-parity` not covering Cline yet (Medium backlog item from PR #36). - Inherits prior Open risks (ruleset context sync, deferred automation). + +## PR #43 pr-title-check stale re-trigger + +The first PR #43 title `fix(v1.1.1): ...` did not match the +pr-title-check regex (scope `v1.1.1` contains dots, but the regex allows +only `[a-z0-9-]+`). PR title was retitled to `fix(release): ... for v1.1.1`, +and this follow-up commit re-triggers the workflow so the refresh catches +the new title.