A `pkg @ git+https://github.com/<owner>/<repo>@pkg-vX.Y.Z` pin outside
kuhyx/utils was asked of utils, which has no such tag, so
steam-backlog-enforcer's leisure-watch pin was undeterminable (exit 3).
Foreign pins are now named `<host>/<owner>/<repo>:<pkg>`; utils pins keep
their bare names (allowlist labels and cache keys unchanged).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dsh4Znwh2KnC7q6vbGpX1N
**The gate could not see the shared Python libraries at all.** They are
consumed as PEP 508 direct references --
`crdt-sync @ git+https://github.com/kuhyx/utils@crdt-sync-v0.5.1#subdirectory=crdt-sync`
-- which parsed as an unpinned PyPI package that does not exist on PyPI. That
answer is undeterminable, not stale, so wake-alarm sat on gatelock-v0.4.1
against v0.7.1 and crdt-sync-v0.5.1 against v0.9.0 with a green gate. They are
git-tag dependencies now, judged the same way the Dart libraries are.
A ref that is a branch or a SHA still parses as before: there is no version
in it to compare.
The line is found textually when the key indexer cannot help. A TOML
dependency array holds bare strings with no `key =`, so these findings would
otherwise report line 0 and make the fix a search.
Both libraries also had their dependency upgrades land after their newest tag
(81d2493), so anything moving to "latest tag" would have taken the pre-upgrade
constraints. Cut 0.9.1 and 0.7.2; version bump only.
315 tests, 100% branch coverage.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CRykabMwPgDzFHcARoqpaK
"Are we on the newest version?" was not mechanically answerable across these
repos: constraint styles had drifted per-ecosystem and a caret range is
"newest-compatible" by definition, which gives a checker nothing to compare.
This gate makes it answerable and then enforces the answer.
Checks two things at once -- that every dependency carries an exact pin, and
that the pin equals its registry's latest STABLE release -- across pub.dev,
PyPI, npm, crates.io, the Go proxy, kuhyx/utils git tags, and the Flutter /
Node / Python toolchains.
Three deliberate design points:
* Pre-releases are rejected per-ecosystem, because every ecosystem leaks them
differently and each leak fails GREEN. npm lets a maintainer point
`dist-tags.latest` at an rc; crates.io's `max_version` includes them; PyPI's
legacy `info.version` includes yanked releases. Unit-tested with fixtures,
since an end-to-end run cannot surface a wrong "up to date".
* Offline, the gate serves an expired cache entry and PASSES with a DEGRADED
banner (exit 3 only under --strict, which is CI). `--no-verify` is banned
here, so a hook that hard-failed offline would leave no way to commit at all.
* The allowlist has two entry classes. `blocked_by: transitive:<pkg>@<ver>` is
a predicate that clears itself when upstream moves and must NOT carry a
date; everything else is discretionary and requires `expires`, capped at 90
days. Exact pins plus latest-everywhere is provably unsatisfiable when a
dependency internally constrains a shared transitive, and expiring the
predicate class on a calendar would turn the gate red for a reason no commit
could fix. An entry with nothing left to excuse is itself an error.
One hook id, one shim, one shared implementation -- always delegating, so it
cannot fork the way the 250-line cap forked into four ids, two of which were
independent reimplementations.
256 tests, 100% branch coverage on every module of the gate. Also adds the
workflow that runs them, which incidentally gives md_naming's 71 tests their
first CI run.
Not yet installed in any repo: a repo that gets the gate before its deps are
bumped is instantly and permanently red, so the gate and the upgrade land
together, per repo.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CRykabMwPgDzFHcARoqpaK