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
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -129,4 +129,3 @@ src/v2/test/claim/manual/sg2_scratch_probe.dag
# Regenerable; not committed. Whitelist: ci.yml, .gitignore, .md docs (ROADMAP.md, DESIGN.md, docs/plans/).
/.github/fleet-runner-deploy.manifest
/provisioning/srv3/user-data
/provisioning/srv3/dnsmasq.conf
9 changes: 7 additions & 2 deletions dsl/extdeps/bmc/webui/nbd_proxy_serve.dag
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,17 @@ import extdeps.languages.bash.program {
serialize_bash,
}
import extdeps.bmc.webui.nbd_proxy {
extdeps_external_authority_anchor,
nbd_proxy_role_assignment,
NbdServer,
}
import extdeps.uri { Uri, Https }

data nbd_proxy_serve_transport_authority: ExternalAuthority = extdeps_external_authority_anchor
data extdeps_external_authority_anchor: ExternalAuthority = ExternalAuthority {
uri: Uri {
scheme: Https
locator: "github.com/openbmc/jsnbd"
}
}

data bmcweb_session_token_env_ref: NonEmptyStr = concat("$", "BMCWEB_SESSION_TOKEN") as NonEmptyStr

Expand Down
107 changes: 105 additions & 2 deletions src/v1/stage0/src/cli_run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,19 @@ pub const UNIFIED_CLAIM_VERIFICATION_MODULE: &str = "v2.std.verification";
pub const BOOL_WITNESS_CLAIM_TYPE: &str = "BoolWitnessClaim";
pub const NODE_CORPUS_TYPE: &str = "NodeCorpus";

// cargo's build-output dir (a `target` dir beside a Cargo.toml) is realization
// output, not source: a corpus copy materialized under it (e.g.
// target/func_env_semantic_baseline_corpus/dsl/**) must never enter a module
// index alongside the tree it was copied from. A source root passed FROM
// inside target/ is still walked — only descent into the output dir is refused.
pub(crate) fn is_cargo_target_output_dir(
parent: &std::path::Path,
child: &std::path::Path,
) -> bool {
child.file_name().and_then(|n| n.to_str()) == Some("target")
&& parent.join("Cargo.toml").is_file()
}

fn collect_dag_files(dir: &std::path::Path, files: &mut Vec<std::path::PathBuf>) {
let mut entries: Vec<_> = std::fs::read_dir(dir)
.unwrap_or_else(|e| panic!("failed to read dir {:?}: {}", dir, e))
Expand All @@ -81,6 +94,9 @@ fn collect_dag_files(dir: &std::path::Path, files: &mut Vec<std::path::PathBuf>)
for entry in entries {
let path = entry.path();
if path.is_dir() {
if is_cargo_target_output_dir(dir, &path) {
continue;
}
collect_dag_files(&path, files);
} else if path.extension().map(|e| e == "dag").unwrap_or(false) {
files.push(path);
Expand Down Expand Up @@ -174,6 +190,14 @@ pub fn build_module_path_index(source_roots: &[String]) -> HashMap<String, Strin
})
.to_string_lossy()
.replace('\\', "/");
if let Some(existing) = index.get(&module_path) {
if existing != &rel {
panic!(
"module-path collision: module '{}' is declared by both '{}' and '{}' — one module, one authority (DESIGN §3); silent last-root-wins shadowing broke the floor (extdeps.shell, 2026-07-01) — de-fork or rename one side",
module_path, existing, rel
);
}
}
index.insert(module_path.clone(), rel);
}
}
Expand Down Expand Up @@ -465,6 +489,14 @@ fn build_module_index(source_roots: &[String]) -> ModuleSourceIndex {
.unwrap_or_else(|e| panic!("failed to read {:?}: {}", path, e));
if let Some(module_path) = extract_module_path(&content) {
let rel_path = path.to_string_lossy().to_string();
if let Some(existing) = index.get(&module_path) {
if existing.path != rel_path {
panic!(
"module-path collision: module '{}' is declared by both '{}' and '{}' — one module, one authority (DESIGN §3); silent last-root-wins shadowing broke the floor (extdeps.shell, 2026-07-01) — de-fork or rename one side",
module_path, existing.path, rel_path
);
}
}
index.insert(
module_path,
Rc::new(v1_compiler_compile::SourceFile {
Expand Down Expand Up @@ -2876,6 +2908,9 @@ pub(crate) fn collect_dag_files_tolerant(dir: &Path, out: &mut Vec<PathBuf>) {
for entry in entries.flatten() {
let path = entry.path();
if path.is_dir() {
if is_cargo_target_output_dir(dir, &path) {
continue;
}
collect_dag_files_tolerant(&path, out);
} else if path.extension().and_then(|e| e.to_str()) == Some("dag") {
out.push(path);
Expand Down Expand Up @@ -8533,9 +8568,77 @@ mod module_path_index_tests {
}

#[test]
fn co_root_overlay_last_root_wins_on_duplicate_module_path() {
fn extdeps_shell_resolves_to_the_dsl_authority() {
let path = source_path_for_module_path("extdeps.shell".to_string());
assert_eq!(path, "src/v2/extdeps/shell.dag");
assert_eq!(path, "dsl/extdeps/shell/shell.dag");
}

#[test]
fn duplicate_module_path_across_roots_refuses_loudly() {
let dir = std::env::temp_dir().join(format!(
"gunbc-module-collision-wall-{}",
std::process::id()
));
let root_a = dir.join("root_a");
let root_b = dir.join("root_b");
std::fs::create_dir_all(&root_a).unwrap();
std::fs::create_dir_all(&root_b).unwrap();
std::fs::write(root_a.join("m.dag"), "module collision.example\n").unwrap();
std::fs::write(root_b.join("m.dag"), "module collision.example\n").unwrap();
let roots = vec![
root_a.to_string_lossy().into_owned(),
root_b.to_string_lossy().into_owned(),
];
// RED control: same module declared in two files refuses loudly.
let refused = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
super::build_module_index(&roots)
}))
.is_err();
// GREEN control: distinct modules build fine.
std::fs::write(root_b.join("m.dag"), "module collision.other\n").unwrap();
let built = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
super::build_module_index(&roots)
}))
.is_ok();
std::fs::remove_dir_all(&dir).ok();
assert!(refused, "collision must refuse loudly, not shadow silently");
assert!(built, "distinct modules must still index");
}

#[test]
fn cargo_target_dir_output_never_enters_the_module_index() {
let dir =
std::env::temp_dir().join(format!("gunbc-target-dir-exclusion-{}", std::process::id()));
let root = dir.join("root");
let baseline = root.join("target").join("baseline_corpus");
std::fs::create_dir_all(&baseline).unwrap();
std::fs::write(root.join("m.dag"), "module corpus.example\n").unwrap();
std::fs::write(baseline.join("m.dag"), "module corpus.example\n").unwrap();
let roots = vec![root.to_string_lossy().into_owned()];
// With a Cargo.toml beside it, target/ is build output: the corpus
// copy is skipped and the source file indexes alone (the CI regression:
// target/func_env_semantic_baseline_corpus tripped the collision wall).
std::fs::write(root.join("Cargo.toml"), "[package]\n").unwrap();
let indexed = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
super::build_module_index(&roots)
}));
// RED control: without Cargo.toml the same layout is two source files
// declaring one module — the wall must still refuse.
std::fs::remove_file(root.join("Cargo.toml")).unwrap();
let refused = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
super::build_module_index(&roots)
}))
.is_err();
std::fs::remove_dir_all(&dir).ok();
let index = indexed.expect("cargo target output must be excluded, not collide");
assert!(
index.contains_key("corpus.example"),
"the source-tree declaration must still index"
);
assert!(
refused,
"a plain (non-cargo) target dir is source like any other — collision must refuse"
);
}

