Skip to content
Merged
Changes from 7 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
948d658
retire pipeline include_str side channel ratchet
briansrls May 14, 2026
b70553a
Merge remote-tracking branch 'origin/main' into session/neat-deer-474
briansrls May 14, 2026
5ff8d12
Merge remote-tracking branch 'origin/main' into session/neat-deer-474
briansrls May 14, 2026
37c5e23
WIP: R3 gate #34: bridge_include_str_side_channels_retired (T-Bridge-…
briansrls May 14, 2026
c376221
Merge remote-tracking branch 'origin/main' into session/neat-deer-474
briansrls May 14, 2026
18a7793
Merge remote-tracking branch 'origin/main' into session/neat-deer-474
briansrls May 14, 2026
17dd2d4
Merge remote-tracking branch 'origin/main' into session/neat-deer-474
briansrls May 14, 2026
a824142
Merge remote-tracking branch 'origin/main' into session/neat-deer-474
briansrls May 14, 2026
803d27f
Merge remote-tracking branch 'origin/main' into session/neat-deer-474
briansrls May 14, 2026
f508979
clean up pipeline include_str scanner
briansrls May 14, 2026
afbca3f
Merge remote-tracking branch 'origin/main' into session/neat-deer-474
briansrls May 14, 2026
776cb97
Merge remote-tracking branch 'origin/main' into session/neat-deer-474
briansrls May 14, 2026
b336684
document pipeline include_str sentinels
briansrls May 14, 2026
78a58ec
Merge remote-tracking branch 'origin/main' into session/neat-deer-474
briansrls May 14, 2026
dff54cc
Merge remote-tracking branch 'origin/main' into session/neat-deer-474
briansrls May 14, 2026
fe3167e
Merge remote-tracking branch 'origin/main' into session/neat-deer-474
briansrls May 14, 2026
530b2cf
Merge remote-tracking branch 'origin/main' into session/neat-deer-474
briansrls May 14, 2026
d53b40a
Merge remote-tracking branch 'origin/main' into session/neat-deer-474
briansrls May 14, 2026
d00fe23
Merge remote-tracking branch 'origin/main' into session/neat-deer-474
briansrls May 14, 2026
30895e6
Merge remote-tracking branch 'origin/main' into session/neat-deer-474
briansrls May 14, 2026
8c3ff10
Merge remote-tracking branch 'origin/main' into session/neat-deer-474
briansrls May 14, 2026
3fd9ff1
Merge remote-tracking branch 'origin/main' into session/neat-deer-474
briansrls May 14, 2026
5fa3030
Merge remote-tracking branch 'origin/main' into session/neat-deer-474
briansrls May 14, 2026
8207f46
Merge remote-tracking branch 'origin/main' into session/neat-deer-474
briansrls May 14, 2026
5953581
Merge remote-tracking branch 'origin/main' into session/neat-deer-474
briansrls May 14, 2026
cbb46b9
Merge remote-tracking branch 'origin/main' into session/neat-deer-474
briansrls May 14, 2026
81c6884
Merge remote-tracking branch 'origin/main' into session/neat-deer-474
briansrls May 14, 2026
a03f509
Merge remote-tracking branch 'origin/main' into session/neat-deer-474
briansrls May 14, 2026
5adcc6a
Merge remote-tracking branch 'origin/main' into session/neat-deer-474
briansrls May 14, 2026
7f50c5d
Merge remote-tracking branch 'origin/main' into session/neat-deer-474
briansrls May 14, 2026
72cbf1e
Merge remote-tracking branch 'origin/main' into session/neat-deer-474
briansrls May 14, 2026
ca2b8cd
Merge remote-tracking branch 'origin/main' into session/neat-deer-474
briansrls May 14, 2026
65d30ea
Merge remote-tracking branch 'origin/main' into session/neat-deer-474
briansrls May 14, 2026
5c21923
Harden include_str pipeline ratchet scanner
briansrls May 14, 2026
72a6040
Merge remote-tracking branch 'origin/main' into session/neat-deer-474
briansrls May 14, 2026
d91fb05
Merge remote-tracking branch 'origin/main' into session/neat-deer-474
briansrls May 14, 2026
59f4bc4
Merge remote-tracking branch 'origin/main' into session/neat-deer-474
briansrls May 14, 2026
6fc27fa
Handle lifetimes in pipeline include ratchet
briansrls May 14, 2026
416bc84
WIP: R3 gate #34: bridge_include_str_side_channels_retired (T-Bridge-…
briansrls May 14, 2026
0ac4ccc
WIP: R3 gate #34: bridge_include_str_side_channels_retired (T-Bridge-…
briansrls May 14, 2026
8b62fb3
Handle raw string fragments in pipeline include ratchet
briansrls May 14, 2026
ab4cbbd
Handle char fragments in pipeline include ratchet
briansrls May 14, 2026
464537f
Decode escapes in pipeline include ratchet
briansrls May 14, 2026
c070f86
Merge remote-tracking branch 'origin/main' into session/neat-deer-474
briansrls May 14, 2026
32c07a5
Merge remote-tracking branch 'origin/main' into session/neat-deer-474
briansrls May 15, 2026
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
139 changes: 110 additions & 29 deletions src/v3/compiler/tests/integration/l1_5_fixed_point_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,35 @@ use std::path::{Path, PathBuf};

