You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Implementation plan for persisting the T1 artifact, written so a fresh Claude instance (or human) can execute it without further design work. Prereqs all merged: #49 (Problem/constraints as resolve-time data), #50 (T1 = universal per-registry cache; provider has zero query-dependent parameters), plus the caching design notes on #47 (two comments: "cache T1, everything else per-resolve" and the make-style T0/T1 tiering).
What to build
Two cache tiers, make-style dependencies (see #47):
T0, per registry: the parsed provider output for ONE registry — the expensive part (~2.3s parse for General). Keyed by identity header (registry UUID, registry tree hash, T0 format version). A registry changes → re-parse that one T0.
T1, per active registry SET: the pkg_info output for all packages (dependency closure of everything + arc-consistency + interchangeability classes) built from the merged T0s. ~1–1.5s to rebuild registry-wide. Its cache record stores the list of T0 identity headers it was built from plus a T1 builder version. Staleness check = header comparison, no content hashing at check time. Any mismatch or set change → rebuild T1 (cheap, per the numbers in Caching design for Pkg integration: tiers, artifacts, and open questions #47/T1 preprocessing: a universal per-registry cache #50).
Post-#50, the T1 artifact is query-independent (ordering, compat, pins, prereleases, yanked, julia bound are all per-resolve), so ONE artifact per registry state serves every resolve — the correctness model is requirement-subset monotonicity (T1 built for all packages serves any reqs; theory in docs/src/theory/layered.md, "filtering for a larger requirement set" + the rewritten cache-tier paragraph).
Where things are
src/PkgInfoFiles.jl: existing versioned serialization (format v2) for pkg-info dicts — extend, don't replace. Note PkgInfo gained a classes field in T1 preprocessing: a universal per-registry cache #50, recomputed on load via the 4-arg constructor (it's a function of the conflicts matrix). Decision to make: keep recomputing (~0.45s registry-wide, pass 2 of the class scan is 11–15× pass 1) or bump the format and store class ids. Storing is probably right for a cache whose entire point is amortization — measure both, say which won.
bin/Registries.jl: the provider. T0 = its per-registry parse. Today it merges all reachable_registries() up front; the T0 tier needs the parse decomposed per registry with merge-on-load (the merge is cheap; parsing is not). Registry tree hash: Pkg.Registry.RegistryInstance exposes the registry's tree info (reg.tree_info; verify the field name on both supported Pkg versions — bin/ has established patterns for Pkg version differences).
bin/resolve.jl: wiring + a --cache[=dir] flag (default e.g. ~/.julia/scratchspaces/<Resolver-UUID>/t1cache via Scratch-like layout — but bin/ avoids extra deps; a dot-dir under DEPOT_PATH[1] is fine).
Blocker
Whole-registry pkg_info currently crashes on Python_jll's malformed compat entry — see the dedicated issue (#54). Fix that first (one-line tolerance in compat_uuid_pairs).
Enumerate active registries → current T0 identity headers.
T1 record exists AND its stored headers == current headers AND builder version matches → load T1, done.
Else: for each registry whose T0 is stale/missing, re-parse and save T0 (atomic rename); build T1 from T0s; save with the header manifest; return.
Concurrency: write-to-temp + atomic rename for both tiers; a stale reader never sees a torn file. No locking needed beyond that (last writer wins; content is deterministic for identical inputs).
Tests
Roundtrip equality: resolves from a loaded T1 are identical to from-scratch resolves — reuse T1 preprocessing: a universal per-registry cache #50's one-artifact-serves-every-query harness (orderings × admission settings × julia bounds × reqs).
Invalidation: mutate one T0 header → exactly that registry re-parses and T1 rebuilds; unchanged headers → pure cache hits (assert via timing or a parse counter).
Set change: add/remove a registry from the active set → T1 rebuild.
Format version bump → clean rebuild, no crash on old files.
Atomicity: a truncated/corrupt cache file → falls back to rebuild, never errors out of resolve.
Numbers to report
Cold (no cache) vs. warm (T1 hit) vs. registry-bumped (one T0 stale) end-to-end bin/resolve.jl times; cache file sizes (whole-registry T1 was unmeasurable pre-#54 — the DiffEq-closure artifact is 17 MiB in memory as a reference point).
Implementation plan for persisting the T1 artifact, written so a fresh Claude instance (or human) can execute it without further design work. Prereqs all merged: #49 (Problem/constraints as resolve-time data), #50 (T1 = universal per-registry cache; provider has zero query-dependent parameters), plus the caching design notes on #47 (two comments: "cache T1, everything else per-resolve" and the make-style T0/T1 tiering).
What to build
Two cache tiers, make-style dependencies (see #47):
(registry UUID, registry tree hash, T0 format version). A registry changes → re-parse that one T0.pkg_infooutput for all packages (dependency closure of everything + arc-consistency + interchangeability classes) built from the merged T0s. ~1–1.5s to rebuild registry-wide. Its cache record stores the list of T0 identity headers it was built from plus a T1 builder version. Staleness check = header comparison, no content hashing at check time. Any mismatch or set change → rebuild T1 (cheap, per the numbers in Caching design for Pkg integration: tiers, artifacts, and open questions #47/T1 preprocessing: a universal per-registry cache #50).Post-#50, the T1 artifact is query-independent (ordering, compat, pins, prereleases, yanked, julia bound are all per-resolve), so ONE artifact per registry state serves every resolve — the correctness model is requirement-subset monotonicity (T1 built for all packages serves any reqs; theory in docs/src/theory/layered.md, "filtering for a larger requirement set" + the rewritten cache-tier paragraph).
Where things are
src/PkgInfoFiles.jl: existing versioned serialization (format v2) for pkg-info dicts — extend, don't replace. NotePkgInfogained aclassesfield in T1 preprocessing: a universal per-registry cache #50, recomputed on load via the 4-arg constructor (it's a function of the conflicts matrix). Decision to make: keep recomputing (~0.45s registry-wide, pass 2 of the class scan is 11–15× pass 1) or bump the format and store class ids. Storing is probably right for a cache whose entire point is amortization — measure both, say which won.bin/Registries.jl: the provider. T0 = its per-registry parse. Today it merges allreachable_registries()up front; the T0 tier needs the parse decomposed per registry with merge-on-load (the merge is cheap; parsing is not). Registry tree hash:Pkg.Registry.RegistryInstanceexposes the registry's tree info (reg.tree_info; verify the field name on both supported Pkg versions — bin/ has established patterns for Pkg version differences).bin/resolve.jl: wiring + a--cache[=dir]flag (default e.g.~/.julia/scratchspaces/<Resolver-UUID>/t1cachevia Scratch-like layout — but bin/ avoids extra deps; a dot-dir underDEPOT_PATH[1]is fine).Blocker
Whole-registry
pkg_infocurrently crashes onPython_jll's malformed compat entry — see the dedicated issue (#54). Fix that first (one-line tolerance incompat_uuid_pairs).API sketch
Concurrency: write-to-temp + atomic rename for both tiers; a stale reader never sees a torn file. No locking needed beyond that (last writer wins; content is deterministic for identical inputs).
Tests
Numbers to report
Cold (no cache) vs. warm (T1 hit) vs. registry-bumped (one T0 stale) end-to-end
bin/resolve.jltimes; cache file sizes (whole-registry T1 was unmeasurable pre-#54 — the DiffEq-closure artifact is 17 MiB in memory as a reference point).🤖 Opened with Claude Code at Stefan's request.
https://claude.ai/code/session_016p2sckwc5Gjjg5LG6gdEk7