Repository navigation
feat(v3): structural DiagnosticAttribution surface for bootstrap diagnostics - #1587
Conversation
…nostics Verification PR #1572 STOP+PING flagged that row 82 `diagnostics_empty_after_bootstrap` cannot ratchet structurally today: `DiagnosticTable` was keyed by `PortId` but carried only a `Diagnostic`, `Dag::attach_diagnostic` allocates detached phantom ports with `produced_by: None`, and any consumer asking "is this from a bootstrap authority file?" had to compare `diagnostic.span().file` against a known authority path — a forbidden path-string bridge. This change adds a witness-based attribution surface so verification consumers (PR #1572 Worker B') dispatch on bootstrap origin via `DiagnosticAttribution`, not span paths. Substrate addition (`src/v3/compiler/src/diagnostics.rs`): - `BootstrapAuthorityKey(&'static str)` — opaque, validated witness for a row in `src/v3/std/bootstrap_authority.dag`'s `bootstrap_authority` set. Constructor is `pub(crate)` so only bootstrap loaders / kernel-patch helpers can mint a key; consumers receive minted keys through `DiagnosticAttribution` and dispatch on witness equality. `path()` is exposed for diagnostic display only; consumers must NOT use it to recover attribution (that's the bridge being dissolved). - `DiagnosticAttribution = Unattributed | BootstrapAuthority(key)` rides alongside each entry in `DiagnosticTable`. Adds `is_bootstrap()` and `as_bootstrap_authority()` helpers. - `DiagnosticTable.entries` now stores `(Diagnostic, DiagnosticAttribution)`. Existing `get` / `iter` / `is_empty` / `len` / `contains` accessors preserve the diagnostic- only signatures; new `attribution(port)` and `iter_attributed()` expose attribution. `insert(port, diag, attribution)` is the only breaking signature change (still `pub(crate)`). Dag attach API (`src/v3/compiler/src/dag.rs`): - `mark_unresolved_with_attribution(port, diag, attribution)` — new sibling carrying an explicit attribution; old `mark_unresolved` defers with `Unattributed`. - `attach_bootstrap_diagnostic(authority_key, diagnostic)` — new sibling of `attach_diagnostic` for diagnostics raised while loading or patching a substrate `bootstrap_authority` row. Allocates the same detached phantom port (no fabricated producer node, per dispatch constraint #3) but records `BootstrapAuthority(key)` so detached-phantom-port bootstrap diagnostics carry origin without a `SourceSpan.file` compare. Bootstrap loader rewires (per dispatch step 4): - `bootstrap.rs::patch_kernel_bool_boolean_algebra_inhabits` — three diagnostic-attach sites now call `attach_bootstrap_diagnostic` with `BootstrapAuthorityKey::new("dsl/std/types.dag")`. - `bootstrap.rs::report_pipeline_authority_error` — uses `BootstrapAuthorityKey::new(PIPELINE_AUTHORITY_FILE)` (the `src/v3/compiler/pipeline.dag` authority). - `bootstrap_regen_fresh.rs::parse_fixture` — both the tokenize- failure and parse-failure branches now call `attach_bootstrap_diagnostic(BootstrapAuthorityKey::new(file), …)`. `parse_fixture`'s `file: &str` parameter is tightened to `&'static str`; `load_fixtures` and the corresponding `Vec<(&'static str, &'static str)>` collection in `load_runtime_bootstrap_authorities` carry the `'static` through the producer chain (STAGED_FILES / V3_SPECS / COMPILER_FILES / EXTDEPS_FILES are already `'static`-keyed by `build.rs`). Ordinary non-bootstrap diagnostic call sites (lower / infer / int_literal_ranges / branch-condition checks in `dag.rs`) keep using `attach_diagnostic`, so they continue to record `Unattributed` per dispatch step 4 (don't widen scope). Focused tests (per dispatch step 5): - `crate::diagnostics::tests::diagnostic_attribution_default_is_…` (witness equality / `is_bootstrap` / `as_bootstrap_authority` invariants). - `crate::diagnostics::tests::diagnostic_table_round_trips_…` — drives both `attach_diagnostic` and `attach_bootstrap_diagnostic` through the public Dag surface and asserts per-port attribution via `iter_attributed`. - `crate::bootstrap::tests::kernel_bool_path_a_diagnostic_carries_…` — proves the detached-phantom-port path attaches `BootstrapAuthority(BootstrapAuthorityKey::new("dsl/std/types.dag"))`, consumed through `iter_attributed` / `attribution(port)` without any `span.file ==` compare. - `crate::bootstrap_regen_fresh::tests::parse_fixture_tokenize_…` and `parse_fixture_parse_failure_carries_bootstrap_authority` — cover the two `parse_fixture` failure branches. Validation: - `cargo test -p v3-compiler --lib --features bootstrap-regen-fresh` 303 passed (incl. 6 new attribution tests). - `cargo test -p v3-compiler --test integration` 781 passed. - `cargo run -p v3-compiler --features bootstrap-regen-fresh --bin regen_bootstrap` clean (no snapshot delta — attribution is runtime-only, not embedded in the bootstrap fixture). Receipt against `docs/debt/r3-debt-paydown-ledger-2026-05-02.md` row 82 ("Missing diagnostics-empty bootstrap gate"). Verification owns the final row-82 ratchet PR after this substrate surface lands; the recommended consumer API is `DiagnosticTable::attribution(port).is_bootstrap()` (or filtering via `iter_attributed`). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Review metadata
Verdict: APPROVE — clean structural refactor that replaces a Exploratory observations (non-blocking):
|
Per #1587 reviewer's optional follow-up. The asymmetry (pub `path()` / `pub(crate)` `new`) is intentional but only documented at the type level; pinning the egress-only rule on the accessor itself makes the "no `pub fn from_path` / `From<&str>` re-introducing the path-string bridge" review check explicit. Comment-only; no behavior change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
briansrls
left a comment
There was a problem hiding this comment.
Review metadata
- Provider / model:
codex/unknown - Commit:
48827c01· Trigger:schedule - Thinking:
338s wall
BLOCKING (3)
Root Cause
src/v3/compiler/src/diagnostics.rsbootstrap diagnostic attribution was added before the bootstrap-path authority and generated-snapshot boundary were made structural consumers → ground key minting in a real authority carrier, add the enum dissolution receipt, and serialize attribution viaiter_attributed.
| }) | ||
| } | ||
|
|
||
| /// Opaque, validated witness to a path in the substrate |
There was a problem hiding this comment.
BLOCKING: BootstrapAuthorityKey is documented as a validated substrate witness, but the named src/v3/std/bootstrap_authority.dag authority does not exist and new accepts any static path, so Modeling Faithfulness/P2 API-level enforcement rests on convention.
| /// bootstrap-authority row. Verification surfaces should prefer | ||
| /// this over `span.file == "src/v3/std/..."` predicates. | ||
| pub fn is_bootstrap(&self) -> bool { | ||
| matches!(self, Self::BootstrapAuthority(_)) |
There was a problem hiding this comment.
BLOCKING: Because DiagnosticAttribution is stored in Dag.diagnostics and consumed downstream, it is a substrate coproduct and needs the required 🟢/🟡/🔴 dissolution classification comment.
| &self, | ||
| ) -> impl Iterator<Item = (PortId, &Diagnostic, &DiagnosticAttribution)> { | ||
| self.entries.iter().map(|(p, (d, a))| (*p, d, a)) | ||
| } |
There was a problem hiding this comment.
Invariant violation: BLOCKING: Adding attribution to DiagnosticTable creates a new cross-stage fact, but bootstrap snapshot emission still walks iter() and emits 2-arg inserts, so attribution does not flow through regen output (P2 facts-flow-forward).
|
Review metadata
Verdict: APPROVE — clean, narrowly scoped substrate change. This diff replaces a
Exploratory observations (non-blocking):
|
… attribution Addresses three blocking findings on PR #1587: 1. **Witness validation moves from social to structural.** `BootstrapAuthorityKey::new` previously accepted any `&'static str` and relied on doc-only convention to keep mint sites honest. Adds `BOOTSTRAP_AUTHORITY_PATHS`, an always-on path-only mirror emitted by `build.rs` (siblings of the existing `STAGED_FILES`/`V3_SPECS`/ `COMPILER_FILES`/`EXTDEPS_FILES` payload arrays — same on-disk enumeration that drives the `bootstrap_authority` substrate set). Also adds `crate::bootstrap::DSL_STD_BOOTSTRAP_PATHS` (hand-listed to mirror the 14 `dsl/std/*.dag` rows in `bootstrap_authority.dag`, since `dsl/std/` holds many non-authority files and full-directory enumeration would over-include). `is_bootstrap_authority_path` checks both; `BootstrapAuthorityKey::new` now panics fail-closed on unknown paths so loader-side typos cannot silently mint a bogus witness. 2. **`DiagnosticAttribution` carries dissolution receipt.** Per modeling-discipline review of substrate coproducts, marks the `Unattributed | BootstrapAuthority(_)` enum 🟡 TRANSITIONAL with the explicit dissolution trigger: when PB-Bootstrap-Process makes the loader data-native and the substrate `bootstrap_authority` set is consumed directly, every diagnostic attached during bootstrap inherits its authority reference structurally and the binary attribution split collapses. 3. **Bootstrap snapshot emission forwards attribution (P2 facts-flow- forward).** `regen_bootstrap_emit::render_diagnostics` now walks `iter_attributed()` and emits 3-arg `table.insert(port, diag, attribution)` calls; `render_diagnostic_attribution` renders `DiagnosticAttribution::Unattributed` and `DiagnosticAttribution::BootstrapAuthority(BootstrapAuthorityKey ::new(<path>))`. The static `bootstrap_*_generated.rs` snapshots are still diagnostic-empty in production (the empty-table fast path is unchanged), but the emission contract no longer silently downgrades regen-time diagnostics to `Unattributed`. New tests: - `crate::diagnostics::tests::bootstrap_authority_key_new_panics_on_non_authority_path` (`#[should_panic]`). - `crate::diagnostics::tests::bootstrap_authority_key_validator_accepts_known_authority_rows` — sanity-mints one row from each of the four mirror sets so a regression on any single set surfaces. Validation: - `cargo test -p v3-compiler --lib --features bootstrap-regen-fresh` 305 passed (incl. 8 attribution tests). - `cargo test -p v3-compiler --test integration` 781 passed. - `cargo run -p v3-compiler --features bootstrap-regen-fresh --bin regen_bootstrap` clean (no snapshot delta — production snapshot remains diagnostic-empty). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
Verification PR #1572 STOP+PING: row 82 (
diagnostics_empty_after_bootstrap) couldn't ratchet structurally because consumers had to ask `diagnostic.span().file == bootstrap_authority key` — a forbidden path-string bridge. This PR adds a witness-based attribution surface so bootstrap diagnostics carry structural origin instead.Receipt against `docs/debt/r3-debt-paydown-ledger-2026-05-02.md` row 82 ("Missing diagnostics-empty bootstrap gate"). Verification owns the row-82 closure PR after this substrate surface lands.
Surface added (in `src/v3/compiler/src/diagnostics.rs`)
Dag attach API (in `src/v3/compiler/src/dag.rs`)
Bootstrap loader rewires (per dispatch step 4)
Recommended consumer API (PR #1572 Worker B')
```rust
// Per-port:
dag.diagnostics().attribution(port).map(|a| a.is_bootstrap()).unwrap_or(false)
// Counting / filtering:
let bootstrap_count = dag
.diagnostics()
.iter_attributed()
.filter(|(_, _, a)| a.is_bootstrap())
.count();
```
No `SourceSpan.file` comparison anywhere on the consumer side.
Detached-phantom-port coverage
Every existing bootstrap attach path now goes through `attach_bootstrap_diagnostic`:
All three call sites already used detached phantom ports (`alloc_port(None)` via `attach_diagnostic`); switching to `attach_bootstrap_diagnostic` reuses the same allocator (no fabricated producer node), so detached-phantom-port diagnostics still satisfy the `Unresolved port iff diagnostic` biconditional and now also carry `BootstrapAuthority(_)` attribution.
Focused tests (per dispatch step 5)
Validation commands
Out of scope (per dispatch constraints)
🤖 Generated with Claude Code