#[test]
Expand Down
54 changes: 43 additions & 11 deletions src/v1/tests/src/dependency_pool_index_compile_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,35 +83,67 @@ fn primary_precedence_within_root_duplicate_module_path_panics() {
rm_rf(&out);
}

// The live-tree extdeps.shell collision fixture is gone (the src/v2 fork was
// deleted — one module, one authority, DESIGN §3), so the strict-mode RED
// control synthesizes its own cross-root duplicate instead of depending on a
// defect persisting in the real corpus.
#[test]
fn strict_dependency_pool_index_panics_on_cross_root_extdeps_shell_collision() {
fn strict_dependency_pool_index_panics_on_cross_root_collision() {
let Some(gunbc) = gunbc_bin() else {
eprintln!("skipping: release gunbc binary not found");
return;
};
let ws = workspace_root();
let root_a = temp_dir("strict-cross-root-a");
let root_b = temp_dir("strict-cross-root-b");
fs::write(
root_a.join("dup.dag"),
"module duplicate.across.roots\nfn a() -> Int { 1 }\n",
)
.expect("write root_a dup.dag");
fs::write(
root_b.join("dup.dag"),
"module duplicate.across.roots\nfn b() -> Int { 2 }\n",
)
.expect("write root_b dup.dag");
let out = temp_dir("strict-cross-root-out");
let output = compile_with_roots(
&gunbc,
&ws,
&[&ws.join("dsl"), &ws.join("src/v2")],
"strict",
&out,
);
let output = compile_with_roots(&gunbc, &ws, &[&root_a, &root_b], "strict", &out);
let combined = format!(
"{}{}",
String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr)
);
assert!(
!output.status.success(),
"strict mode must panic on cross-root extdeps.shell collision"
"strict mode must panic on a cross-root duplicate module path; got success"
);
assert!(
combined.contains("duplicate module path"),
"expected duplicate module path panic; got:\n{combined}"
);
// GREEN control: the same fixture under primary-precedence indexes
// first-root-wins instead of refusing at the index layer.
let out_pp = temp_dir("strict-cross-root-pp-out");
let output_pp = compile_with_roots(
&gunbc,
&ws,
&[&root_a, &root_b],
"primary-precedence",
&out_pp,
);
let combined_pp = format!(
"{}{}",
String::from_utf8_lossy(&output_pp.stdout),
String::from_utf8_lossy(&output_pp.stderr)
);
assert!(
combined.contains("extdeps.shell"),
"expected extdeps.shell collision diagnostic; got:\n{combined}"
!combined_pp.contains("duplicate module path"),
"primary-precedence must not refuse the cross-root duplicate at the index layer; got:\n{combined_pp}"
);
rm_rf(&root_a);
rm_rf(&root_b);
rm_rf(&out);
rm_rf(&out_pp);
}

#[test]
Expand Down
45 changes: 0 additions & 45 deletions src/v2/extdeps/shell.dag

This file was deleted.

Loading