You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 49f875e
Browse filesBrowse the repository at this point in the historyBrowse files
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.
Copy file name to clipboardExpand all lines: docs/06-workspace.md
+96Lines changed: 96 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -151,6 +151,102 @@ linkage = "static"
151
151
default = "llvm@20.1.7"
152
152
```
153
153
154
+
### 4.1 `[workspace.package]` and `[workspace.build]`
155
+
156
+
Package metadata and build flags shared by every member are declared once at the
157
+
workspace root:
158
+
159
+
```toml
160
+
[workspace]
161
+
members = ["libs/core", "libs/http", "apps/server"]
162
+
163
+
[workspace.package]
164
+
standard = 26# or "c++26"; both spellings are accepted
165
+
version = "0.4.2"
166
+
license = "Apache-2.0"
167
+
authors = ["example"]
168
+
169
+
[workspace.build]
170
+
cxxflags = ["-Wall", "-Wextra"]
171
+
dialect_cxxflags = ["-fno-exceptions"]
172
+
```
173
+
174
+
A member then declares only what is its own:
175
+
176
+
```toml
177
+
[package]
178
+
name = "core"
179
+
# standard, version, license and authors are inherited;
180
+
# [workspace.build] cxxflags are inherited
181
+
```
182
+
183
+
**The merge rule.**
184
+
185
+
| kind | rule |
186
+
|---|---|
187
+
| scalars (`standard`, `version`, `license`, `c_standard`, `linkage`, …) | the member wins **when it declared the key**; otherwise the workspace value applies |
188
+
| vectors (`cxxflags`, `ldflags`, `defines`, `dialect_cxxflags`, `include_dirs`, …) | append, **workspace first** — so a member's own flag comes later on the command line, where it wins |
189
+
|`[workspace.dependencies]`| explicit opt-in per dependency, `x.workspace = true` (§3) |
190
+
191
+
"Declared" means the key was written, not that its value differs from the
192
+
default. A member that deliberately pins `standard = "c++23"` under a
193
+
`[workspace.package] standard = 26` keeps c++23; a member that says nothing gets
194
+
c++26. Those two are the same value and opposite intents, which is why the
195
+
distinction is recorded rather than inferred.
196
+
197
+
Scalars and vectors are inherited **implicitly**, without a per-key opt-in. The
198
+
drift a workspace exists to prevent is a member that forgot to opt in, so
199
+
inheritance is the default and overriding is the thing you ask for. Dependencies
200
+
keep their explicit opt-in because a dependency is an edge in the resolution
201
+
graph: inheriting one implicitly would change what a member resolves without its
202
+
own manifest naming it.
203
+
204
+
**`version` may be omitted by a member** when `[workspace.package]` supplies it.
205
+
It remains required overall — a member with neither is refused, naming both the
206
+
member and the workspace key that would have supplied it.
207
+
208
+
**Not everything is inheritable.**`[workspace.build] allow_host_libs` is
209
+
refused. It disables the hermetic-link check for a specific artifact, and a
210
+
workspace root able to set it once would disable that check for members added
211
+
later by someone who never read the root manifest. Keys that describe *how to
212
+
build* are inheritable; keys that describe *which safety check not to run* stay
213
+
with the package whose artifact it is. Any other unknown key in
214
+
`[workspace.package]` / `[workspace.build]` is refused too, rather than ignored:
215
+
a key that is silently dropped from a table whose whole purpose is propagation
216
+
produces a workspace that looks configured and is not.
217
+
218
+
**There is no `[workspace.target.<triple>]`.** A plain `[target.<triple>]` block
219
+
in the workspace root is already inherited by every member, per triple, with the
220
+
member winning. A second spelling for the same capability would be surface with
221
+
no function.
222
+
223
+
### 4.2 One standard for the whole module graph
224
+
225
+
A C++ module graph has exactly one standard: BMIs are not compatible across
226
+
levels, so the root package's `standard` is applied to every package in the
227
+
graph, including dependencies. A dependency's own `standard` is not applied.
228
+
229
+
When a dependency **declares** a level higher than the graph is built at, mcpp
230
+
reports it before compiling:
231
+
232
+
```
233
+
warning: dependency `render` declares standard = "c++26", and this graph is
234
+
built at c++23
235
+
impact: a C++ module graph has one standard, so the dependency's declaration
236
+
is not applied and its sources are compiled at the graph's level
237
+
hint: raise the consumer's standard to "c++26", or declare it once for
238
+
every member:
239
+
240
+
[workspace.package]
241
+
standard = "c++26"
242
+
```
243
+
244
+
This is a warning rather than an error — such a build usually succeeds, and it
245
+
is promoted to an error by `--strict`. It is reported only for manifests the
246
+
project author controls (the root package, workspace members, and `path`
247
+
dependencies): a package resolved from an index carries a `standard` written by
248
+
a descriptor generator rather than by the person reading the message.
249
+
154
250
## 5. Build Commands
155
251
156
252
### 5.1 Building & testing from the Workspace Root
0 commit comments