Skip to content

Commit 87c67eb

Browse files
committed
design: the documentation the rule needs, and a self-review that changes the proposal
Section 14 assigns the inheritance rule to docs/05 §2.13 and docs/17 with their Chinese twins, as a table plus the reason the middle row is not an omission, and gives it a criterion so the paragraph is not the only record. Section 15 is the second read against the code. Its first finding changes the proposal: a project-file workspace entry is merged verbatim and the shim looks up a program's own name, so naming a package root pins a node nothing invokes while its programs resolve from the layer underneath. Group expansion happens in cmd_use, which a manifest never goes through. Three ways out are stated; expanding at read time is recommended and is xlings' to accept. Two smaller corrections: 'retired' has always meant the manifest key and never the file field, and section 12.6's claim to touch neither subos nor envs is withdrawn.
1 parent d4d58fa commit 87c67eb

1 file changed

Lines changed: 117 additions & 5 deletions

File tree

.agents/docs/2026-09-03-xlings-workspace-as-the-one-table.md

Lines changed: 117 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,15 @@ one table, an entry means "this project uses this at this version, provision it
1616
if absent", and `deps` is retired through a deprecation path rather than a
1717
removal.
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

Comments
 (0)