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
The agreed sequencing, kept here so the order lives somewhere findable. One-line rationale each; details in the linked issues.
Current as of main at a595e04 (2026-09-12).
Where this stands
The resolver and its diagnostics are complete. What is left is not core work: it is a cache, two query forms, and the mechanics of shipping.
Resolve — layered descent over interchangeability classes, lexicographic optimality against a per-package preference order, relaxation-stable filtering. Done and heavily differential-tested.
The three explanation issues are one feature between them: pkg> why needs goals for presence answers and holdbacks for version answers, and the toolkit is how Pkg renders either in its own voice.
Done
Everything through #116. The load-bearing ones, newest first:
Ask upstream for the release that would fix a conflict #114 — ask upstream for the release that would fix a conflict. A conflict may now name a maintainer request, under a bar high enough that the sentence is sendable as it stands: a hypothetical release modelled in place (Lemma 32), three qualification conditions, and a verified witness. Deliberately scoped to bounds meeting the user's own constraint — one release, one bound, one addressee — so it never judges which of two maintainers is at fault.
The remaining feature work is one deliverable in three parts. pkg> why needs goals for presence answers and holdbacks for version answers, and the toolkit is how Pkg renders either in its own voice — so these are ordered by build cost, cheapest first, not by importance.
Diagnostics should explain steering surprises: why a resolve was held back to an older julia (or package) version #52 — holdbacks. Why a package resolved below its best, and what to relax to move it. The cheapest of the three: the machinery is exactly what a conflict already uses, the proof checker verifies a holdback's lines with no changes, and the archived prototype supplies a scenario list. Closes the last asymmetry — a failed resolve gets a verified proof, a surprising success gets one ⊼ character.
Goals: resolve(…; with, without) — ask whether some solution has a property #112 — goals.resolve(…; with = X, without = X). More expensive than holdbacks because it is not just a query: a goal asks whether some solution has a property rather than about optimal ones, so reachability and redundancy are unsound for it. Carries theory (Proposition G, plus a counterexample in each direction) that should survive the port intact.
Presentation toolkit: report() knobs, changes(), and --explain #113 — the presentation toolkit.report(io, d; knobs), changes(sol₁, sol₂), --explain, the guide section. Last because the structured surface is already public and documented, so Pkg can render its own reports today — and because every knob the prototype chose now aims at something that no longer exists. Best written against a real Pkg renderer.
Prior art for all three
archive/holdbacks-prototype is a pushed archive of a local branch that carried working prototypes of all three: holdbacks (c9e8b0c), goals (1539af3) and the toolkit plus --explain (3993297). Each is now captured as an issue with a build plan — #52, #112, #113 — and each issue names its own commit.
All three target src/Diagnose.jl and the Fact/UserCompat/Bound vocabulary that #105 replaced with clauses, and their tests assert wording that has since been retired: restricts for what is now leaves, and the symmetric "works with … only at" that directed verbs replaced. Read them for structure, theory and edge cases; re-derive every string.
They are not equally salvageable. Goals carries theory that should survive intact; holdbacks carries structure and a scenario list; the toolkit is mostly archaeology, since every knob it proposed aims at something main no longer has.
One thing found while capturing them: the prototype's upstream suggestions were never merged — but #114 has since shipped a better-founded version of the same idea, verified and narrowly scoped. What the prototype had that main still lacks is the knobs, not the feature.
Items 2 and 3 are what pkg> why needs. Presence answers come from goals, version answers from holdbacks; until both exist, why cannot be built on this.
T1 persistence: save/load/invalidate the universal registry cache (implementation plan) #55 — persistence. Demoted from first on the list above, after measuring what a resolve pays. It is not a parity gap — Pkg does not cache parsed registry data either, and bin/ uses Pkg's own lazy per-package parse — and the plan's headline numbers are whole-registry figures the normal path never touches. The cacheable work is 105–520 ms per query warm; the eager ~4 s of process setup is julia_versions.json and the stdlib tables, which are registry-independent and which a registry cache would not touch. Gated on separating work from JIT in that 4 s, which would also say whether the thing worth caching is the registry data at all.
Settled by measurement
Both of the measurements this section used to list have been taken, and they went opposite ways — which is the point of taking them.
Redundancy is conservative for relaxations' sake; shadows might let it be aggressive #91 — closed. The aggressive i < j rule strikes zero extra classes over Rule A, across fifteen configurations including every package of a closure bounded below its unconstrained pick. Both rules were already in mark_necessary!, selected by one argument, so this cost a script rather than an implementation. A negative control confirms the harness detects a difference where one exists. The conservatism buys relaxation-stability for free.
Release
Worth stating plainly, because the mechanics have never been exercised: there are no tags, no GitHub releases, and Resolver is not registered in General.Project.toml says 0.2.0 and TagBot.yml is configured, but the path from here to an installable package has not been walked once.
Nothing in the code argues against releasing. CI is green across 1.9–1.13 plus nightly on four platforms, the suite includes registry-scale differential tests, and bin/resolve.jl has a production consumer in julia-downgrade-compat. The README's "experimental and research-stage" framing predates the diagnosis work and undersells what is here.
The one substantive question a release should settle is what is public. docs/src/api.md currently documents the diagnosis surface, the clause algebra and a block frankly labelled "Internals" (PkgInfo, prepare_pkg_info, exclusion_masks, find_reachable, …). Anything documented is something a user may reasonably pin to. Deciding which of those are 0.x-stable, and which should be marked as internal in a way that permits change, is cheaper before a release than after.
The Pkg test drive
Still not scheduled against a PR — but the calculus has changed. The unsatisfiable path is ready to review now: the API is stable, the reports are verified, the modelling conventions on #62 are settled.
JuliaLang/Pkg.jl#4738 targets the #30 API, three generations stale, and should be rewritten rather than rebased. KristofferC's feature-flag rollout — new resolver as default, old one retained — is unchanged and still right.
Principle behind the order
Structure before what is built on it; representation before persistence; performance and polish only where evidence points.
One addition, earned rather than assumed: where an issue's own text says "measure first", the measurement is the work item, and no successor is scheduled until the number exists. #60 and #91 were the test of it, and they went opposite ways — #60's penalty was real and got implemented, #91's payoff was exactly zero and got closed. Neither outcome was predictable from reading the issue, and #91's measurement in particular cost a script, because both rules turned out to be in the tree already.
#118 added a second lesson, cheaper than a measurement: check the premise before scheduling the gate. It was closed on 2026-09-15 without its measurement ever being run, because the mechanism it named was not the one at work — upstream fixes read PkgData.compat directly, so direction was never missing; what declines the registry-versus-registry case is a deliberate refusal to judge between two maintainers, which a direction bit does not resolve. The measurement would have counted the right rejections for the wrong reason.
The agreed sequencing, kept here so the order lives somewhere findable. One-line rationale each; details in the linked issues.
Current as of
mainata595e04(2026-09-12).Where this stands
The resolver and its diagnostics are complete. What is left is not core work: it is a cache, two query forms, and the mechanics of shipping.
What is open, in full
Short enough now to list. Everything else is closed.
--explainThe three explanation issues are one feature between them:
pkg> whyneeds goals for presence answers and holdbacks for version answers, and the toolkit is how Pkg renders either in its own voice.Done
Everything through #116. The load-bearing ones, newest first:
bin/resolves on it, with a manifest of its own and the stdlib check extended to cover it. The staleness guards from Fix stale stdlib snapshot, and notice the next one (#103) #110 did their job: the missing manifest was reported rather than silently falling back to 1.9's pins.:compat,:pin, custom);issatisfiableis the exported spelling andsat_solveis the internal one-argument form.HistoricalStdlibVersionspin that hadbin/resolve.jlresolving against a Julia 1.12 that never existed, and added the checks that catch the next one.Next
The remaining feature work is one deliverable in three parts.
pkg> whyneeds goals for presence answers and holdbacks for version answers, and the toolkit is how Pkg renders either in its own voice — so these are ordered by build cost, cheapest first, not by importance.⊼character.resolve(…; with = X, without = X). More expensive than holdbacks because it is not just a query: a goal asks whether some solution has a property rather than about optimal ones, so reachability and redundancy are unsound for it. Carries theory (Proposition G, plus a counterexample in each direction) that should survive the port intact.report(io, d; knobs),changes(sol₁, sol₂),--explain, the guide section. Last because the structured surface is already public and documented, so Pkg can render its own reports today — and because every knob the prototype chose now aims at something that no longer exists. Best written against a real Pkg renderer.Prior art for all three
archive/holdbacks-prototypeis a pushed archive of a local branch that carried working prototypes of all three: holdbacks (c9e8b0c), goals (1539af3) and the toolkit plus--explain(3993297). Each is now captured as an issue with a build plan — #52, #112, #113 — and each issue names its own commit.All three target
src/Diagnose.jland theFact/UserCompat/Boundvocabulary that #105 replaced with clauses, and their tests assert wording that has since been retired:restrictsfor what is nowleaves, and the symmetric "works with … only at" that directed verbs replaced. Read them for structure, theory and edge cases; re-derive every string.They are not equally salvageable. Goals carries theory that should survive intact; holdbacks carries structure and a scenario list; the toolkit is mostly archaeology, since every knob it proposed aims at something
mainno longer has.One thing found while capturing them: the prototype's upstream suggestions were never merged — but #114 has since shipped a better-founded version of the same idea, verified and narrowly scoped. What the prototype had that
mainstill lacks is the knobs, not the feature.Items 2 and 3 are what
pkg> whyneeds. Presence answers come from goals, version answers from holdbacks; until both exist,whycannot be built on this.Evidence-gated, not scheduled
bin/uses Pkg's own lazy per-package parse — and the plan's headline numbers are whole-registry figures the normal path never touches. The cacheable work is 105–520 ms per query warm; the eager ~4 s of process setup isjulia_versions.jsonand the stdlib tables, which are registry-independent and which a registry cache would not touch. Gated on separating work from JIT in that 4 s, which would also say whether the thing worth caching is the registry data at all.Settled by measurement
Both of the measurements this section used to list have been taken, and they went opposite ways — which is the point of taking them.
i < jrule strikes zero extra classes over Rule A, across fifteen configurations including every package of a closure bounded below its unconstrained pick. Both rules were already inmark_necessary!, selected by one argument, so this cost a script rather than an implementation. A negative control confirms the harness detects a difference where one exists. The conservatism buys relaxation-stability for free.Release
Worth stating plainly, because the mechanics have never been exercised: there are no tags, no GitHub releases, and Resolver is not registered in General.
Project.tomlsays0.2.0andTagBot.ymlis configured, but the path from here to an installable package has not been walked once.Nothing in the code argues against releasing. CI is green across 1.9–1.13 plus nightly on four platforms, the suite includes registry-scale differential tests, and
bin/resolve.jlhas a production consumer injulia-downgrade-compat. The README's "experimental and research-stage" framing predates the diagnosis work and undersells what is here.The one substantive question a release should settle is what is public.
docs/src/api.mdcurrently documents the diagnosis surface, the clause algebra and a block frankly labelled "Internals" (PkgInfo,prepare_pkg_info,exclusion_masks,find_reachable, …). Anything documented is something a user may reasonably pin to. Deciding which of those are 0.x-stable, and which should be marked as internal in a way that permits change, is cheaper before a release than after.The Pkg test drive
Still not scheduled against a PR — but the calculus has changed. The unsatisfiable path is ready to review now: the API is stable, the reports are verified, the modelling conventions on #62 are settled.
JuliaLang/Pkg.jl#4738 targets the #30 API, three generations stale, and should be rewritten rather than rebased. KristofferC's feature-flag rollout — new resolver as default, old one retained — is unchanged and still right.
Principle behind the order
Structure before what is built on it; representation before persistence; performance and polish only where evidence points.
One addition, earned rather than assumed: where an issue's own text says "measure first", the measurement is the work item, and no successor is scheduled until the number exists. #60 and #91 were the test of it, and they went opposite ways — #60's penalty was real and got implemented, #91's payoff was exactly zero and got closed. Neither outcome was predictable from reading the issue, and #91's measurement in particular cost a script, because both rules turned out to be in the tree already.
#118 added a second lesson, cheaper than a measurement: check the premise before scheduling the gate. It was closed on 2026-09-15 without its measurement ever being run, because the mechanism it named was not the one at work — upstream fixes read
PkgData.compatdirectly, so direction was never missing; what declines the registry-versus-registry case is a deliberate refusal to judge between two maintainers, which a direction bit does not resolve. The measurement would have counted the right rejections for the wrong reason.🤖 Maintained with Claude Code.