Skip to content

Commit 161aec2

Browse files
committed
design: the key space measured, the namespace put where the file puts it, and the migration collapsed to one release
Three corrections, all from data rather than inference. A workspace key is an xvm target of any kind. The default SubOS holds binutils beside ar, as and ld at one version, and gcc beside g++ and cc at another: package roots, programs and file assets share one namespace, and a root and its programs agree because cmd_use wrote them as one release. Calling the keys program names was wrong, and writing a package in a manifest is a legitimate entry whose programs receive the same version. The namespace rides the value, not the key: parse_ns_version splits the first colon of a version string and a real entry reads xim:2026.8.30.2. So the form is picolibc-riscv = "xim:1.8.12", which needs no rule mcpp has to invent. The migration is one release. Three manifests declare the key and mcpp declares none, so the deprecation window buys nothing; what does not bend is that deps is refused with a message rather than dropped in silence.
1 parent acd9cba commit 161aec2

1 file changed

Lines changed: 77 additions & 52 deletions

File tree

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

Lines changed: 77 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -102,20 +102,20 @@ manifest therefore accepts a version, a prefix, or `latest`, and "must exist,
102102
version unconstrained" is spelled `latest`. Measured on three real subos files
103103
on the development host: every stored value is concrete.
104104

105-
**W2. The authoring key may carry a namespace; the file's key never does.**
106-
Every workspace key in those same three files is a bare xvm target name —
107-
`cc`, `ar`, `mcpp`, `binutils`, even `crt1.o`and none contains a colon. That
108-
is the name the shim looks up. A namespace belongs to the *install address*
109-
(`xim:picolibc-riscv@1.8.12`), which is a different vocabulary: xlings itself
110-
keeps them apart, and `parse_ns_version` handles a scope prefix on the
111-
**version** (`local:0.4.47`), not a namespace on the key.
112-
113-
So mcpp accepts `"xim:picolibc-riscv" = "1.8.12"` as an authoring key, uses the
114-
full address when it provisions, and writes the bare `picolibc-riscv` into the
115-
file's `workspace`. This is not a translation layer; it is the same split
116-
xlings makes between what you install and what you resolve. It is also what
117-
lets mcpp express its own injected entry, whose value from the target row is a
118-
namespaced address (§4).
105+
**W2. The namespace goes on the version, and that needs no new rule.**
106+
`.xlings.json` already spells a namespaced entry as `target = "<ns>:<version>"`:
107+
`parse_ns_version` splits on the first colon and `make_ns_version` builds it
108+
(`src/core/xvm/db.cpp:10-20`), and a real subos on the development host holds
109+
`"mcpp": {"active": "xim:2026.8.30.2", …}`. So the form is
110+
111+
```toml
112+
[xlings.workspace]
113+
picolibc-riscv = "xim:1.8.12"
114+
```
115+
116+
with the key being the xvm target and the namespace riding the value, exactly
117+
as the file writes it. mcpp accepts what xlings already accepts, and the
118+
target's C library that mcpp injects (§4) is expressible in the same shape.
119119

120120
**W3. The per-platform value form is unchanged.** It is already accepted on
121121
both keys (2026.9.2.1) and it survives the merge unmodified.
@@ -145,13 +145,12 @@ If xlings later provisions from `workspace`, the `deps` half of the emission
145145
can be dropped without touching `mcpp.toml` or any manifest. That is the
146146
end-state, and it is a change on the xlings side, not here.
147147

148-
One consequence for section 3's W2. mcpp does not only pass the author's
149-
entries through: it appends the target's C library to the same channel
150-
(`prepare.cppm:3186-3194`), and the value comes from the target row as
151-
`xim:picolibc-riscv@1.8.12` — a namespaced reference with a version. So a
152-
workspace key that cannot carry a namespace would make mcpp unable to express
153-
its own injected entry, not merely inconvenience an author. W2 is a
154-
prerequisite rather than a preference.
148+
One consequence for section 3. The target's C library that mcpp appends
149+
(`prepare.cppm:3186-3194`) arrives from the target row as
150+
`xim:picolibc-riscv@1.8.12`, an install address. As a workspace entry it is
151+
`picolibc-riscv = "xim:1.8.12"` — the same two facts, in the shape the file
152+
already uses. mcpp splits the address once, at the point it builds the entry;
153+
nothing downstream sees two spellings.
155154

156155
## 5. What provisioning means after the merge
157156

@@ -175,35 +174,36 @@ the documented claim rather than away from it: an entry that was a pin becomes
175174
a pin that is also honoured. Section 9 makes it a criterion rather than an
176175
assumption.
177176

