Skip to content

fix(worktree): provision a worktree venv against constraints.lock, like CI does - #68

Merged
wshallwshall merged 1 commit into
mainfrom
claude/worktree-venv-constraint
Jul 30, 2026
Merged

fix(worktree): provision a worktree venv against constraints.lock, like CI does#68
wshallwshall merged 1 commit into
mainfrom
claude/worktree-venv-constraint

Conversation

@wshallwshall

Copy link
Copy Markdown
Collaborator

Every install in ci.yml passes --constraint constraints.lock, and ci.yml's own comment says why: "Without it, uv pip install -e ".[extras]" RE-RESOLVES". scripts/worktree/new.ps1 was the one place that omitted it, so a fresh worktree resolved freely inside pyproject's ranges and could disagree with CI on any tool.

Not hypothetical — it cost most of a session

A worktree created 2026-07-29 came up with ruff 0.16.0 while constraints.lock pinned 0.15.22 (installed past pyproject's <0.16 cap). Two consequences, and the second is why this is a guard rather than a tidy-up:

  • it reported ~829 findings CI does not have (0.16 turned on stricter defaults), so "lint is red" meant nothing; and
  • the ruff-check pre-commit hook runs ruff check --fix, so it rewrote files to match — stripping # noqa directives the pinned ruff still wants.

A venv that lints differently from CI doesn't merely mislead; it edits your source on commit.

The same worktree was also missing pytest-asyncio and pytest-rerunfailures, so every async test ERRORED and a "154 passed" run silently covered only the non-async files. Constraining the install doesn't by itself fix a partial install, but it removes the version half of that class.

The guard

tests/test_worktree_venv_constraint.py pins it three ways:

  1. every editable project install in new.ps1 carries --constraint;
  2. constraints.lock actually exists and parses to a plausible number of pins — a flag pointing at a missing or near-empty file constrains nothing, which is the same outcome as omitting it, and would only surface on a developer's first run;
  3. ci.yml still constrains too. Asserted in that direction deliberately: the property is "developer provisioning agrees with CI provisioning", not "new.ps1 contains a flag". If CI ever moves off constraints.lock, this fails and forces the two to be reconciled rather than drifting apart again.

Verification

Both regressions injected:

injected caught message
revert to bare pip install -e ".[extras]" "WITHOUT --constraint"
delete the install line (vacuity trap) "no longer contains an editable project install"

A text test because nothing in the suite can run new.ps1 — it creates a git worktree and a venv, minutes of I/O, and mutates the repo's worktree list. The invariant is a property of the command, so it's asserted against the command; same posture as tests/test_ci_venv_pinning.py, which pins workflow install lines no test can execute either.

77 passed across the worktree, scaffold, gate-wiring and DEP-1 lockstep suites.

…ke CI does

Every install in ci.yml passes `--constraint constraints.lock`, and ci.yml's own comment
says why: "Without it, `uv pip install -e ".[extras]"` RE-RESOLVES". scripts/worktree/
new.ps1 was the one place that omitted it, so a fresh worktree resolved freely inside
pyproject's ranges and could disagree with CI on any tool.

Not hypothetical -- it cost most of a session. A worktree created 2026-07-29 came up
with ruff 0.16.0 while constraints.lock pinned 0.15.22 (installed past pyproject's
`<0.16` cap). Two consequences, and the second is why this is a guard rather than a
tidy-up:

  * it reported ~829 findings CI does not have (0.16 turned on stricter defaults), so
    "lint is red" meant nothing; and
  * the ruff-check pre-commit hook runs `ruff check --fix`, so it REWROTE files to
    match -- stripping `# noqa` directives the pinned ruff still wants.

A venv that lints differently from CI does not merely mislead, it edits your source on
commit. The same worktree was also missing pytest-asyncio and pytest-rerunfailures, so
every async test ERRORED and a "154 passed" run silently covered only the non-async
files. Constraining the install does not by itself fix a partial install, but it
removes the version half of that class.

tests/test_worktree_venv_constraint.py pins it three ways:

  1. every editable project install in new.ps1 carries --constraint;
  2. constraints.lock actually EXISTS and parses to a plausible number of pins -- a
     flag pointing at a missing or near-empty file constrains nothing, which is the
     same outcome as omitting it, and would only surface on a developer's first run;
  3. ci.yml still constrains too. Asserted in that direction deliberately: the property
     is "developer provisioning agrees with CI provisioning", not "new.ps1 contains a
     flag". If CI ever moves off constraints.lock this fails and forces the two to be
     reconciled rather than drifting apart again.

Proven by injecting both regressions:

  revert to the bare `pip install -e ".[extras]"`  -> caught, "WITHOUT --constraint"
  delete the install line entirely (vacuity trap)  -> caught, "no longer contains an
                                                      editable project install"

A text test because nothing in the suite can run new.ps1 -- it creates a git worktree
and a venv, minutes of I/O, and mutates the repo's worktree list. The invariant is a
property of the COMMAND, so it is asserted against the command; same posture as
tests/test_ci_venv_pinning.py, which pins workflow install lines no test can execute
either.

77 passed across the worktree, scaffold, gate-wiring and DEP-1 lockstep suites.
@wshallwshall
wshallwshall enabled auto-merge (squash) July 30, 2026 15:00
@wshallwshall
wshallwshall merged commit 36de3b7 into main Jul 30, 2026
32 checks passed
@wshallwshall
wshallwshall deleted the claude/worktree-venv-constraint branch July 30, 2026 15:22
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.

1 participant