@@ -175,13 +175,26 @@ Three manifests declare `[xlings] deps` — `aarch64-virt-rt`, `riscv-virt-rt`,
175175mcpp's own ` mcpp.toml ` declares no ` [xlings] ` section. A deprecation window
176176buys nothing at that size.
177177
178- 1 . The merged reader ships, and ` deps ` becomes a hard error naming the line to
179- write instead.
178+ ** Corrected during implementation (2026-09-03): step 1 refuses in the root
179+ manifest and advises in a dependency's.** The denominator argument counts
180+ manifests that declare the key, and it is the wrong denominator: those three
181+ packages are * consumed* , and a consumer pins an exact version. A refusal that
182+ reached a dependency's manifest would make ` riscv-virt-rt@0.6.0 ` unbuildable on
183+ the new engine for everyone who pinned it, and no republished version reaches
184+ them until each consumer re-pins. That is the shape this repository has already
185+ paid for once — a consumer must ship before the thing it depends on moves.
186+
187+ The asymmetry is not a hedge. A root manifest is the author's own file and they
188+ can fix it in the same minute they read the message; a dependency's manifest is
189+ not theirs to edit, and refusing it punishes the wrong person.
190+
191+ 1 . The merged reader ships. ` [xlings] deps ` in the ** root** manifest is a hard
192+ error naming the line to write; in a ** dependency's** manifest it is
193+ honoured and reported once, naming the package.
1801942 . The three packages are republished with ` [xlings.workspace] ` and an mcpp
181195 floor.
182- 3 . An index sweep confirms the denominator. It gates nothing; if it finds
183- manifests nobody knew about, step 1's refusal becomes an advisory for one
184- release and a window opens after all.
196+ 3 . An index sweep confirms no other published manifest declares it. When the
197+ advisory has been silent across a release, the dependency path refuses too.
185198
186199** What does not bend:** ` deps ` is refused with a message, never dropped in
187200silence. ` [xlings] ` has no unknown-key sweep — no ` kKnownXlings ` list exists in
@@ -502,3 +515,65 @@ The two that mattered: reading a SubOS **state** file as though it were an
502515authored ** project** file, and asserting a blast radius without checking which
503516environment the call runs in. Both were arguments from the shape of the code
504517rather than from what it does.
518+
519+ ## 16. Implementation plan
520+
521+ Eight tasks. T1 is the only one everything else waits on; T2, T3 and T4 are
522+ independent of each other; T7 runs after the release.
523+
524+ ```
525+ T1 manifest ──┬── T2 provisioning scope ──┐
526+ ├── T3 xlings module ├── T5 docs ── T6 tests ── T8 release
527+ └── T4 descriptor emitter ──┘ │
528+ T7 ecosystem ──┘
529+ ```
530+
531+ | # | Task | Files | Depends on |
532+ | ---| ---| ---| ---|
533+ | T1 | The merged table: parse, both namespace positions, ` "" ` , per-platform, ` workspaceByPlatform ` , ` deps ` root-refusal and dependency-advisory, ` envs ` refusal | ` modules/manifest/src/{toml,types}.cppm ` | — |
534+ | T2 | Provisioning in project scope; the materialisation feeds both file fields | ` src/build/prepare.cppm ` | T1 |
535+ | T3 | ` ProjectEnv ` and ` seed_xlings_json ` lose ` envs ` | ` src/xlings/xlings.cppm ` | T1 |
536+ | T4 | ` xpm.<platform>.deps ` in the emitted descriptor | ` src/pm/publisher.cppm ` | T1 |
537+ | T5 | ` docs/05 ` §2.13, ` docs/17 ` , both ` docs/zh/ ` twins | docs | T1-T4 |
538+ | T6 | Unit tests and one e2e | ` tests/ ` | T1-T4 |
539+ | T7 | The three packages republish | ` mcpplibs/{aarch64-virt-rt,riscv-virt-rt,std-freestanding} ` | T8 |
540+ | T8 | Version, CI, self-review, merge, release, sandbox | — | T5, T6 |
541+
542+ ### 16.1 What each axis demands of the implementation
543+
544+ ** Architecture.** One authored key, one derived list, one provisioning pass in
545+ one scope. ` XlingsConfig::deps ` becomes the derived install addresses, so every
546+ existing reader — the pass, ` fillXpkgDirs ` , ` xlingsDepBinDirs ` , the file's
547+ ` deps ` array — keeps reading the field it reads today and none of them learns
548+ about namespaces.
549+
550+ ** Stability.** The scope change moves writes off a workspace shared by every
551+ project on the machine. Nothing else about the pass changes: its result check,
552+ its stamp, its offline gates.
553+
554+ ** Simplicity.** No new mechanism on either side, and the diff is smaller than
555+ the design: the parse produces two projections and everything downstream is
556+ untouched.
557+
558+ ** User experience.** A key that never worked stops being documented as working;
559+ a key that did two jobs becomes one; and a version a project declares is the
560+ one that runs.
561+
562+ ** Compatibility.** No manifest key is added. ` [xlings] deps ` keeps working in a
563+ dependency and is reported once. ` [xlings.envs] ` is refused, and no manifest in
564+ the ecosystem uses it — measured across the local checkouts.
565+
566+ ** Cross-platform.** ` workspaceByPlatform ` keeps the unresolved declaration, so
567+ the descriptor a Linux host emits carries the Windows edge.
568+
569+ ** Consistency.** The platform vocabulary becomes xlings' own (` macosx ` shown,
570+ ` macos ` still accepted), and the namespace may be written in either position
571+ because both are already spellings the ecosystem uses.
572+
573+ ** Upgrading without noticing.** The stamp key changes with the declared set, so
574+ one re-provision per project and then nothing. No cache is invalidated, no
575+ output path moves, and a manifest written for this loads on an older mcpp.
576+
577+ ** Test coverage.** Five of the twelve criteria fail on the current engine and
578+ are the ones that prove the change; C11 exists because this repository has
579+ twice measured a fast path instead of the thing under test.
0 commit comments