@@ -16,11 +16,15 @@ one table, an entry means "this project uses this at this version, provision it
1616if absent", and ` deps ` is retired through a deprecation path rather than a
1717removal.
1818
19- Two things the change is not. It is not a translation layer: ` [xlings] ` mirrors
20- xlings' own ` .xlings.json ` 1:1, and the merged semantics is the one xlings
21- already has, so mcpp is following the schema rather than inventing one. And it
22- does not weaken any existing behaviour: a declaration that cannot be satisfied
23- stays a hard build error, which is the property #531 was filed to obtain.
19+ The merge is in ` mcpp.toml ` , not in the file mcpp writes. ` .xlings.json ` keeps
20+ both fields, because xlings reads them in two different places and neither
21+ subsumes the other (section 13). One authored entry therefore materialises as
22+ one ` deps ` element and one ` workspace ` member: not a translation, but one
23+ statement written where each of its two halves is read.
24+
25+ The change does not weaken any existing behaviour: a declaration that cannot be
26+ satisfied stays a hard build error, which is the property #531 was filed to
27+ obtain.
2428
2529## 1. What the two keys are today
2630
@@ -531,3 +535,111 @@ section half-audited.
531535| §11 Q1 | open | answered: xlings does not provision from `workspace` |
532536| §12.1 | `envs` reader: "the materialisation" | no reader on either side |
533537| §12.5 | `[xlings.envs]` is read by xlings for the tool environment | it is read by nothing |
538+
539+ ## 14. What mcpp's own documentation must say
540+
541+ The inheritance rule of §13.3 is the behaviour an author most needs and the one
542+ mcpp states nowhere. It is not introduced by this proposal; it is being written
543+ down because the proposal makes `workspace` the key everybody writes.
544+
545+ **`docs/05-mcpp-toml.md` §2.13** gains the rule as a table, next to the
546+ `[xlings.workspace]` description:
547+
548+ | The project declares | The version of a tool it did not name comes from |
549+ |---|---|
550+ | `[xlings.workspace]`, no `subos` | the machine's global workspace; the project's own entries win over it |
551+ | `[xlings.workspace]` and `subos = "<name>"` | that SubOS's own workspace; the global one does not apply |
552+ | neither | the machine's global workspace |
553+
554+ with one sentence for why the middle row is not an omission: a named SubOS is a
555+ different environment with its own installed set, and carrying the host's pins
556+ into it would name versions that are not there.
557+
558+ **`docs/17-the-project-environment.md`** gains the consequence, because that
559+ chapter is where `subos` is chosen: declaring a SubOS changes which version
560+ pins apply, and a project that relied on the machine's pins has to state them
561+ itself once it names an environment. The chapter already says mcpp reads an
562+ environment and never creates one; this is the other half of what the
563+ declaration decides.
564+
565+ **`docs/13-baremetal.md`** needs no change: it declares packages, not versions.
566+
567+ Each with its `docs/zh/` twin, which CI enforces.
568+
569+ A criterion, so the paragraph is not the only record: a project that pins a
570+ tool and declares a SubOS resolves that tool to its pin, and a tool it does not
571+ pin resolves inside the SubOS rather than to the host's global choice. It is an
572+ e2e over two SubOS environments and one tool installed at two versions.
573+
574+ ## 15. Self-review
575+
576+ Read against the code a second time, 2026-09-03. Three findings; the first is
577+ the one that changes the proposal.
578+
579+ ### 15.1 A project-file `workspace` entry does not expand a binding group
580+
581+ Programs are resolved by name. The shim reads `Config::effective_workspace()`
582+ and looks up **the program's own name** (`src/core/xvm/shim.cpp:409-412`), and
583+ the merge that produces that map is a plain per-key merge
584+ (`config.cpp:846-864`). Neither consults the version database, so neither
585+ expands a package into its members.
586+
587+ Expansion happens somewhere else: `cmd_use` resolves the whole release through
588+ `resolve_binding_selection` and writes one workspace entry **per member**
589+ (`src/core/xvm/commands.cpp:763-772`). That is why using either a package name
590+ or one of its programs switches all of them — the group is expanded at the
591+ moment of the switch, and what lands in the file is already per-member.
592+
593+ A project manifest does not go through `cmd_use`. Its `workspace` object is
594+ read as a layer, verbatim. So:
595+
596+ ```toml
597+ [xlings.workspace]
598+ qemu-riscv = "9.2.4-1" # the package, and the umbrella node
599+ ```
600+
601+ pins the node named ` qemu-riscv ` , which nothing invokes, and leaves
602+ ` qemu-system-riscv64 ` to be resolved by the layer underneath. On a machine with
603+ two versions installed and the newer one active, the project's pin is silently
604+ inert — the exact shape the merge was meant to remove.
605+
606+ Three ways out, and this is the decision the proposal now needs most:
607+
608+ 1 . ** Expand at read time.** When a project layer names a group root, resolve
609+ the group and apply the version to every member. Correct and invisible to
610+ authors, and it puts version-database knowledge into the merge, which is
611+ xlings' side to accept or refuse.
612+ 2 . ** Expand at materialisation.** mcpp writes one ` workspace ` member per group
613+ member. mcpp does not have the version database, so it cannot.
614+ 3 . ** Require program names for pinning.** The author writes
615+ ` qemu-system-riscv64 = "9.2.4-1" ` . This works today with no change anywhere
616+ — and it re-opens the split the proposal closes, because provisioning needs
617+ the package (` qemu-user-aarch64 ` ) while pinning needs the program
618+ (` qemu-aarch64-static ` ), and §13 measured that those names differ.
619+
620+ Option 1 is the recommendation. Option 3 is honest about today and should be
621+ what the documentation says until 1 exists.
622+
623+ ### 15.2 "Retired" means the manifest key, never the file field
624+
625+ Section 6 reads as though ` deps ` disappears. It does not: §4 keeps emitting the
626+ ` deps ` array into ` .xlings.json ` , because that is the only key xlings' install
627+ path reads. What is retired is the key an author writes in ` mcpp.toml ` . Every
628+ occurrence of "retired" in sections 0, 6 and 12 means that and only that, and
629+ the phases apply to the manifest surface alone.
630+
631+ ### 15.3 Section 12.6 is no longer true as written
632+
633+ It says the proposal touches neither ` subos ` nor ` envs ` . It touches both,
634+ though not their behaviour: §13.4 asks for a decision on ` envs ` , which is
635+ written by mcpp and read by nobody, and §14 adds documentation for what ` subos `
636+ does to the inheritance chain. Neither changes an effect; both change what the
637+ project states about itself, which is the part this document is for.
638+
639+ ### 15.4 What survived the review unchanged
640+
641+ The two facts section 1 rests on. The general form still installs nothing while
642+ its documented shorthand installs, and nothing still compares the two keys when
643+ both name one package. §13 strengthened rather than weakened them: xlings makes
644+ the same split in the same direction, which is why the merge belongs in the
645+ manifest and not in the file.
0 commit comments