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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
@@ -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": "./"
}
]
}
2 changes: 1 addition & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -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."
}
2 changes: 1 addition & 1 deletion adapters/antigravity/plugin.json
Original file line number Diff line number Diff line change
@@ -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."
}
2 changes: 1 addition & 1 deletion adapters/gemini-cli/gemini-extension.json
Original file line number Diff line number Diff line change
@@ -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."
}
31 changes: 31 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <repo>` 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.
Expand Down
93 changes: 51 additions & 42 deletions handoff.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,58 +2,67 @@

## 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.
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.

## Verification
## Open risks

- `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.
- 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).

## Open risks
## PR #43 pr-title-check stale re-trigger

- `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).
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.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
Expand Down
72 changes: 72 additions & 0 deletions scripts/sync_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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.
Expand Down Expand Up @@ -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.

Expand All @@ -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)
Expand All @@ -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))
Expand Down Expand Up @@ -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)

Expand Down
2 changes: 1 addition & 1 deletion skills/adr-toolkit/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion skills/adr-toolkit/VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.1.0
1.1.1
Loading
Loading