Repository navigation
Fix Hatch environments being invisible to stale-install checks and VEX (#335) - #700
Conversation
Hatch installs a project into envs under its data directory, never ./.venv, so stale-install checks, VEX and agent mode never looked at the environment hatch run actually uses. Model Hatch's placement rules (data dir, dirs.env.virtual, flat layouts, explicit env paths, the project id hash) and add those envs to local venv discovery. Hosted scans now warn about a stale Hatch env with the remedy that works (hatch env remove / prune), and vendored Hatch gets the same check as pypi_hatch_stale_install. A real-Hatch e2e covers both modes from an existing env through vex and the remedy. Fixes #335 Assisted-by: Claude Code:claude-opus-5-5
35f7b32 to
f36a803
Compare
Hatch 1.0 to 1.2 keep envs at <name>-<id>/<env>, so discover that layout too. Vendored vex already warns when the installed tree is out of sync with the committed artifact; for a Hatch project the advice to re-run the install does nothing, so name hatch env remove instead. Refs #335 Assisted-by: Claude Code:claude-opus-5-5
Explain where Hatch keeps environments, which warning each mode gives for a stale one and the remedy, and how to run the real-Hatch check. Refs #335 Assisted-by: Claude Code:claude-opus-5-5
|
[agent] CI note on Generated by Claude Code |
On macOS /var is a symlink to /private/var, so an activated env and the discovered one can name the same directory differently, and the stale-install check then missed it. Compare resolved paths as a fallback, and leave dirs.env.virtual unresolved as Hatch does (only an env's explicit path is resolved). Refs #335 Assisted-by: Claude Code:claude-opus-5-5
|
BugBot review Generated by Claude Code |
An activated venv that is not one of Hatch's own is never used by hatch run, yet it returned from discovery before the Hatch envs were added, so a developer shell with any venv active hid the stale Hatch env again. Add Hatch's envs in that case too, and have the vendored probe judge Hatch's env prefixes directly. Refs #335 Assisted-by: Claude Code:claude-opus-5-5
|
BugBot review Generated by Claude Code |
|
Bugbot Autofix prepared a fix for the issue found in the latest run.
Or push these changes by commenting: Preview (d5f09ab674)You can send follow-ups to the cloud agent here. |
|
Ready for review (burn-down agent).
Generated by Claude Code |
main restructured the activated-venv step so Poetry projects go through poetry_project_site_packages; keep that and add Hatch's out-of-tree envs to the non-Poetry activated-venv result, which is what this branch did before the restructure (#335). In CLI_CONTRACT.md keep main's reworded pypi_pipenv_stale_install row and this branch's pypi_hatch_stale_install row. Co-Authored-By: Claude <noreply@anthropic.com>
|
bugbot run Generated by Claude Code |
1 similar comment
|
bugbot run Generated by Claude Code |
main added a pipenv_lock parameter to the hosted Python stale-install probe; the Hatch remedy test from this branch still called it with the old arity, so the CLI test build broke after the merge. Refs #335 Assisted-by: Claude Code:claude-opus-5-5
|
[agent] Generated by Claude Code |
A Hatch project's pyproject alone reads as a uv project, so a set UV_PROJECT_ENVIRONMENT returned from discovery before Hatch's envs were added, hiding the env hatch run uses again. Add them on that path too. Refs #335 Assisted-by: Claude Code:claude-opus-5-5
Instead of appending Hatch's envs at each early return, wrap the whole local discovery so a recorded PDM/uv env, an activated venv, Pipenv's or Poetry's resolution all keep the project's Hatch envs visible to stale-install checks, agent mode and VEX. Refs #335 Assisted-by: Claude Code:claude-opus-5-5
|
BugBot review Generated by Claude Code |
A project's [tool.hatch.envs.hatch-test] table is layered over Hatch's built-in hatch-test config, so the default Python matrix still applies unless the project sets its own matrix. The ~/.virtualenvs lookup skipped hatch-test.* whenever the table existed at all, which hid those envs from the stale-install checks and VEX. It now skips them only when the project defines its own hatch-test matrix. Checked against Hatch 1.18.1's `hatch env show`. Assisted-by: Claude Code:claude-opus-5-5
|
BugBot review Generated by Claude Code |
|
[agent] Every red check on 97ce9b5 is a job GitHub never started. The jobs are cancelled with "The job was not acquired by Runner of type hosted even after multiple attempts", across CI, Bun, Go, vlt, PDM, Pipenv, npm and pnpm, and the queue is backed up for other branches too. vlt I re-ran the failed jobs once in CI, Bun, Go, vlt, PDM and Pipenv. npm and pnpm already had their one re-run at 20:16 and were starved again, so they will re-run on the next push. Nothing in the diff needs to change for these. Generated by Claude Code |
Brings in the three commits that landed on main since the last merge, so CI re-runs on this branch after the runner outage cancelled most of the previous run. Assisted-by: Claude Code:claude-opus-5-5
rustfmt the two files this branch touches so they match the repository's formatting; no behavior change. Assisted-by: Claude Code:claude-opus-5-5
|
BugBot review Generated by Claude Code |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.
Autofix Details
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Parent-relative virtual dirs misclassified
- Normalized env_dir path before the starts_with check by canonicalizing it first, which correctly resolves '..' components and prevents misclassification of parent-relative virtual directories.
Or push these changes by commenting:
@cursor push e7c9282626
Preview (e7c9282626)
diff --git a/crates/socket-patch-core/src/crawlers/hatch_env.rs b/crates/socket-patch-core/src/crawlers/hatch_env.rs
--- a/crates/socket-patch-core/src/crawlers/hatch_env.rs
+++ b/crates/socket-patch-core/src/crawlers/hatch_env.rs
@@ -119,8 +119,8 @@
let Some(env_dir) = virtual_env_dir(&root, config.as_ref(), var) else {
return found;
};
- let in_project = env_dir.starts_with(&root)
- || std::fs::canonicalize(&env_dir).is_ok_and(|d| d.starts_with(&root));
+ let env_dir = std::fs::canonicalize(&env_dir).unwrap_or(env_dir);
+ let in_project = env_dir.starts_with(&root);
let shared_flat = home_dir(var).is_some_and(|h| same_path(&env_dir, &h.join(".virtualenvs")));
for id in project_ids(&root) {
You can send follow-ups to the cloud agent here.
A relative [dirs.env] virtual such as ../envs still started with the project path, so discovery treated it as an in-project flat directory. It then claimed every venv in the parent directory and missed the nested <name>/<id>/<env> envs Hatch actually uses there. Hatch tests `root in data_directory.resolve().parents`, so the directory is now resolved like Python's Path.resolve() first, and the project root itself no longer counts as inside the project. Assisted-by: Claude Code:claude-opus-5-5
|
BugBot review Generated by Claude Code |
canonicalize returns verbatim \\?\ paths on Windows. In those, `/` is not a separator and the OS does not fold `..`, so a virtual = "../envs" joined onto the project root stayed one opaque component. It still counted as inside the project, and reading the directory found nothing. Config paths are now joined component by component, and resolve() folds `.` and `..` before it canonicalizes the longest existing prefix. Assisted-by: Claude Code:claude-opus-5-5
|
BugBot review Generated by Claude Code |
|
[agent] Ready for review at head
Generated by Claude Code |
Bring the Hatch environment discovery branch up to date with main so it can merge cleanly. The only conflict was the crawlers module list, where this branch adds `hatch_env` and main added `ivy_cache`; both modules are kept. Co-Authored-By: Claude <noreply@anthropic.com>
|
bugbot run Generated by Claude Code |
|
bugbot run Generated by Claude Code |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
Bugbot Autofix is ON, but it could not run because the branch was deleted or merged before autofix could start.
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 25c8fbd. Configure here.
| .as_array()? | ||
| .iter() | ||
| .filter_map(|v| v.as_str()) | ||
| .collect(); |
There was a problem hiding this comment.
Non-string matrix values skipped
Medium Severity
matrix_names keeps only TOML strings, so integer or boolean matrix values become no variants. In the shared ~/.virtualenvs layout those names are the only probe keys, so a stale env Hatch actually created is never found and the #335 warning never fires.
Reviewed by Cursor Bugbot for commit 25c8fbd. Configure here.



LLM Description written by Claude Code:claude-opus-5-5
Fixes #335
Summary
On a Hatch project whose environment already exists (any developer checkout,
a warm CI cache), a hosted or vendored scan reported success with no warning.
The next
hatch runkept the unpatched release, andvexattestednot_affected. Socket Patch now finds Hatch's own environments, warns withthe remedy that works (
hatch env remove <env>/hatch env prune), and nolonger attests over an unpatched one.
Root cause
find_local_venv_site_packages(crawlers/python_crawler.rs) knows theout-of-tree envs of Poetry, Pipenv, PDM and uv, but not Hatch's. Hatch never
uses
./.venv. It keeps a project's envs under<data dir>/env/virtual/<project>/<sha256(root)[:8]>/<env>. Every consumerof that discovery was therefore blind to the env
hatch runuses: the hostedredirect_pypi_stale_installprobe, VEX's installed basis, and the agent-modecrawl. The vendored Hatch flavour also never ran a stale-install probe. pip,
and uv before Hatch 1.16, keep a same-version release that is already
installed, and Hatch then records the env as synced, so the env stays
unpatched indefinitely.
Changes
crawlers/hatch_env.rs(new): models Hatch's placement rules, checkedagainst Hatch's source for 1.0, 1.1, 1.2, 1.9 and 1.18:
HATCH_DATA_DIR, thendirs.datainHATCH_CONFIGor the platformconfig file, then the platform data dir (Linux XDG, macOS
Application Support, WindowsLOCALAPPDATA)[dirs.env] virtual(absolute or project-relative; flat when inside theproject or
~/.virtualenvs)pathandHATCH_ENV_TYPE_VIRTUAL_PATHWindows/macOS where newer Hatch does that)
<id>-unmanagedprojects without a[project]table<name>-<id>/<env>layoutConfig reads are FIFO-safe (
read_regular_to_string). Paths spelledthrough a symlink (macOS
/var→/private/var) still match.python_crawler.rs: every existing Hatch env's site-packages is addednext to whatever the generic probes found. That includes when an
unrelated venv is activated, because
hatch runnever uses a foreignvenv.
Hosted (
scan/hosted/python.rs): a stale site inside a Hatch env getsthe Hatch remedy naming the env.
Vendored (
vendor/pypi.rs): the Pipenv-only probe is factored intostale_install_sites. The Hatch flavour judges Hatch's env prefixesdirectly and emits
pypi_hatch_stale_installper stale env, on fresh andin-sync runs alike.
vex: vendoredvendored_tree_out_of_syncalso nameshatch env remove <env>when the project has Hatch envs.Docs:
CLI_CONTRACT.md(new code row, stale-guard paragraph,out-of-sync note) and
docs/testing/hatch.md.Per-issue checklist
redirect_pypi_stale_installwithhatch env remove default;vexfrom the project root does not attest.Covered by
hatch_existing_env_hosted(real Hatch) andhatch_env_gets_the_stale_install_warning_with_hatch_remedy(unit).pypi_hatch_stale_install, plus vex'sout-of-sync disclosure with the Hatch remedy. Covered by
hatch_existing_env_vendored(real Hatch) andhatch_vendor_warns_about_a_stale_hatch_env(unit).layout, unmanaged, FIFO, symlinked spellings, activated foreign venv):
crawlers::hatch_env::tests::*andhatch_out_of_tree_envs_are_project_envs.same place, so they are discovered and judged the same way. The real-Hatch
e2e uses the default pip installer; the warning is installer-agnostic.
Test evidence
Hatch e2e matrix (1.0.0 / 1.2.1 / 1.9.7 / 1.14.2 / 1.18.1, Linux and
macOS), the Linux, macOS and Windows tests, clippy and CodeQL. Bugbot is
clean on this head.
hatch_out_of_tree_envs_are_project_envsfails without thecrawler step.
hatch_vendor_warns_about_a_stale_hatch_envfails without the vendoredprobe (only
vendor_prebuilt_downloaded, the issue's symptom).hatch_existing_env_hosted(real Hatch 1.18.1) fails without the crawlerstep, with
successand no warning. That is the issue's repro.1.18.1. The remedy is executed (
hatch env remove default, thenhatch run) and the env then holds the patched bytes, whichvexattests.
env/virtual/my-app/OuNYZq5s/my-app.cargo fmt --checkis not enforced in CI and main is not fmt-clean.Only this PR's lines were formatted; no unrelated reformatting.
npm/,pypi/,gem/) only dispatch to the binary, so noparallel change is needed.
Follow-ups (not in this PR)
vendored_tree_out_of_syncdetail is per purl, not per site, so itlists every Hatch env of the project rather than only the stale one.
🤖 Generated with Claude Code
https://claude.ai/code/session_018vDNsy9HnaC3kU2vrqc5j6
Note
Medium Risk
Changes Python install discovery and user-facing warnings/VEX attestation for Hatch projects; behavior is additive with broad layout coverage but limited to Hatch/PyPI paths.
Overview
Hatch projects with existing out-of-tree environments are no longer invisible to Python install discovery, stale-install warnings, or VEX. Socket Patch now locates Hatch envs (data dir, config, explicit
path, legacy layouts) and treats theirsite-packageslike other project venvs.Hosted scan extends the
redirect_pypi_stale_installprobe to those envs and recommendshatch env remove <env>/hatch env pruneinstead of “reinstall from the rewritten lock,” because Hatch’s pip/uv sync keeps an already-installed same-version release.Vendored Hatch adds
pypi_hatch_stale_installvia a shared stale-site helper; Pipenv’s stale probe is narrowed to non-Hatch venvs so it does not mis-warn about Hatch trees it cannot fix.vexappends the same Hatch remedy tovendored_tree_out_of_syncwhen the project has Hatch envs.Contract docs and Hatch e2e tests (
existing-envhosted/vendored) document and lock in the new codes and behavior.Reviewed by Cursor Bugbot for commit 25c8fbd. Configure here.
Generated by Claude Code