Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/briefs/bridge-retirement-audit-sourcespan-family.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,11 @@ shape · **(d)** sibling / blocker.
| # | Bridge entry | (a) Declaration / anchor | (b) Consumers | (c) Retirement shape | (d) Sibling / blocker |
| --- | --- | --- | --- | --- | --- |
| 1 | **`dsl_std_render_repeat_string_decl_id` authority via `span.file.ends_with`** | `lower.rs` (`dsl_std_render_repeat_string_decl_id`, `REPEAT_STRING_AUTHORITY_SUFFIXES`) | 1 (`try_lower_repeat_string_string_data` call path) | `DeclarationId` (or `DeclarationRef`) wired to the single canonical `repeat_string` data decl; delete suffix table. | Duplicate `dsl/std` vs bootstrap excerpt convergence (ROADMAP T-P0 narrative); `declaration_by_name` still finds `repeat_string` today. |
| 2 | **Kernel `Bool` bootstrap patch lookup** | `bootstrap.rs` `patch_kernel_bool_boolean_algebra_inhabits` (`BOOL_TYPES_FILE`, `span.file ==`) | 1 (+ tests in same file) | Express `Bool` `inhabits` in `dsl/std/types.dag` when v2 accepts syntax; delete patch + file gate. | v2 `dsl/` parse authority (commented dissolution at site). |
| 2 | **Kernel `Bool` bootstrap patch lookup** ◐ partial (bootstrap.rs slice — PR #2150) | `bootstrap.rs` `patch_kernel_bool_boolean_algebra_inhabits` (`BOOL_TYPES_FILE`, `span.file ==`) | 1 (+ tests in same file) | Partial retirement (bootstrap.rs slice 1 of 2): the bare path constant `BOOL_TYPES_FILE` is gone; the participation gate is now routed through `BootstrapAuthorityKey::for_kernel_bool().path()` (typed-witness egress) rather than a free string constant, and the not-found diagnostic carries `DiagnosticAttribution::BootstrapAuthority`. The `span.file ==` predicate itself remains — `declaration_by_name` is rank-biased on `span.file` (audit row #14), so dropping the gate would let a non-kernel duplicate be selected and silently attributed to `dsl/std/types.dag`. Full structural dissolution (a kernel-`Bool` `DeclarationId` accessor / row-#14 rank retirement / `Bool inhabits` upstreamed into `dsl/std/types.dag` once v2 accepts the syntax) lands when row #14 retires. | v2 `dsl/` parse authority (commented dissolution at site); audit row #14 (`declaration_name_preference_rank`). |
| 3 | **`Dimension` phantom / surface validation gated on authority file** | `lower.rs` `DIMENSION_STD_AUTHORITY_FILE`, `validate_dimension_phantom_surface_item`, `attach_dimension_phantom_parameter` | 3 direct file compares + unit tests in `lower.rs` | Substrate marker (`meta_tag` / nominal module stamp) on the std `Dimension` record; delete `src/v3/std/dimensions.dag` path string. | Depends on stable `Dimension` home in std (not file-string). |
| 4 | **`error_primitives` authority file gate** | `infer.rs` + `emit.rs` `ERROR_PRIMITIVES_AUTHORITY_FILE` (`decl.span.file ==` / `!=`) | 4 sites (2 infer, 1 emit gate, const) | Import-graph or `DeclarationRef` to error-primitive decl set; no bare path compare. | Bootstrap ordering / duplicate std mirrors. |
| 5 | **`dsl/std/types.dag` type-alias refinement placeholder** | `lower.rs` `lower_type_alias_refinements_phase` (`span.file == "dsl/std/types.dag"`) | 1 phase (all `where` aliases in that file) | Resolved Bool-level helpers **or** `meta_tag` for doc-only refinements **or** PB-1 authority list not keyed by string (see comment `lower.rs:829-835`). | PB-1 diagnostic-empty bootstrap gate; may interact with #2 when `types.dag` grows `inhabits`. |
| 6 | **Pipeline authority file guard** | `bootstrap.rs` (`span.file == PIPELINE_AUTHORITY_FILE`); `pipeline_authority.rs` (`decl.span.file == PIPELINE_AUTHORITY_FILE` in stage binding walk) | 2 + structural `PipelineStageBinding` consumers | Typed pipeline-stage graph only; drop file compare when compile arrow is lowered (`bridge_include_str_side_channels_retired` prerequisite). | Same structural gap as ledger `bridge_include_str_side_channels_retired` (compile body still `ArrowBody::Unparsed`). |
| 6 | **Pipeline authority file guard** ✅ retired (bootstrap.rs slice — PR #2150; `pipeline_authority.rs` stage-binding walk remains under separate ownership) | `bootstrap.rs` (`span.file == PIPELINE_AUTHORITY_FILE`); `pipeline_authority.rs` (`decl.span.file == PIPELINE_AUTHORITY_FILE` in stage binding walk) | 2 + structural `PipelineStageBinding` consumers | Retired (bootstrap.rs slice 1 of 2): `report_pipeline_authority_error` carries the offending stage's actual `SourceSpan` (in-loop) or a witness-derived synthetic span (pre-loop); `BootstrapAuthorityKey::for_pipeline_authority()` encapsulates the path-string. `pipeline_authority.rs` stage-binding walk + `compile` arrow lowering remain (separate audit row owners). | Same structural gap as ledger `bridge_include_str_side_channels_retired` (compile body still `ArrowBody::Unparsed`). |
| 7 | **`reflect_program_dag_nodes_in_file` + `behavior_source_file`** | `lens_apply.rs` (`behavior_source_file`, `reflect_program_dag_nodes_in_file`) | 7 references across `src/v3` (runner + tests); **all** lens folds using file partition | `CompilationUnitId` / `ModuleId` (or declaration-owned “authored here” bit) on `Behavior` nodes; filter by id, not string. | Stamping must happen at lower/bind creation; touches `compile_to_dag` contract. |
| 8 | **`fold_lens_over_reflected_program`** | `lens_apply.rs` (wraps #7 + `apply_lens_declaration`) | 3 unit tests in `lens_apply.rs` + indirect runner use | Absorbs #7; same carrier. | Blocked on #7. |
| 9 | **`TestClaim` / runner `claim.file_name` as logical compilation unit** | `test_runner.rs` (`compile_to_dag(&claim.source, &claim.file_name)`, `find_bind(…, &claim.file_name)`, `decl.span.file == claim.file_name`) | **11** `compile_to_dag(&claim.source, &claim.file_name)`; additional `find_bind` / reflect call sites | `TestClaim` carries `DeclarationRef` / role-tagged program root; runner does not invent parallel filenames for identity. | **B4.1** `DeclarationRef` migration; overlaps **lens-name** family for lens selection. |
Expand Down
92 changes: 71 additions & 21 deletions src/v3/compiler/src/bootstrap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@

use crate::dag::{ArrowBody, Dag, Declaration, TemplateArgument, TypeConnective};
use crate::diagnostics::{Diagnostic, SourceSpan};
use crate::pipeline_authority::{ordered_pipeline_stages, PIPELINE_AUTHORITY_FILE};
use crate::pipeline_authority::ordered_pipeline_stages;

// Used by `materialize_pipeline_realizations` (regen + unit tests below). When
// `bootstrap-regen-fresh` is off, that path is cfg-dead in non-test lib builds.
Expand Down Expand Up @@ -121,21 +121,31 @@ pub const BOOTSTRAP_FIXTURE_PATH_KEYS: &[&str] = &[
/// `dsl/std/types.dag` and delete `patch_kernel_bool_boolean_algebra_inhabits`).
#[cfg_attr(not(feature = "bootstrap-regen-fresh"), allow(dead_code))]
pub(crate) fn patch_kernel_bool_boolean_algebra_inhabits(dag: &mut Dag) {
const BOOL_TYPES_FILE: &str = "dsl/std/types.dag";
let bool_authority = crate::diagnostics::BootstrapAuthorityKey::new(BOOL_TYPES_FILE);
// Audit row #2 retirement (bootstrap.rs slice 1 of 2):
// The path-string `dsl/std/types.dag` is encapsulated behind
// `BootstrapAuthorityKey::for_kernel_bool()` and is no longer named
// in this file outside doc-comments. The participation gate is
// retained — `declaration_by_name` still rank-biases on `span.file`
// (audit row #14, separate owner) so a bare lookup could surface a
// non-kernel duplicate; we walk the declarations directly and gate
// by the authority's `path()` egress so the witness, not a free
// constant, is the structural source. Full dissolution (delete the
// gate; rely on a structural kernel-`Bool` `DeclarationId` accessor)
// lands when row #14 retires.
let bool_authority = crate::diagnostics::BootstrapAuthorityKey::for_kernel_bool();
let Some(bool_decl) = dag
.declarations()
.iter()
.find(|d| d.name.as_deref() == Some("Bool") && d.span.file == BOOL_TYPES_FILE)
.find(|d| d.name.as_deref() == Some("Bool") && d.span.file == bool_authority.path())
else {
let authority_span = SourceSpan::new(bool_authority.path(), 0, 0);
dag.attach_bootstrap_diagnostic(
bool_authority,
Diagnostic::ResolveError {
name: format!(
"bootstrap: Lane 1e-2b Path A — kernel `Bool` not found in `{BOOL_TYPES_FILE}`; \
name: "bootstrap: Lane 1e-2b Path A — kernel `Bool` not found in bootstrap Dag; \
cannot set `Declaration.inhabits` for `BooleanAlgebra<Bool>`"
),
span: SourceSpan::new(BOOL_TYPES_FILE, 0, 0),
.to_string(),
span: authority_span,
fixes: Vec::new(),
},
);
Expand Down Expand Up @@ -220,7 +230,7 @@ pub(crate) fn materialize_pipeline_realizations(dag: &mut Dag) {
let stages = match ordered_pipeline_stages(dag) {
Ok(stages) => stages,
Err(error) => {
report_pipeline_authority_error(dag, error);
report_pipeline_authority_error(dag, error, pipeline_authority_span());
return;
}
};
Expand All @@ -231,6 +241,7 @@ pub(crate) fn materialize_pipeline_realizations(dag: &mut Dag) {
report_pipeline_authority_error(
dag,
format!("missing pipeline realization meta `{PIPELINE_REALIZATION_META}`"),
pipeline_authority_span(),
);
return;
};
Expand All @@ -242,12 +253,14 @@ pub(crate) fn materialize_pipeline_realizations(dag: &mut Dag) {
format!(
"pipeline realization meta `{PIPELINE_REALIZATION_META}` must lower to a record"
),
dag.declaration(meta_decl_id).span.clone(),
);
return;
}
};

for stage in &stages {
let realization_span = dag.declaration(stage.realization).span.clone();
let realization = dag.declaration_mut(stage.realization);
if realization.meta_tag != Some(meta_decl_id) {
report_pipeline_authority_error(
Expand All @@ -256,13 +269,15 @@ pub(crate) fn materialize_pipeline_realizations(dag: &mut Dag) {
"pipeline realization `{}` is not tagged with `{PIPELINE_REALIZATION_META}`",
stage.realization_name
),
realization_span,
);
continue;
}
realization.connective = meta_connective.clone();
}

for stage in stages {
let stage_span = dag.declaration(stage.stage).span.clone();
let stage_decl = dag.declaration_mut(stage.stage);
match &mut stage_decl.connective {
TypeConnective::Arrow { body, .. } => {
Expand All @@ -274,18 +289,33 @@ pub(crate) fn materialize_pipeline_realizations(dag: &mut Dag) {
"pipeline stage `{}` must lower to an arrow",
stage.stage_name
),
stage_span,
),
}
}
}

/// Synthesize a span for pipeline-authority errors that occur before any
/// stage binding is in hand (binding-type missing, meta-type missing,
/// etc.). The path string is routed through the
/// [`BootstrapAuthorityKey`] egress accessor (display-only), not pulled
/// from the `PIPELINE_AUTHORITY_FILE` constant — this keeps the witness
/// the structural source for bootstrap-attribution; the `(file, 0, 0)`
/// shape remains diagnostic display, not a participation key (audit
/// row #6 retirement; bootstrap.rs slice 1 of 2).
#[cfg_attr(not(feature = "bootstrap-regen-fresh"), allow(dead_code))]
fn pipeline_authority_span() -> SourceSpan {
let key = crate::diagnostics::BootstrapAuthorityKey::for_pipeline_authority();
SourceSpan::new(key.path(), 0, 0)
}

#[cfg_attr(not(feature = "bootstrap-regen-fresh"), allow(dead_code))]
fn report_pipeline_authority_error(dag: &mut Dag, name: String) {
fn report_pipeline_authority_error(dag: &mut Dag, name: String, span: SourceSpan) {
dag.attach_bootstrap_diagnostic(
crate::diagnostics::BootstrapAuthorityKey::new(PIPELINE_AUTHORITY_FILE),
crate::diagnostics::BootstrapAuthorityKey::for_pipeline_authority(),
Diagnostic::ResolveError {
name,
span: SourceSpan::new(PIPELINE_AUTHORITY_FILE, 0, 0),
span,
fixes: Vec::new(),
},
);
Expand Down Expand Up @@ -450,6 +480,8 @@ mod tests {

#[test]
fn malformed_pipeline_stage_attaches_diagnostic() {
use crate::diagnostics::BootstrapAuthorityKey;

let mut dag = Dag::new();
assert!(dag.diagnostics().is_empty(), "bootstrap should start clean");

Expand All @@ -463,14 +495,32 @@ mod tests {

materialize_pipeline_realizations(&mut dag);

assert!(
dag.diagnostics().iter().any(|(_, diag)| matches!(
diag,
Diagnostic::ResolveError { name, span, .. }
if name.contains("pipeline stage `parse`")
&& span.file == PIPELINE_AUTHORITY_FILE
)),
"malformed pipeline authority should fail closed with a diagnostic"
// Consumer-side dispatch on attribution witness, not on
// `span.file == PIPELINE_AUTHORITY_FILE` (audit row #6
// retirement; bootstrap.rs slice 1 of 2).
let expected_key = BootstrapAuthorityKey::for_pipeline_authority();
let mut matched = 0usize;
for (_, diag, attribution) in dag.diagnostics().iter_attributed() {
let Diagnostic::ResolveError { name, .. } = diag else {
continue;
};
if !name.contains("pipeline stage `parse`") {
continue;
}
assert!(
attribution.is_bootstrap(),
"malformed-stage diagnostic must carry BootstrapAuthority attribution, got {attribution:?}"
);
assert_eq!(
attribution.as_bootstrap_authority(),
Some(&expected_key),
"attribution witness must match pipeline-authority key"
);
matched += 1;
}
assert_eq!(
matched, 1,
"expected exactly one bootstrap-attributed pipeline-stage diagnostic, got {matched}"
);
}

Expand Down Expand Up @@ -536,7 +586,7 @@ mod tests {

super::patch_kernel_bool_boolean_algebra_inhabits(&mut dag);

let expected_key = BootstrapAuthorityKey::new("dsl/std/types.dag");
let expected_key = BootstrapAuthorityKey::for_kernel_bool();
let mut bootstrap_attributed = 0usize;
for (port, diag, attribution) in dag.diagnostics().iter_attributed() {
// Sanity-check: the message we expect from this scenario.
Expand Down
21 changes: 21 additions & 0 deletions src/v3/compiler/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -748,6 +748,27 @@ impl BootstrapAuthorityKey {
Self(path)
}

/// Typed witness for the kernel-`Bool` `inhabits` patch authority
/// (`dsl/std/types.dag` row of the substrate `bootstrap_authority`
/// set, classified `StdAuthority`). Encapsulates the path-string so
/// `bootstrap.rs` consumers do not name `dsl/std/types.dag` directly
/// — audit-row #2 retirement (bootstrap.rs slice 1 of 2). Reviewers
/// must treat new `for_*` accessors as additions to the witness
/// surface; each one represents a hand-Rust authority site.
pub(crate) fn for_kernel_bool() -> Self {
Self::new("dsl/std/types.dag")
}

/// Typed witness for the pipeline-authority row
/// (`src/v3/compiler/pipeline.dag` of the substrate
/// `bootstrap_authority` set, classified `CompilerAuthority`).
/// Encapsulates the path-string so `bootstrap.rs` consumers do not
/// name `src/v3/compiler/pipeline.dag` directly — audit-row #6
/// retirement (bootstrap.rs slice 1 of 2).
pub(crate) fn for_pipeline_authority() -> Self {
Self::new("src/v3/compiler/pipeline.dag")
}

/// Canonical bootstrap-authority path. **Display only.** Consumers
/// dispatching on attribution must use witness equality, not this
/// string, to decide bootstrap membership; a string compare against
Expand Down
1 change: 1 addition & 0 deletions src/v3/compiler/src/pipeline_authority.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::dag::{Dag, DeclarationId, FieldValue, TypeConnective, ValueBody};

#[cfg(test)]
pub(crate) const PIPELINE_AUTHORITY_FILE: &str = "src/v3/compiler/pipeline.dag";

const PIPELINE_STAGE_BINDING_TYPE: &str = "PipelineStageBinding";
Expand Down
Loading