@@ -102,20 +102,20 @@ manifest therefore accepts a version, a prefix, or `latest`, and "must exist,
102102version unconstrained" is spelled ` latest ` . Measured on three real subos files
103103on 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
121121both 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
145145can be dropped without touching ` mcpp.toml ` or any manifest. That is the
146146end-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
175174a pin that is also honoured. Section 9 makes it a criterion rather than an
176175assumption.
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.
3213214. **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
327328The 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`.
508509answers Q1: xlings does not provision from `workspace`, which is why section 4
509510was 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
513538Every `envs` consumer in the xlings source is one of two structures, and
0 commit comments