use v3_compiler::dag::{ArrowBody, TypeConnective};
use v3_compiler::{
compare_stage_snapshots, compile_stage_snapshots, default_fixed_point_source, parse_for_test,
tokenize_for_test, Dag,
compare_stage_snapshots, compile_stage_snapshots, default_fixed_point_source, Dag,
};

/// Matches `pipeline_authority::PIPELINE_AUTHORITY_FILE` — integration tests cannot import `pub(crate)` helpers.
const PIPELINE_AUTHORITY_FILE: &str = "src/v3/compiler/pipeline.dag";

#[test]
fn pipeline_dag_parses() {
let source = include_str!("../../pipeline.dag");
let tokens = tokenize_for_test(source, "pipeline.dag")
.unwrap_or_else(|diag| panic!("tokenize pipeline.dag failed: {diag:?}"));
let _module = parse_for_test(&tokens, "pipeline.dag")
.unwrap_or_else(|diag| panic!("parse pipeline.dag failed: {diag:?}"));
fn pipeline_dag_bootstrap_authority_is_loaded_structurally() {
let dag = Dag::new();
let pipeline_types = [
"PipelineStageBinding",
"PipelineSnapshotKind",
"CompilerHostRealization",
];
for name in pipeline_types {
let decl = dag
.declaration_by_name(name)
.unwrap_or_else(|| panic!("pipeline authority declaration `{name}` present"));
assert_eq!(
decl.span.file.as_str(),
PIPELINE_AUTHORITY_FILE,
"`{name}` must come from the bootstrapped pipeline authority"
);
}
}

/// Ratchet for `bridge_include_str_side_channels_retired` (pipeline slice): library sources under
/// `src/v3/compiler/src/` must not embed `pipeline.dag` via `include_str!` — ordering authority
/// stays structural (`PipelineStageBinding` / bootstrap witness). Integration tests may still
/// `include_str!("../../pipeline.dag")` for parse fixtures (`pipeline_dag_parses`).
/// `src/v3/compiler/src/` and compiler tests must not embed `pipeline.dag` via `include_str!` —
/// ordering authority stays structural (`PipelineStageBinding` / bootstrap witness).
fn collect_rs_files(dir: &Path, out: &mut Vec<PathBuf>) {
let entries =
fs::read_dir(dir).unwrap_or_else(|e| panic!("read_dir {} failed: {e}", dir.display()));
Expand All @@ -40,40 +49,112 @@ fn collect_rs_files(dir: &Path, out: &mut Vec<PathBuf>) {
}
}

fn include_str_pipeline_dag_offenders(manifest_dir: &Path, path: &Path, text: &str) -> Vec<String> {
let forbidden_macro = concat!("include", "_str!");
let forbidden_path = concat!("pipeline", ".dag");
let mut offenders = Vec::new();
for (idx, _) in text.match_indices(forbidden_macro) {
let line_start = text[..idx].rfind('\n').map_or(0, |pos| pos + 1);
let line = &text[line_start..text[idx..].find('\n').map_or(text.len(), |pos| idx + pos)];
let trimmed = line.trim_start();
if trimmed.starts_with("//") {
continue;
}

let after_macro = idx + forbidden_macro.len();
let Some(open_offset) = text[after_macro..].find('(') else {
continue;
};
if !text[after_macro..after_macro + open_offset]
.chars()
.all(char::is_whitespace)
{
continue;
}
let open = after_macro + open_offset;
let mut depth = 0usize;
let mut close = None;
for (pos, ch) in text[open..].char_indices() {
match ch {
'(' => depth += 1,
')' => {
depth -= 1;
if depth == 0 {
close = Some(open + pos);
break;
}
}
_ => {}
}
}
let Some(close) = close else {
continue;
};

let macro_arg = &text[open + 1..close];
let joined_literals: String = macro_arg
.split('"')
.skip(1)
.step_by(2)
.collect::<Vec<_>>()
.join("");
if macro_arg.contains(forbidden_path) || joined_literals.contains(forbidden_path) {
offenders.push(format!(
"{}:{}:{}",
path.strip_prefix(manifest_dir).unwrap_or(path).display(),
text[..idx].bytes().filter(|byte| *byte == b'\n').count() + 1,
trimmed
));
}
}
offenders
}

#[test]
fn compiler_src_has_no_include_str_pipeline_dag_authority() {
fn compiler_sources_and_tests_have_no_include_str_pipeline_dag_authority() {
let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let src_dir = manifest_dir.join("src");
let mut files = Vec::new();
collect_rs_files(&src_dir, &mut files);
collect_rs_files(&manifest_dir.join("src"), &mut files);
collect_rs_files(&manifest_dir.join("tests"), &mut files);
files.sort();
let mut offenders = Vec::new();
let forbidden_macro = concat!("include", "_str!");
let forbidden_path = concat!("pipeline", ".dag");
for path in files {
let text = fs::read_to_string(&path)
.unwrap_or_else(|e| panic!("read {} failed: {e}", path.display()));
for (idx, line) in text.lines().enumerate() {
let trimmed = line.trim_start();
if trimmed.starts_with("//") {
continue;
}
if line.contains("include_str!") && line.contains("pipeline.dag") {
offenders.push(format!(
"{}:{}:{}",
path.strip_prefix(&manifest_dir).unwrap_or(&path).display(),
idx + 1,
trimmed
));
}
}
offenders.extend(include_str_pipeline_dag_offenders(
&manifest_dir,
&path,
&text,
));
}
assert!(
offenders.is_empty(),
"`src/**/*.rs` must not use include_str! on pipeline.dag (see bridge_ledger \
"`src/**/*.rs` and `tests/**/*.rs` must not use {} on {} (see bridge_ledger \
bridge_include_str_side_channels_retired + pipeline_authority.rs). Offenders:\n{}",
forbidden_macro,
forbidden_path,
offenders.join("\n")
);
}

#[test]
fn include_str_pipeline_dag_ratchet_catches_split_literals() {
let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
let path = manifest_dir.join("tests/integration/synthetic.rs");
let synthetic = format!(
r#"
const OK: &str = include{}("other.dag");
const BAD: &str = include{}(concat!("../../", "pipeline", ".dag"));
"#,
"_str!", "_str!"
);
let offenders = include_str_pipeline_dag_offenders(manifest_dir, &path, &synthetic);
assert_eq!(offenders.len(), 1);
assert!(offenders[0].contains("synthetic.rs:3:"));
}

#[test]
fn bootstrap_loads_pipeline_stage_realizations() {
let dag = Dag::new();
Expand Down
Loading