From c36213201670c92d4a27e68d39cfebf475b5b484 Mon Sep 17 00:00:00 2001 From: Florian DAVID <150798857+fdaviddpt@users.noreply.github.com> Date: Sat, 8 Aug 2026 23:25:49 +0200 Subject: [PATCH] Fix stale docs/validators.md citations (#333) and pin README version badge to manifest (#335) Co-Authored-By: Max --- CHANGELOG.md | 4 +++ README.md | 2 +- .../before_session_start/50-git-restore.sh | 7 ++--- tests/test_git_restore_hook_253.py | 5 ++-- tests/test_version_manifest.py | 27 +++++++++++++++++++ 5 files changed, 39 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 58692b9..645130f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- **The README version badge had read 0.8.3 for nine releases** ([#335](https://github.com/Digital-Process-Tools/claude-remember/issues/335)) — the release sweep greps for the *outgoing* version when cutting a new one, which finds every site mid-bump and none that stopped being bumped at all. Bumped to match `.claude-plugin/plugin.json` (0.18.0), and `tests/test_version_manifest.py::test_readme_version_badge_matches_code` now pins the badge to the manifest and fails loud if its own pattern stops matching, rather than passing on a badge it never found. + +- **Two files cited `docs/validators.md`, a path that only exists in the sibling `claude-supertool` repo** ([#333](https://github.com/Digital-Process-Tools/claude-remember/issues/333)) — `hooks.d/before_session_start/50-git-restore.sh` and `tests/test_git_restore_hook_253.py` pointed readers at a doc this repo has never shipped. The "declining instead of guessing" three-state contract they were citing does exist here: the 0.12.0 entry above, and `_push_and_report` in `hooks.d/after_save/50-git-backup.sh`. Both citations now point there instead. + - **A cooldown marker AHEAD of now sticks the throttle ON, permanently and mutely** ([#326](https://github.com/Digital-Process-Tools/claude-remember/issues/326)) — `case "$X" in ''|*[!0-9]*) X=0 ;; esac` validates a marker's *syntax*. It does not bound its *range*. A digits-only value ahead of the current clock is accepted, makes `ELAPSED` negative, and a negative `ELAPSED` is `-lt` any cooldown — so the gate takes its `exit 0`. That exit sits **above** the line that rewrites the marker, so the self-heal the previous three releases rest on is unreachable on exactly the path that needs it. Four sites, four different outages: no session is ever saved again; `now.md` is never compressed again and grows unbounded; the git backup stops, which is [#258](https://github.com/Digital-Process-Tools/claude-remember/issues/258)'s original outage reached through a value the guard *accepts*; and the per-tool-call fork throttle refuses to fork the save that would have healed the marker, so the two throttles hold each other shut. **This needs no corruption.** An NTP step backwards, a VM snapshot restore, a container clock jump, or a store on a share with a skewed clock all produce it. Not a timezone or DST change — epoch seconds do not move for those, which is why proceeding is the right call and costs at most one extra save rather than mis-throttling a laptop that suspended over a boundary. diff --git a/README.md b/README.md index ad1e8b9..cbc5b7c 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![Python](https://img.shields.io/badge/python-3.9%2B-blue)](https://www.python.org/) [![OS](https://img.shields.io/badge/tested%20on-Linux%20%7C%20macOS%20%7C%20Windows-blue)](https://github.com/Digital-Process-Tools/claude-remember/actions/workflows/tests.yml) [![License](https://img.shields.io/badge/license-Community-brightgreen)](LICENSE) -[![Version](https://img.shields.io/badge/version-0.8.3-orange)](.claude-plugin/plugin.json) +[![Version](https://img.shields.io/badge/version-0.18.0-orange)](.claude-plugin/plugin.json) Claude Code starts every session blank. It doesn't know what you worked on yesterday, what conventions your team follows, or what mistakes it already made. You re-explain everything, every time. diff --git a/hooks.d/before_session_start/50-git-restore.sh b/hooks.d/before_session_start/50-git-restore.sh index 79396eb..ae56b47 100755 --- a/hooks.d/before_session_start/50-git-restore.sh +++ b/hooks.d/before_session_start/50-git-restore.sh @@ -310,9 +310,10 @@ _spawn_fetch() { } # ── What the last fetch has to say ─────────────────────────────────────────── -# Three answers, never two (docs/validators.md, "Declining instead of -# guessing"): it worked / it failed / it never came back. Silence is not a -# fourth way of saying "up to date". +# Three answers, never two ("declining instead of guessing", CHANGELOG.md +# 0.12.0, and _push_and_report in hooks.d/after_save/50-git-backup.sh for the +# worked example): it worked / it failed / it never came back. Silence is not +# a fourth way of saying "up to date". _fetch_health() { [ -f "$FETCH_STATE_FILE" ] || { echo "never-run"; return; } local _s='' _f='' _rc='' _now _age diff --git a/tests/test_git_restore_hook_253.py b/tests/test_git_restore_hook_253.py index 5e04fa8..8ff3609 100644 --- a/tests/test_git_restore_hook_253.py +++ b/tests/test_git_restore_hook_253.py @@ -460,8 +460,9 @@ def test_a_repaired_store_clears_the_count(self, tmp_path): class TestCouldNotCheckIsItsOwnState: """Part 1's defect, mirrored onto the read side: 'I could not reach the - remote' must never render as 'you are up to date'. `docs/validators.md` - calls this declining instead of guessing.""" + remote' must never render as 'you are up to date'. CHANGELOG.md's 0.12.0 + entry and `_push_and_report` in hooks.d/after_save/50-git-backup.sh call + this declining instead of guessing.""" def test_a_failed_fetch_is_not_reported_as_up_to_date(self, tmp_path): home, remember, remote, slug_dir, project = _store(tmp_path) diff --git a/tests/test_version_manifest.py b/tests/test_version_manifest.py index 4c8985d..f20bc77 100644 --- a/tests/test_version_manifest.py +++ b/tests/test_version_manifest.py @@ -14,9 +14,13 @@ REPO_ROOT = Path(__file__).resolve().parent.parent MANIFEST = REPO_ROOT / ".claude-plugin" / "plugin.json" CHANGELOG = REPO_ROOT / "CHANGELOG.md" +README = REPO_ROOT / "README.md" SEMVER_RE = re.compile(r"^\d+\.\d+\.\d+$") RELEASE_HEADING_RE = re.compile(r"^## \[(\d+\.\d+\.\d+)\]") +README_BADGE_RE = re.compile( + r"!\[Version\]\(https://img\.shields\.io/badge/version-(\d+\.\d+\.\d+)-orange\)" +) def _manifest_version() -> str: @@ -59,3 +63,26 @@ def test_changelog_releases_are_strictly_descending(): assert versions == sorted(versions, reverse=True), ( f"CHANGELOG.md release headings are not in descending order: {_changelog_versions()}" ) + +def test_readme_version_badge_matches_code(): + """A sweep keyed on the outgoing version can never find a badge that stopped + being bumped (#335): it is not mid-transition, so it never matches the + pattern any release sweep would grep for. Pin the badge to the manifest + directly, and fail loud if the badge is not even found -- a regex that + matched nothing has not checked the badge, and must not read as a pass. + """ + text = README.read_text(encoding="utf-8") + match = README_BADGE_RE.search(text) + assert match is not None, ( + "README.md has no version badge matching the expected shields.io pattern " + "(![Version](.../badge/version-X.Y.Z-orange)) -- the badge markup may have " + "changed shape. Update README_BADGE_RE rather than letting this pass silently." + ) + badge_version = match.group(1) + manifest_version = _manifest_version() + assert badge_version == manifest_version, ( + f"README.md's version badge reads {badge_version} but " + f".claude-plugin/plugin.json declares {manifest_version}. The badge is " + f"README.md:9 and drifts silently because release sweeps grep for the " + f"outgoing version, which a frozen badge never matches (#335)." + )