Judge peer dependencies by range, and check the packageManager pin

Two npm-specific corrections found by running the gate over web_ui.

**`peerDependencies` must not be exact-pinned.** The gate was reporting
`react: ">=19"` as "not exact-pinned", and acting on that would have been
actively harmful: a peer range declares what a CONSUMER may bring, so pinning
it forces every consumer of @kuhyx/web-ui onto one exact React. Peers are now
judged the way toolchain ranges are -- does the range still admit latest? --
and only reported when it does not.

**`packageManager` is a real dependency.** corepack enforces it, so
`pnpm@10.13.1` is an exact pin that nothing was checking. It is now read and
compared, which immediately surfaced that both TS packages sit on pnpm 10.13.1
against 11.24.0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CRykabMwPgDzFHcARoqpaK
This commit is contained in:
Krzysztof Rudnicki 2026-08-28 13:38:40 +02:00
parent 81d2493476
commit 71133a808f
5 changed files with 70 additions and 8 deletions

View File

@ -64,6 +64,17 @@ def judge(dep: Dep, answer: Answer) -> Finding | None:
if dep.ecosystem == TOOLCHAIN:
return _toolchain(dep, latest)
if dep.peer:
# A peerDependency declares what a CONSUMER may bring, so exact-pinning
# it is actively wrong: it would force every consumer onto one version.
# The meaningful question is whether the range still admits latest.
if not satisfies(dep.constraint, latest):
return Finding(
dep, Severity.STALE, latest,
detail=f"the peer range excludes the current {latest}",
)
return None
if dep.pinned is None:
if dep.caret_ok:
floor = lower_bound(dep.constraint)

View File

@ -37,6 +37,7 @@ class Dep:
dev: bool = False
caret_ok: bool = False # Q13 carve-out: a range is legal here
override: bool = False
peer: bool = False # a compatibility range, not a build pin
@dataclass(frozen=True)

View File

@ -16,13 +16,14 @@ from dep_freshness.models import Dep
from dep_freshness.parsers._lines import index
from dep_freshness.versions import exact_pin
# (section, is_dev, is_override, is_peer)
_SECTIONS = (
("dependencies", False, False),
("devDependencies", True, False),
("optionalDependencies", False, False),
("peerDependencies", False, False),
("resolutions", False, True),
("overrides", False, True),
("dependencies", False, False, False),
("devDependencies", True, False, False),
("optionalDependencies", False, False, False),
("peerDependencies", False, False, True),
("resolutions", False, True, False),
("overrides", False, True, False),
)
_LOCAL_PREFIXES = ("file:", "link:", "workspace:", "git+", "github:", "npm:")
@ -49,14 +50,26 @@ def parse_package_json(path: Path) -> list[Dep]:
line=lines.get("node", 0), pinned=exact_pin(node), caret_ok=True,
))
for section, is_dev, is_override in _SECTIONS:
# `packageManager: pnpm@10.13.1` is an exact pin of the package manager
# itself, which corepack enforces -- a real dependency, and the only one
# three of these repos omit entirely.
manager = data.get("packageManager")
if isinstance(manager, str) and "@" in manager:
name, _, version = manager.rpartition("@")
deps.append(Dep(
ecosystem=NPM, name=name, constraint=version, path=path,
line=lines.get("packageManager", 0), pinned=exact_pin(version),
dev=True,
))
for section, is_dev, is_override, is_peer in _SECTIONS:
for name, spec in (data.get(section) or {}).items():
if not isinstance(spec, str) or spec.startswith(_LOCAL_PREFIXES):
continue
deps.append(Dep(
ecosystem=NPM, name=str(name), constraint=spec, path=path,
line=lines.get(str(name), 0), pinned=exact_pin(spec),
dev=is_dev, override=is_override,
dev=is_dev, override=is_override, peer=is_peer,
))
return deps

View File

@ -110,6 +110,25 @@ def test_a_pinned_toolchain_behind_latest_is_stale():
assert finding.severity is Severity.STALE
def test_a_peer_range_admitting_latest_is_not_a_violation():
"""Exact-pinning a peer dep would force every consumer onto one version."""
assert judge(
dep(ecosystem="npm", name="react", constraint=">=19", pinned=None,
peer=True),
Answer("19.2.8"),
) is None
def test_a_peer_range_excluding_latest_is_reported():
finding = judge(
dep(ecosystem="npm", name="react", constraint=">=19 <19.2", pinned=None,
peer=True),
Answer("19.2.8"),
)
assert finding.severity is Severity.STALE
assert "peer range excludes" in finding.detail
def test_no_answer_at_all_is_unknown_not_stale():
finding = judge(dep(), Answer(None, unavailable=True))
assert finding.severity is Severity.UNKNOWN

View File

@ -110,6 +110,24 @@ lint = ["ruff==0.16.5"]
assert deps["python"].constraint == ">=3.10"
def test_package_json_reads_the_package_manager_pin(tmp_path):
"""corepack enforces `packageManager`, so it is a real dependency."""
path = write(tmp_path, "package.json", '{"packageManager": "pnpm@10.13.1"}')
deps = {d.name: d for d in javascript.parse_package_json(path)}
assert deps["pnpm"].pinned == "10.13.1"
def test_a_package_manager_without_a_version_is_ignored(tmp_path):
path = write(tmp_path, "package.json", '{"packageManager": "pnpm"}')
assert javascript.parse_package_json(path) == []
def test_peer_dependencies_are_marked_as_such(tmp_path):
path = write(tmp_path, "package.json",
'{"peerDependencies": {"react": ">=19"}}')
assert javascript.parse_package_json(path)[0].peer
def test_package_json_reads_engines_and_overrides(tmp_path):
path = write(tmp_path, "package.json", """\
{"engines": {"node": "24.20.0"},