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
design: D8 withdrawn — the merge already does it, and the defect is the provisioning scope
merge_workspace_into_ assigns rather than inserts, so the later layer wins: in
Anonymous mode the project's declaration beats the machine's, and in Named mode
the machine's layer is not merged at all. A project declaring a version
therefore resolves to it without anything forcing a switch, and the developer
picks the strength by naming a SubOS or not.
What is wrong today is one argument. Provisioning calls with make_xlings_env,
which carries no projectDir, so the child runs in global scope: the install's
records land in the registry's shared workspace while programs invoked from the
project resolve through the project's layers. That disagreement is the
'not installed in this subos' line the 2026.9.2.1 verification measured and the
reason the runner needed a payload lookup.
So the change is make_project_xlings_env, and useAfterInstall is not passed and
no use_version call is added. Two consequences are written down rather than
left to be discovered: the project SubOS layer outranks the manifest, and an
uninstalled declared version already fails with wording that names the project.
(`prepare.cppm:3421`), and `make_xlings_env` carries no `projectDir`
791
795
(`src/config.cpp:129`), so no `XLINGS_PROJECT_DIR` reaches the child and the
792
796
cwd walk finds nothing — mcpp writes `<project>/.mcpp/.xlings.json`, one level
793
-
below the directory a walk from the project root inspects. The child therefore
794
-
runs in **global** scope against mcpp's registry home, where
795
-
`Config::workspace_mut()` is the global workspace and `effective_workspace()`
796
-
is the global workspace.
797
-
798
-
Three consequences follow, and they explain measurements this repository
799
-
already has:
800
-
801
-
1.`active` is read from a workspace shared by **every mcpp project on the
802
-
machine**, so a version another project activated is enough to make this
803
-
project's declaration lose.
804
-
2. Forcing the switch there would let two checkouts flip each other. The
805
-
decline is protective in this scope, not obstructive.
806
-
3. The install's view and the shim's view disagree. The install activates in
807
-
the registry's global workspace; a program invoked from the project
808
-
directory resolves through the project's own layers. That is exactly the
797
+
below the directory a walk from the project root inspects. The child runs in
798
+
**global** scope against mcpp's registry home.
799
+
800
+
That single fact produces every symptom in this section:
801
+
802
+
1. The install's records land in the registry's global workspace, while a
803
+
program invoked from the project resolves through the project's layers. The
804
+
two views disagree, which is the
809
805
`qemu-aarch64-static is not installed in this subos (_)` measured during the
810
-
2026.9.2.1 verification, and the reason mcpp had to add a payload-directory
811
-
lookup for the runner at all (§13.2 of the runner design).
812
-
813
-
### 16.3 The two designs
814
-
815
-
**Design P — provision in the project's environment.** mcpp calls with
816
-
`make_project_xlings_env(cfg, root)`, so `XLINGS_PROJECT_DIR=<project>/.mcpp`
817
-
and the child runs in project scope. `Config::workspace_mut()` is then the
818
-
project's own SubOS workspace at `<project>/.mcpp/.xlings/subos/{name|_}`
819
-
(`config.cpp:348-353`), and `effective_workspace()` includes the project's
820
-
layers.
821
-
822
-
Under P, xlings' native behaviour is what is wanted and no flag is needed —
823
-
with one condition. In **Anonymous** mode the merge still includes the global
824
-
layer (§13.3), so a globally active version still shadows the declaration. In
825
-
**Named** mode the global layer is dropped, `active` is empty for a name the
826
-
project has not pinned, and the install activates exactly what was declared.
827
-
So P is "provision in the project scope, and the project's environment is its
828
-
own SubOS".
829
-
830
-
**Design G — keep provisioning globally, and force.** Pass
831
-
`useAfterInstall: true`. It makes the declaration win, and it makes two
832
-
checkouts fight over one shared workspace. It also cannot be verified: a forced
833
-
switch that fails is a `log::warn` inside a call that exits zero
834
-
(`installer.cpp`), so mcpp would hold a request whose outcome it cannot read.
835
-
836
-
**P is the recommendation**, and it is the one that matches what `[xlings]`
837
-
claims to be — a project-level isolated environment. G's forcing is not needed
838
-
once the scope is right, and G's shared workspace is the thing the isolation
839
-
exists to avoid.
840
-
841
-
### 16.4 What P costs, and what has to be decided with it
842
-
843
-
P is not free, and the cost is the same fact §13.3 records: a Named SubOS does
844
-
not inherit the machine's global pins. A project that declares one gets an
845
-
environment holding what it declared and what was installed into it, and
846
-
nothing else. That is isolation working as specified, and it is a change for
847
-
every project that today relies on the machine's tools being visible.
848
-
849
-
So P carries one decision that is not mcpp's alone:
850
-
851
-
**Does an mcpp project's environment inherit the machine's, or not?**
852
-
853
-
- Inheriting (Anonymous) keeps every existing project building and leaves the
854
-
declaration able to lose to a globally active version.
855
-
- Not inheriting (Named) makes the declaration authoritative and requires the
856
-
project to declare everything it uses.
857
-
858
-
A middle position exists and should be considered rather than assumed away:
859
-
provision in project scope while staying Anonymous, and accept that a declared
860
-
version can still lose to a globally active one — then report that case rather
861
-
than silently accepting it. The installer already emits the sentence; mcpp
862
-
would have to read it, which returns to the verification problem G has.
863
-
864
-
### 16.5 What to verify, whichever is chosen
806
+
2026.9.2.1 verification — and the reason the runner needed a
807
+
payload-directory lookup at all.
808
+
2. `active` is read from a workspace shared by every mcpp project on the
809
+
machine, so the auto-activation decides against a state no project owns.
810
+
3. Any attempt to force the switch would write into that shared workspace,
811
+
letting two checkouts flip each other.
812
+
813
+
**The fix is the scope, not a flag**: provision with
814
+
`make_project_xlings_env(cfg, root)`. The child then runs in project scope, the
815
+
installed set and activation records land in the project's own SubOS
816
+
(`config.cpp:348-353` puts it under `<project>/.mcpp/.xlings/subos/…`), and the
817
+
view the install writes is the view the shim reads.
818
+
819
+
**D8 is withdrawn.** `useAfterInstall` is not passed, and no `use_version` call
820
+
is added. The provisioning pass keeps its shape and changes one argument.
821
+
822
+
### 16.3 Two consequences to write down rather than discover
823
+
824
+
**The project SubOS layer outranks the project manifest.** It is merged last,
825
+
so an explicit `xlings use` performed inside the project beats what
826
+
`mcpp.toml` declares, until the manifest is re-materialised. That is defensible
827
+
— an action a person took beats a file — but it is not obvious, and it belongs
828
+
in the documentation next to the table above.
829
+
830
+
**A declared version that is not installed fails at the shim, with the right
831
+
words.** Resolution finds the pin, `match_version` finds no such installed
832
+
version, and the diagnostic is already
833
+
`"{}@{} is the version this project asks for"` (`src/core/xvm/shim.cpp:495`).
834
+
Provisioning is what prevents it; the diagnostic is what happens if
835
+
provisioning was skipped, and it names the project rather than the machine.
836
+
837
+
### 16.4 What to verify
865
838
866
839
| # | Criterion |
867
840
|---|---|
868
-
| A1 | A project declaring a version different from the machine's active one builds and runs against the declared version |
841
+
| A1 | A project declaring a version different from the machine's active one builds and runs against the declared version, in Anonymous mode |
869
842
| A2 | The machine's global workspace file is unchanged after that build |
870
843
| A3 | Two checkouts declaring different versions of one tool each get their own, in one session |
871
844
| A4 | An entry with `""` on a machine where another version is active leaves that version active |
872
-
| A5 | A declared version that could not be made active ends the build with a message naming tool, version and layer — never a warning inside a successful build|
873
-
| A6 | The second build of an unchanged project performs no install and no switch |
874
-
| A7 | A program the project did not declare still resolves, or fails with a message naming the environment it was looked up in|
845
+
| A5 | A Named SubOS project resolves only what it declared plus what its environment holds, and a tool it did not declare fails naming that environment |
846
+
| A6 | The second build of an unchanged project performs no install |
847
+
| A7 | A declared version that was never installed fails at the shim with the "version this project asks for" wording, not with a bare "not found" |
875
848
876
-
A2 and A3 are the ones that fail under G today. A7 is the one that changes
877
-
meaning between the two branches of §16.4, and it is where the decision becomes
878
-
visible to a user.
849
+
A1, A2 and A3 fail today for one reason, the scope of `make_xlings_env`, and
850
+
pass together once it changes. A5 is the Named branch of the table in §16.1 and
0 commit comments