178-
## 6. Migration, and what "retired" means
179-
180-
`deps` is not deleted. Three phases, each with a criterion.
181-
182-
**Phase 1 — the merged reader.** `workspace` gains provisioning, the payload
183-
directory hand-off (`MCPP_XPKG_*_DIR`) and the runner lookup path. `deps` keeps
184-
working exactly as it does and is documented as deprecated. A manifest that
185-
names one package in both, with different versions, is a hard error naming both
186-
lines: the drift of section 1 becomes unrepresentable at the moment the second
187-
reader appears rather than later.
188-
189-
**Phase 2 — the warning.** A manifest using `deps` builds and prints one
190-
advisory naming the `[xlings.workspace]` line to write instead. The advisory is
191-
per package, so the message is the edit.
192-
193-
**Phase 3 — refusal, never silence.** `deps` stops being honoured and becomes a
194-
hard error that names the replacement. It must not become an unknown key:
195-
`[xlings]` has no unknown-key sweep (verified — no `kKnownXlings` list exists
196-
in `toml.cppm`), so a removed key would be read by nobody and reported by
197-
nobody, which is the shape #531 exists to prevent. Phase 3 is gated on an index
198-
sweep showing no published manifest still uses `deps`, and on an mcpp floor in
199-
the packages that migrate.
200-
201-
**The ecosystem denominator is small.** Measured across the local `mcpplibs`
202-
checkouts: three manifests declare `[xlings] deps`
203-
(`aarch64-virt-rt`, `riscv-virt-rt`, `std-freestanding`), one entry each, all
204-
of the form `xim:<name>@<version>`. mcpp's own `mcpp.toml` declares no
205-
`[xlings]` section. The index has to be swept before Phase 3; the local
206-
denominator is not the ecosystem.
177+
## 6. Migration: one release, not a deprecation window
178+
179+
The population is three manifests. `aarch64-virt-rt`, `riscv-virt-rt` and
180+
`std-freestanding` each declare one `[xlings] deps` entry of the form
181+
`xim:<name>@<version>`; mcpp's own `mcpp.toml` declares no `[xlings]` section
182+
at all. A three-phase deprecation exists to give an ecosystem time it does not
183+
need here, so the migration is a single release: the packages are edited and
184+
republished with the new form, and `deps` is refused in the same version that
185+
introduces the merged reader.
186+
187+
What "refused" must mean, and this is the part that does not bend: `deps` stops
188+
being honoured by becoming a **hard error that names the replacement**, never
189+
by becoming a key nobody reads. `[xlings]` has no unknown-key sweep — no
190+
`kKnownXlings` list exists in `toml.cppm` — so a silently dropped key would be
191+
read by nobody and reported by nobody, which is the shape #531 exists to
192+
prevent.
193+
194+
Ordering, because the three packages are consumed by projects that may be built
195+
with either engine:
196+
197+
1. The merged reader ships, accepting `workspace` and refusing `deps` with a
198+
message naming the line to write.
199+
2. The three packages are republished with `[xlings.workspace]` and an mcpp
200+
floor at that version.
201+
3. An index sweep confirms no other published manifest declares `deps`. The
202+
local checkouts are not the ecosystem; the sweep is what makes the claim.
203+
204+
Step 3 gates nothing on the mcpp side — it is a check that the denominator was
205+
what it looked like. If it turns up manifests nobody knew about, the refusal in
206+
step 1 becomes an advisory for one release and the window opens after all.
207207

208208
## 7. The packaging map, and a loss the current implementation has
209209

@@ -321,8 +321,9 @@ that was open is part of how the design was reached.
321321
4. **Does binding a package determine its programs' versions?** Yes, and the
322322
expansion happens when the entry is honoured rather than when it is merged
323323
(§15.1).
324-
5. **Phase 3's floor.** Open. Which mcpp version the migrating packages
325-
declare, and whether the index sweep is a release gate or a one-off.
324+
5. **The migration window.** Answered by the denominator: three manifests, so
325+
no window (§6). The index sweep confirms the denominator rather than gating
326+
the change.
326327
327328
The one decision left for review is D8 (§15.1): the provisioning pass sends
328329
`useAfterInstall: true`, so a declared version becomes the active one in the
@@ -508,6 +509,30 @@ workspace replaces the global one. That belongs in `docs/17`.
508509
answers Q1: xlings does not provision from `workspace`, which is why section 4
509510
was revised rather than kept.
510511
512+
### 13.3.1 What a workspace key is: an xvm target, of any kind
513+
514+
Measured on the development host's default SubOS, 546 entries:
515+
516+
```
517+
binutils = 2.42 ar = 2.42 as = 2.42 ld = 2.42
518+
gcc = 16.1.0 g++ = 16.1.0 cc = 16.1.0
519+
mcpp = xim:2026.8.30.2
520+
Scrt1.o, crt1.o, crti.o, crtn.o, glibc.files.1 … glibc.files.101
521+
```
522+
523+
Package roots, the programs of those packages, and file assets all live in one
524+
namespace, each with `{active, installed}`. A package root and its programs
525+
carry the **same version** because they are members of one release and
526+
`cmd_use` wrote them together (§15.1) — that identity is the group expansion's
527+
own footprint in the data.
528+
529+
So "the workspace holds packages" and "the workspace holds programs" are both
530+
half-right: it holds xvm targets, and a package's root is one of them. Writing
531+
the package in a manifest is therefore a legitimate entry, and its programs
532+
receive the same version when the entry is honoured. Writing a program is
533+
equally legitimate and selects the same release. What a key never carries is a
534+
namespace; that rides the value (§3 W2).
535+
511536
### 13.4 `envs` has no reader anywhere
512537
513538
Every `envs` consumer in the xlings source is one of two structures, and

0 commit comments

Comments
 (0)