Skip to content

fix(release): name a stale-but-resolved gate 3 plugin root - #1732

Merged
fdaviddpt merged 2 commits into
mainfrom
fix/1721
Sep 23, 2026
Merged

fdaviddpt merged 2 commits into
mainfrom
fix/1721

Conversation

@fdaviddpt

Copy link
Copy Markdown
Contributor

Gate 3 audited with one copy of the plugin, measured another, and a third was installed -- checklist_skew.py --plugin-root <resolved> reported a version and route, but nothing said whether that resolved copy was the newest one actually in the cache. #1721 asked for three things and this closes all three.

What changed

  • scripts/plugin_update.py: newest_cached_version(name, plugins_root=None) scans <plugins_root>/cache/<marketplace>/<name>/* for the highest version directory present, independent of what any project has recorded as installed. New --print-newest-cached-version CLI mode mirrors the existing --print-resolved-root. The version-sort closure (_version_key) moved to module level so both call sites share it.
  • scripts/checklist_skew.py: compare_root_freshness(resolved_version, newest_cached_version) is a pure, three-state comparison -- root-current, root-stale, root-could-not-tell (missing either input never silently reads as current) -- plus root_freshness_receipt() and a --compare-root-freshness CLI mode.
  • commands/release.md gate 3: the existing GATE3_ROOT bash block (kept as one fenced block, per the The release command's checklist_skew.py recipe omits --plugin-root, so gate 3 reports could-not-tell where the real answer is not-applicable #789 lesson already documented there -- bash variables do not survive across separate Bash tool calls) now also derives the resolved version and the newest cached version, then calls --compare-root-freshness. Prose states this is a separate third axis, not a fifth state of the existing matches/differs/not-applicable/could-not-tell set, and answers the issue's other two asks directly: the spawn's own plugin root cannot be pinned to gate 3's measured root by construction of the harness's plugin registration (the pre-existing Un agent spawne charge une definition plus vieille que ce que plugin_update.py rapporte, et rien ne compare les deux #1328 --compare-effect mechanism already effectively covers this; this diff makes the conclusion explicit in prose), and the growing version cache is to be tolerated rather than pruned, since neither this fix nor newest_cached_version knows which sibling project on the machine still depends on an older cached copy.

Self-review

Two findings fixed: .get("version", "") on the new one-liner only defaulted on a missing key, not an explicit JSON null, which would have fabricated the literal version string "None" instead of falling into root-could-not-tell (caught by oss:auditor, verified by direct Python repro); and a missing encoding="utf-8" on the same one-liner's open() call (caught independently by two reviewers).

One adjacent finding logged rather than fixed here: skills/manager/phases/tick-order.md has the identical staleness gap for DOCTOR_ROOT, out of this issue's scope (a different subsystem, the maintainer tick loop rather than the release gate) -- see trap.d/1721.doctor-root-same-staleness-gap.md.

Closes #1721

[AI-generated]

fdaviddpt and others added 2 commits September 23, 2026 23:41
Gate 3's `checklist_skew.py` already names WHICH checklist version ran
against a repo, but had no notion of whether the root it measured was
itself the newest copy of the plugin sitting on this machine. A
`resolved-install` root is built from the version THIS project has
recorded, which a sibling project's more recent install -- or a cache
directory nobody ever prunes -- can leave behind without anything
saying so: gate 3 once audited with one copy, measured another, and a
third was already installed.

`plugin_update.newest_cached_version` scans the plugin's own cache
directory for the highest version present, independent of which
project is pinned to it, exposed via a new `--print-newest-cached-
version` CLI mode mirroring `--print-resolved-root`.
`checklist_skew.compare_root_freshness` compares that against the
root gate 3 resolved, in three states -- `root-current` / `root-stale`
/ `root-could-not-tell` -- the same discipline `compare_effect`
already uses, wired in via a new `--compare-root-freshness` CLI mode.

`commands/release.md` gate 3's prose now computes and reports this
alongside the existing checklist-skew payload, states that the
spawned auditor's own root cannot be pinned to `GATE3_ROOT` by
construction of the harness's plugin registration (already answered
by the existing `--compare-effect` mechanism, now said explicitly),
and records the decision to tolerate rather than prune a growing
version cache, since neither this fix nor `newest_cached_version` has
any notion of which sibling project still depends on an older copy.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ng (#1721)

Two findings from the spawned self-review round on 765bc07, both fixed:

- `checklist_skew.py`'s auditor found `GATE3_RESOLVED_VERSION`'s python3 -c
  one-liner used `.get('version', '')`, which only substitutes the default
  when the key is ABSENT. A `plugin.json` carrying `"version": null`
  (key present, value JSON null) decodes to Python `None`, and `print(None)`
  writes the literal string "None" -- truthy and non-empty, so
  `compare_root_freshness` would skip its could-not-tell branch and report
  `root-stale` naming a fabricated "None" version instead of the real
  unknown. Fixed with `.get('version') or ''`, which degrades both the
  missing-key and explicit-null cases to could-not-tell alike.
- The Explore reviewer and the auditor both independently noted the same
  one-liner's bare `open(...)` carries no explicit `encoding="utf-8"`, an
  existing repo convention elsewhere (agents/doctor.md, agents/tick-review.md
  use the identical idiom) but one this new line could trivially avoid
  joining. Added `encoding="utf-8"` explicitly.

A third finding -- `skills/manager/phases/tick-order.md`'s `DOCTOR_ROOT`
resolution has the identical staleness gap this issue closes for gate 3,
left out of scope on purpose since the issue's own title scopes to gate 3 --
is logged to trap.d/ for /oss:curate to weigh rather than fixed here: it
reaches a different subsystem (the maintainer tick loop, not the release
gate) and needs its own judgment call about whether doctor's own root
staleness is worth reporting the same way.

TREE: clean (tree_snapshot.py compare, before both reviewer spawns).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@fdaviddpt
fdaviddpt merged commit d8c7f6f into main Sep 23, 2026
10 checks passed
@fdaviddpt
fdaviddpt deleted the fix/1721 branch September 23, 2026 22:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Gate 3 resolved-install can return a stale plugin root, and the spawn resolves its own root independently

1 participant