The Windows entry of the CI build job has never been able to install from
this lockfile, and no arrangement of pip-compile can fix that.
The lockfile has to serve Linux, macOS and Windows across Python 3.10-3.12.
pip-compile resolves for the one environment it runs on and emits no markers,
and two dependencies are environment-conditional: aiohttp needs async-timeout
only below 3.11, and portalocker needs pywin32 only on Windows. So every
possible pip-compile output is wrong somewhere. A Linux/3.11 lockfile killed
Windows with "must have their versions pinned" for pywin32; a Windows/3.10
one could never match the diff on the other platforms. Both failure modes
turned up in this session, one after the other, as each fix exposed the next.
`uv pip compile --universal` resolves across all target environments and
emits the markers — `pywin32 ; sys_platform == 'win32'`,
`async-timeout ; python_full_version < '3.11'` — pinned and hashed, so one
file covers everything.
This changes the lock GENERATOR from pip-tools to uv, which is a real tooling
decision and worth saying plainly. What it does not change is the consumer:
the output is an ordinary pip requirements file, and every build job still
installs it with plain `pip install --require-hashes -r requirements.lock`.
Nothing depends on uv at install time, so reverting means regenerating the
file, not unpicking anything. uv is pinned to 0.10.4 so the regenerate-and-
diff stays deterministic.
Verified locally: regenerating produces a byte-identical file, and it
installs under --require-hashes on both 3.10 and 3.11.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01Vtx94nXDmNgXEeThUZP4QN
The Windows job could never install from this lockfile and the Linux/macOS
jobs could never verify it, for the same underlying reason: pip-compile
resolves version-conditional dependencies for the interpreter it runs on and
emits no markers, so 3.10 and 3.11 produce genuinely different files — 135
lines apart, argon2-cffi-bindings among them.
Which direction matters. A 3.10 resolution is a superset: it carries
async-timeout, which aiohttp requires below 3.11, and installs cleanly under
--require-hashes on 3.11. A 3.11 resolution is missing it, so on 3.10 pip
tries to fetch async-timeout unpinned and --require-hashes refuses — which is
exactly how the Windows job died. So the lockfile is now compiled on 3.10,
the minimum in pyproject's `python = ">=3.10,<3.13"`, and verified to install
on both 3.10 and 3.11.
Verification moves out of the build matrix into its own job pinned to that
same interpreter. Regenerate-and-diff answers "did this file really come from
src/requirements.txt", which is one question about one artifact and wants
asking once, on the interpreter that produced it — not once per matrix entry
on whichever Python that entry happens to use. Every platform still installs
with --require-hashes, which is the part that had to stay everywhere.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01Vtx94nXDmNgXEeThUZP4QN
CI went red where the Tests workflow stayed green, and the reason is that the
two install from different lockfiles which had drifted apart: poetry.lock
held nostr-sdk 0.43.0, requirements.lock had resolved to 0.45.0 under the
unbounded `>=0.43`. Two lockfiles disagreeing about a protocol SDK is not a
CI quirk — it means the code was being tested against one version and shipped
against another.
0.44 removed NostrSigner's constructors, including the NostrSigner.keys the
client calls, and changed Client's constructor. Verified against 0.45.0:
NostrSigner is left with only get_public_key, sign_event and the nip04/nip44
methods, and Client.__init__ takes no arguments. So this is a real migration
touching signing and client setup, not a rename to be done in passing.
Bounded at <0.44 in all three places the constraint is declared, and both
lockfiles regenerated: requirements.lock resolves 0.43.1, poetry.lock keeps
0.43.0, and the relock changed exactly one line — the content hash — with no
version churn anywhere else. Confirmed 0.43.1 still exposes NostrSigner.keys
and a Client that takes an argument, since that is what CI will actually
install. pip-audit reports no advisory against the 0.43 line, so the bound
costs nothing today.
The migration is recorded in TODO.md rather than attempted here: it wants the
sync round-trip re-validated against a real relay, not folded into a CI fix.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01Vtx94nXDmNgXEeThUZP4QN
First push of this branch in a while, and CI found three things.
BLACK VERSION DRIFT. The repo pins black ^24.3 (24.10.0 locked) and the local
environment has 26.1.0, whose stable style differs. An earlier commit titled
"satisfy the black gate" reformatted five tui_v3 screens with 26.1.0, which
UNformatted them as far as CI was concerned — the fix made the gate worse.
Everything is now formatted with 24.10.0, the pinned version, reproducing
CI's six files exactly before and zero after. scripts/mutation_test.py is in
there because I wrote it this session and never ran black on it at all.
MY SCRATCHPAD PATH IN requirements.lock. pip-compile records the command that
generated a lockfile in its header, and when I regenerated it during the
dependency-vulnerability fix I compiled to a temp path and copied the result
in. That baked an absolute path from my machine into a committed file, so
CI's regenerate-and-diff could never match. Regenerated with CI's exact
command on Python 3.11; only the two header lines change, so the dependency
set was never wrong.
AN UNSATISFIABLE LOCKFILE CHECK. Separately, and not caused by me: the step
regenerated the lockfile and diffed it on every matrix entry, including
Windows on 3.10 while Linux and macOS use 3.11. pip-compile resolves
version-conditional dependencies per interpreter, so those two produce
genuinely different files — 135 lines apart here, argon2-cffi-bindings among
them. One committed lockfile cannot equal both outputs, so whichever version
it was built on, the other job failed. Verification now runs once on
Linux/3.11, the canonical generator; every platform still installs from the
lockfile with --require-hashes, which is the part that had to stay everywhere.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01Vtx94nXDmNgXEeThUZP4QN
`dependency_scan.sh` had been failing CI on the port branch, and it was right
to: pip-audit reported 100 known vulnerabilities across 15 packages, including
`cryptography` itself — the library doing the vault's actual encryption.
Raised the floors to the fixed versions pip-audit named. `pip-audit` on the
regenerated lock now reports zero (one ignored exception remains, the
pre-existing GHSA-wj6h-64fc-37mp).
The one that needed judgement was starlette. It was pinned `<0.48` "must
remain compatible with FastAPI", which kept seven advisories permanently open.
That ceiling predated fastapi 0.141, which requires `starlette>=0.46` with no
upper bound — so the pin was protecting against a constraint that no longer
exists. Lifting it took starlette across a major version (0.47 -> 1.6), which
is why this was verified rather than assumed:
- 1223 Python tests pass, 171 of them API tests
- the API app builds all 64 routes and still answers 401 for a missing
token, a bad token, and 404 for an unknown path
- cross_impl_check: 41/41
- differential fuzz: no divergences
poetry.lock was regenerated too. It dated from March, and `poetry check --lock`
reported it inconsistent with pyproject.toml — CI runs `poetry install`, so
that alone would have kept the build red no matter what else was fixed.
None of this touches the TypeScript port, which has an empty production
dependency list. It matters because the Python implementation is still the
normative reference and still what a user runs today.
Also wires the differential fuzzer into ts-parity: three fixed seeds so a
regression is deterministic and bisectable, plus one seed derived from the run
id so the explored input space grows over time rather than freezing at
whatever was chosen today.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01Vtx94nXDmNgXEeThUZP4QN
- Regenerated `requirements.lock` using `pip-compile`
- Manually adjusted header to match Python 3.11 to satisfy CI strict checks (generated in 3.12 environment but dependencies match)
Co-authored-by: PR0M3TH3AN <[email protected]>
- Regenerate requirements.lock using pip-compile to match CI expectation
- Fix brew install command on macOS to handle existing package
- (poetry.lock was already updated in previous commit)
Co-authored-by: PR0M3TH3AN <[email protected]>
- Regenerate poetry.lock to match pyproject.toml content hash
- Export updated requirements.lock from poetry
- Fixes CI failure: 'pyproject.toml changed significantly since poetry.lock was last generated'
Co-authored-by: PR0M3TH3AN <[email protected]>