package manager: load two majors of one package side by side #324
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context
Slice 2 of 3 of #28. After the resolver and lock can hold two majors, the workspace and the checker still identify a package by its name (
workspace.packageNamed,usespecifier lookup), so the two majors cannot be loaded side by side.Design
docs/design/PROJECT.md section 6a: "two majors would need a package identity of name and major and per-package resolution of
usespecifiers".Scope
compiler/src/project/workspace.trb: load every locked package, identity(name, major); thePackagelist may hold two entries of one name.compiler/src/semantics/scope.trb,graph.trb):use ... from "acme/json"resolves to the major that the importing package's owndependenciesline requires. Source files stay unchanged (no version in the specifier); a package has one requirement per name, so the choice is unambiguous. Packages looked up by name for the toolchain (std/*, prelude) keep their single version.acme/json1Valueis not anacme/json2Value).Acceptance criteria
xandy,xdepending onacme/json ^1andyonacme/json ^2, checks without errors; each resolves its ownuse "acme/json".Seed impact
None expected (no syntax, native or driver flag; the compiler looks up
std/*by name only, which keep one version). If the work finds a string lookup of a package name that must become major-aware in the seed path, state it in the PR under the two-commit rule.Dependencies
Needs slice 1 (#323). Size: one PR (
feat(project): load two majors of one package side by side).[loop:torbscript] refine: slice 2 of 3 of #28, specified; next: waiting (dependency #323)
[loop:torbscript] claim refine by session:loop-torbscript-20261013 until 2026-10-13T09:07Z
[loop:torbscript] decided by the loop: refinement of the scope after reading
main(e1d13b07landed #323). No owner question: this is the internal identity of a package in the toolchain, the language and the published contract do not change, and single-major graphs behave as before. The points below add to the issue text and win where they differ from it.Where the code stands (checked on
main)Workspace.packageNamed(name)returns the first package of that name;withLockedPackages(compiler/src/project/workspace.trb) skips a locked entry whenpackageNamed(locked.name)is already there, so the second major of a name is dropped today.resolveImport(compiler/src/semantics/graph.trb) is the one choke point forusespecifiers (scope, checker, language server and the reachability walk all call it), pluspackageIndexNamedinprogramPackagesOf/noteImports/the reached-package walk, which also look a package up by name only.describeTypewrites the bare symbol name, so a mismatch reads "expectedValue, foundValue".Decisions
(name, version)of thePackage, no new field. An installed registry package already carries its version inmanifest.version(from its own lock'ssettings). Two entries of one name can only come from the registry: asourceline (path:,archive:,git:) names one location per package. So the fixture uses two locked registry packages, not path packages (the acceptance criterion below says so).withLockedPackageskeys the "already there" check by name and exact locked version, so both majors are added. The lock-side checks of the same file (lock.locked(name)for "a dependency of a locked package the lock lacks", and the twosourcechecks) use the importer's requirement (lock.lockedWithin) instead of the first entry.Workspace(design rule: methods, no free wrapper), for exampledependencyOf(importer: Package, name: String): Package?: the candidates are the packages of that name; with one candidate it is that one (allpath:,archive:,git:and single-major cases, so nothing changes there); with several, the importer's own requirement line for the name (manifest.runtimeRequirementsthendevelopmentRequirements, for members and for installed packages alike, since a package's locksettingscarry theruntimelines) picks the candidates whose version it allows. Tie rule: if a requirement spanning majors (>=1 <3) allows both locked majors, the highest wins, which is what the resolver prefers (PROJECT.md 6a). If the importer has no requirement for it, the first candidate (the lookup was by name before).packageNamedstays for the toolchain lookups (std/*, preludes,std/project,std/sandbox,std/encoding), which keep one version.resolveImport,noteImports,programPackagesOfand the reached-package walk go through the new method with the importing package, so the closed world of a program contains the majors its packages actually reach (both, when both are reached), andisAvailablestays "the importer depends on the name".acme/json 2.x):NotADependency,NoModuleInPackageand the "has no module" text (theImportcases carry the label). Single-major messages stay byte-identical.acme/json 1.x Value,acme/json 2.x Value); everything else stays as it is. Implementation is the implementer's call (a check in the one place that builds the "expected X, found Y" text, using the symbol's module to reach the package), no change ofdescribeType's other 8 callers.ir/instantiate.trb(SymbolPath package.name) and the mangling inir/lowerstill identify a package by name until #325, so two majors would collide silently inbuild/run. This PR adds a guard at the entry of lowering: a program that reaches two packages of one name is refused withacme/json is in the program in two majors, which build and run cannot keep apart yet (#325);checkis unaffected. #325 removes the guard. The guard is the second commit if the first gets large.stdname: thestd/*lookups stay by name). The PR states "seed impact: none".Acceptance criteria (replaces the list in the issue)
lockedTreeincompiler/tests/packages.test.trb): a project with members/locked packagesx(acme/json:^1) andy(acme/json:^2), a lock withacme/json1.x and 2.x, both unpacked in the cache:findWorkspacehas no problems and holds twoacme/jsonpackages (1.x and 2.x); a lock missing either major still namestorb update.xandyeachuse "acme/json"and resolve to their own major (different module ids, different files);torb checkis clean.Valuepassed where the major-2Valueis expected is a type error whose text names both (acme/json 1.x Value,acme/json 2.x Value); two same-named types of one major still print bare.>=1 <3) with both locked resolves to the higher one (workspace test).build/runof the fixture refuses with the guard message (test), never produces a binary.skills/regenerated if the docs change requires it.Size: one PR, two Conventional Commits:
feat(project): load two majors of one package side by side(workspace, graph, scope diagnostics, tests) andfeat(checker): name both majors in a type mismatch and refuse to lower them(items 6 and 7, docs). Touches compiler sources anddocs/design/PROJECT.md, notseed/. Implementer: opus/high (checker).[loop:torbscript] refine: read
mainafter #323, checked the identity anduselookup sites, specified the per-importer lookup, the diagnostics and the mismatch printing, added a guard against a silent miscompile until #325, replaced the acceptance criteria (see the decided-by-the-loop comment above); no owner question, one PR in two commits, no seed impact. Labelready. Worker tokens: ~120k, next: implement[loop:torbscript] claim implement by session:loop-torbscript-20261013 until 2026-10-13T13:10Z
[loop:torbscript] release implement: the implement worker session was killed at ~07:14Z (gateway restart, no push, no branch, nothing to recover); respawning a fresh worker
[loop:torbscript] claim implement by session:loop-torbscript-20261013 until 2026-10-13T15:50Z
[loop:torbscript] implement: PR #360 (two commits,
Closes #324), forge CI green (gates, bootstrap, tier A/B, same C on every host, playground). Two majors are two packages with a per-importer lookup (Workspace.dependencyOf), the checker names both majors in a mismatch, and lowering refuses with the #325 message so no binary is produced. Docs: PROJECT.md 6a and RELEASE.md;skills/needed no regeneration (docs skill --checkgreen). Seed impact: none. Tokens: about 400k in this worker session, including two CI fix rounds (call-form parse errors, formatter). next: review