fix(plugins): plugin dependencies follow the plugin's own security policy

Hermes's 14-day `[tool.uv] exclude-newer` quarantine applies to Hermes's own
dependencies only (uv lock/sync, `hermes update`, LAZY_DEPS extras via
`ensure()`). A plugin's declared `python_dependencies` install under the
PLUGIN's policy: `install_specs(policy="plugin")` runs uv with `--no-config`
from any cwd, still inside the core constraints file.

Reverses item 3 of #118841, which ran the uv tier with cwd=<checkout> for
every install so the quarantine reached plugin deps from any cwd. That made
catalog re-pins floored on a <14-day release uninstallable (#120076:
"only hindsight-client<=0.9.2 is available"; #114530 held on the same gate).

Maintainer ruling (Teknium): "plugins dont have to abide by our 14 day rule
btw. They can have their own security policy on that. Only hermes'
dependencies themselves have to. We should recommend that they do this for
their plugins and we should give guidance to plugin devs that they should
though."

- tools/lazy_deps.py: INSTALL_POLICIES ("core" | "plugin"); `_uv_policy_args`
  replaces `_uv_policy_cwd`; `_venv_pip_install(policy=)` defaults to core
  (ensure/LAZY_DEPS), `install_specs(policy=)` defaults to plugin.
- hermes_cli/plugin_python_deps.py: `resolve()` passes policy="plugin".
- Docs: developer guide "Dependency security policy" section, catalog README
  admission rule 9, AGENTS.md pinning policy — plugin authors are responsible
  for their deps and strongly recommended to pin upper bounds, floor on the
  oldest API-compatible version and run their own release quarantine
  (`uv --exclude-newer` in their CI); operators can set UV_EXCLUDE_NEWER.
- Tests: the #118841 cwd test is replaced by two invariants — a plugin install
  carries `--no-config` and no checkout cwd (red on base), a core lazy install
  keeps the checkout cwd and no `--no-config`.
This commit is contained in:
teknium1
2026-09-23 05:58:26 -07:00
committed by Teknium
parent 8230b524a5
commit 358d50ca6d
6 changed files with 125 additions and 24 deletions

View File

@@ -266,7 +266,9 @@ def resolve(specs: list[str], constraints: list[str], *, dry_run: bool, timeout:
"""Run the shared installer ladder on *specs* under *constraints*. Raises ``DependencyConflict`` or
``DependencyInstallError``; returns the installer result on success."""
from tools.lazy_deps import install_specs
result = install_specs(specs, timeout=timeout, constraints=constraints, dry_run=dry_run)
# A plugin's declared deps follow the plugin's own security policy; Hermes's exclude-newer quarantine
# covers Hermes's packages only (core_constraints still keeps them in range).
result = install_specs(specs, timeout=timeout, constraints=constraints, dry_run=dry_run, policy="plugin")
error = _classify(result)
if error is not None:
raise error