Repository navigation
Commit 7735387
fix: 六处「答案已记录、做决定的代码不读它」+ 明确拒绝 system 工具链 (#538)
* fix: six defects where a recorded answer never reached the decision (2026.8.30.2)
Analysis, measurements and design:
`.agents/docs/2026-08-30-issues-527-529-535-537-analysis-and-design.md`.
Two families. A record exists and the code that decides does not read it; or
the runtime search path mixes what was declared with what this machine happens
to have installed. The host-dependence policy that settles every severity here
is stated once in that document's §1.2 and in docs/03: mcpp and everything the
ecosystem publishes depend on no host, a user's own project may and that choice
is theirs to guarantee, and the boundary between a warning and a refusal is
whether the result builds and runs.
`mcpp test` re-derived an unchanged answer on every invocation (#529). Both
post-link ELF passes were written with a read-back keyed on the artifact's
stat, and `prepare_build` rewrites `resolution.json` from a fresh json object
at the start of every run, carrying neither record — so each invocation began
by deleting the memo its own backend was about to look for. The records move to
`.mcpp-runtime-verdicts.json`, which survives, and `resolution.json` keeps
publishing a copy, which is the shape `sync_resolution_verdict` already had.
Pruning now asks whether the artifact left the DISK rather than whether it left
the current command's plan: `build` and `test` share one output directory with
different link-unit sets and were deleting each other's verdicts. The record's
invalidation key gains the SubOS farm stamp and MCPP_ALLOW_HOST_LIBS, because
making a memo durable creates a staleness obligation that did not exist while
the answer was recomputed. Measured on ten link units, all warm: `mcpp test`
after a `mcpp build` 3.15s -> 0.36s.
A new source file in a `path` dependency was invisible to the fast path, which
swept only the project being built. `mcpp build` printed `Finished dev in
0.00s` and the module was never compiled. Content edits were caught, but by the
post-link snapshot rather than by the sweep. This is #359's shape in a
directory that fix did not reach, and workspace members depend on each other by
`path`, so it is not an edge case. The dependency source roots are recorded in
`.build_cache` and swept, manifest included.
`[toolchain] system` with a `build.mcpp` died as `posix_spawnp('') failed`
(#527 Bug 1). The resolved compiler path was in `tc->binaryPath` the whole time
and was not handed to the build.mcpp closure. This fills an unset variable; it
adds no host capability, and the same manifest without a build.mcpp already
built. The host-dependence warning states the cost once per build and names the
xim route.
`standard = 26` — the spelling #527 uses in three of its own examples — was
silently ignored, because the key is documented as a string and `get_string`
returns nothing for a bare integer. Both spellings are now accepted.
`[workspace.package]` and `[workspace.build]` (#527 Bug 2, RFC 3). The
workspace root's `[build]` reached no member. Scalars are inherited when the
member did not DECLARE the key, vectors append workspace-first, and
"declared" is recorded by both parse paths rather than inferred by comparing
against the default — a member pinning `standard = "c++23"` under a c++26
workspace must keep it, and that is the same bytes as the default. This is the
precondition the cpp20 design doc's §9-Q3 wrote down and deferred. Both
inheritance sites now call one function. `allow_host_libs` is refused there:
it turns a correctness gate off, and a root able to set it once would disable
it for members added later by someone who never read that file.
A dependency declaring a standard above the graph's is now reported instead of
silently discarded, degraded and promoted by --strict, and scoped to manifests
the author controls: every index descriptor with an mcpp segment declares
`language` (782 of 782 measured locally, 756 of 774 being C libraries carrying
a boilerplate "c++23"), so declaredness does not mean authorship there.
A dialect-class flag in `cxxflags` that never reaches the `import std` prebuild
is refused before compiling, naming `dialect_cxxflags`. Read from the effective
flag set, so a profile or target block is covered too; silent when nothing in
the graph imports std, and scoped to the root package because a dependency
carrying the flag may legitimately not import std at all.
Tests: tests/e2e/321..326, each with the negative case that keeps the check
honest. Docs: 03, 05, 06 in both languages.
* fix: refuse `[toolchain] system`; only mcpp-managed toolchains build
The host-dependence rule is not uniform across axes, and the split is the
point rather than an inconsistency.
THE TOOLCHAIN IS MCPP'S OWN CONTRACT. Everything mcpp promises — that
`import std` is available, that the runtime closure is computable, that two
machines and CI produce the same build — is a statement about a compiler mcpp
resolved and can identify. A compiler taken from PATH makes every one of those
unverifiable, so `[toolchain] … = "system"` is refused rather than warned
about. `msvc@system` is the single exception and is a different spelling: it
names a FAMILY whose installation mcpp locates, on the one platform where the
compiler cannot be redistributed.
THE LIBRARIES A PROGRAM LINKS ARE THE PROGRAM'S BUSINESS. A project may link a
host library or its own `.so`; mcpp names the supported route — declare the
provider so it resolves from mcpp-index, contribute the package if the index
does not carry it yet — and does not refuse while the result builds and runs.
The developer owns the artifact and guarantees it.
This replaces the previous commit's treatment of #527 Bug 1, which filled in
the resolved compiler path and warned. The crash it removed was real —
`posix_spawnp('') failed (error 2)` as soon as the project had a build.mcpp —
but a refusal that arrives as a crash three layers down is not a policy, it is
a bug wearing one. The refusal now fires during toolchain resolution, before
anything tries to compile the build program, and says what to write instead.
Three existing tests referenced the escape hatch and each needed a different
answer:
14_toolchain_fallback asserted only that `system` did NOT produce "no
toolchain configured". That predicate stays satisfied
by any other error, so the test went on passing while
its stated intent inverted — a negative-only
assertion cannot tell "it worked" from "it failed
differently". Both halves are checked now.
293_…_name_one_os used `system` to point a Linux compiler at a Windows
target. The refusal fires first, so the test began
taking its skip branch — and its own header says a
skip there has to be earned or the test cannot see a
revert. The refusal is now an accepted PASS branch
with its own reason, because the invariant holds by a
stronger mechanism: that door is closed entirely.
105_asm_sources_nasm genuinely unaffected; its broken-MCPP_HOME bootstrap
error still fires first. Verified, not assumed.
325 is rewritten accordingly, and asserts the refusal reaches the user before
the build program starts, that it fires for the environment side channel too,
that it names the msvc@system exception and the library axis, and — the
denominator — that a project with no `[toolchain]` at all still builds.
`mcpp.diag`'s host-route helper is reverted: with the toolchain axis refusing
rather than warning, and the library-provenance work not in this change, it
had no consumer. Shipping an unread field is the defect this branch is about.
Also fixes the version constant: `modules/versioning/src/version.cppm` is the
second source of truth `check_version_pins.sh` enforces, and CI caught it —
that mismatch is what failed e2e on all three platforms and the Windows
`SubsystemContracts.TheBinaryVersionMatchesTheRootManifest`.
* docs: the host-dependence rule is per axis; second person out of the reference doc
The design document argued a single boundary — "does it build and run" — for
every host dependency, and D15 followed it to "warn, do not refuse". §1.2 is
corrected to state the rule per axis: the toolchain is mcpp's contract and is
refused, the libraries a program links are the program's own and stay a
warning. §7, §9, §10, §11.7, §12 and the review record follow.
The measurement behind the earlier conclusion was right and stays in the
document — `[toolchain] system` does build a project using `import std`. What
was wrong was carrying it across an axis boundary, which is the failure this
document keeps finding from the other side.
Also drops a second-person sentence from docs/06 that check_docs_style.sh
refuses in a reference doc.
* fix(workspace): anchor an inherited include_dirs to the workspace root
Found by re-reading the merge, not by a failure. `[workspace.build]
include_dirs = ["shared/inc"]` was prepended to each member verbatim, so every
member resolved it against its OWN directory — looking for
`<member>/shared/inc` for a directory that lives at `<workspace>/shared/inc`.
This is #224 for a new key. `[indices].path` and `[workspace.dependencies]
path` are anchored to the workspace root for exactly this reason, and a third
relative-path key that skipped it fails as a missing header three members deep,
naming neither the manifest that declared it nor the root it was written
against. Anchored rather than refused: `expandIncludeDirs` already accepts an
absolute include directory, so the anchored form needs nothing downstream.
321 now includes a header from the workspace root, so the anchoring has an
assertion rather than a comment.
* test(293): the new refusal branch must not exit early
The ecosystem job runs this file and checks that each test "ran to its
conclusion" — an assertion that exists so an early exit cannot masquerade as a
pass. My branch for the toolchain refusal did `exit 0`, which skipped half two
entirely: four correct cross builds that this file also guards. CI caught it;
a local run did not, because locally the exit code is all a caller sees.
The branch now records that half one is settled and lets the script continue.
The "names both systems" assertion is asked only of the OS-mismatch refusal —
the toolchain refusal is a different sentence about a different decision, one
that never resolved a target at all, and demanding both triples from it would
be asserting on the wrong object.
---------
Co-authored-by: speak-agent <x.d2learn.org@gmail.com>1 parent adc7077 commit 7735387
25 files changed
Lines changed: 4042 additions & 172 deletions
File tree
- .agents/docs
- docs
- zh
- modules
- manifest/src
- versioning/src
- src
- build
- tests/e2e
Lines changed: 1749 additions & 0 deletions
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
6 | 95 | | |
7 | 96 | | |
8 | 97 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
305 | 305 | | |
306 | 306 | | |
307 | 307 | | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
308 | 357 | | |
309 | 358 | | |
310 | 359 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
72 | 72 | | |
73 | 73 | | |
74 | 74 | | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
75 | 116 | | |
76 | 117 | | |
77 | 118 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
151 | 151 | | |
152 | 152 | | |
153 | 153 | | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
154 | 250 | | |
155 | 251 | | |
156 | 252 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
284 | 284 | | |
285 | 285 | | |
286 | 286 | | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
287 | 328 | | |
288 | 329 | | |
289 | 330 | | |
| |||
0 commit comments