Skip to content

Commit 881d7a1

Browse files
committed
refactor: move package graph review to review boundary
1 parent 500f1d5 commit 881d7a1

5 files changed

Lines changed: 122 additions & 50 deletions

File tree

crates/rsscript-compiler/src/package.rs

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,27 @@ mod check;
4646
mod dependency {
4747
pub(super) use rsscript_package_review::*;
4848
}
49-
mod graph;
49+
// Legacy composition only: graph evidence is package-review-owned. The
50+
// compiler supplies the captured-input authorization and native wrapper seam.
51+
mod graph {
52+
use std::path::Path;
53+
54+
use rsscript_package_model::{PackageGraphCheck, PackageTree};
55+
56+
pub(super) fn package_tree_captured(package_dir: &Path) -> Result<PackageTree, String> {
57+
rsscript_package_review::package_tree_captured(
58+
package_dir,
59+
super::native::package_native_rust_review,
60+
)
61+
}
62+
63+
pub(super) fn check_package_graph(package_dir: &Path) -> Result<PackageGraphCheck, String> {
64+
rsscript_package_review::check_package_graph(
65+
package_dir,
66+
super::native::package_native_rust_review,
67+
)
68+
}
69+
}
5070
// Legacy composition only: package lock semantics and hashing are owned by
5171
// `rsscript-package-review`; the compiler supplies native-wrapper callbacks.
5272
mod lock {
@@ -55,8 +75,7 @@ mod lock {
5575
use rsscript_package_model::{PackageLock, PackageLockDiff};
5676

5777
pub(super) use rsscript_package_review::{
58-
compare_locked_packages, effective_interface_hash, package_lock_diff_reasons,
59-
parse_package_lock, read_package_lock,
78+
compare_locked_packages, package_lock_diff_reasons, parse_package_lock, read_package_lock,
6079
};
6180
pub(super) const PACKAGE_LOCK_MAX_BYTES: u64 = rsscript_package_review::PACKAGE_LOCK_MAX_BYTES;
6281

@@ -145,11 +164,14 @@ pub use authorization::{
145164
prepare_package_for_execution,
146165
};
147166
pub use check::check_package_dir;
148-
use dependency::{
149-
PackageDependencySpec, collect_dependency_interface_sources,
150-
collect_dependency_lowering_sources,
151-
};
152-
pub use graph::package_tree;
167+
use dependency::{collect_dependency_interface_sources, collect_dependency_lowering_sources};
168+
pub fn package_tree(package_dir: &Path) -> Result<PackageTree, String> {
169+
let snapshot = authorization::snapshot_package_graph_inputs(package_dir)?;
170+
let mut tree = graph::package_tree_captured(snapshot.root())
171+
.map_err(|error| snapshot.remap_error(error))?;
172+
authorization::remap_tree(&snapshot, &mut tree);
173+
Ok(tree)
174+
}
153175
pub(super) use lock_format::package_lock_toml;
154176
pub use metadata::package_lowering_input;
155177
pub(crate) use native::package_native_plugin_build_dependencies;
@@ -214,14 +236,6 @@ pub(super) fn dedup_diagnostics(diagnostics: &mut Vec<Diagnostic>) {
214236
});
215237
}
216238

217-
fn package_identity(manifest: &Manifest) -> PackageIdentity {
218-
PackageIdentity {
219-
name: manifest.package.name.clone(),
220-
version: manifest.package.version.clone(),
221-
edition: manifest.package.edition.clone(),
222-
}
223-
}
224-
225239
#[cfg(test)]
226240
mod preparation_limit_tests {
227241
use super::*;

crates/rsscript-compiler/src/package/graph.rs renamed to crates/rsscript-package-review/src/graph.rs

Lines changed: 56 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,60 @@
11
use std::collections::{BTreeMap, BTreeSet};
22
use std::path::Path;
33

4-
use super::dependency::{
4+
use crate::dependency::{
55
DependencyResolutionScope, ResolvedDependencyEdge, ResolvedDependencyGraph,
66
resolve_dependency_graph,
77
};
8-
use super::lock::effective_interface_hash;
9-
use super::review::review_package_dir_captured_with_features;
10-
use super::source_set::{
8+
use crate::lock::effective_interface_hash;
9+
use crate::review::{NativeRustReviewFn, review_package_dir_captured_with_features};
10+
use crate::source_set::{
1111
ManifestDependencyBudget, ManifestProviderChoice, load_package_manifest,
1212
load_package_with_features,
1313
};
14-
use super::{
15-
PackageDependencyKind, PackageDependencySpec, PackageGraphCheck, PackageRisk, PackageTree,
16-
PackageTreeNode, PackageTreeSummary, package_identity,
14+
use rsscript_package_model::{
15+
PackageDependencyKind, PackageGraphCheck, PackageIdentity, PackageProviderImplementation,
16+
PackageReviewFileKind, PackageRisk, PackageTree, PackageTreeNode, PackageTreeSummary,
17+
PackageVirtual,
1718
};
1819

19-
pub fn package_tree(package_dir: &Path) -> Result<PackageTree, String> {
20-
let snapshot = super::authorization::snapshot_package_graph_inputs(package_dir)?;
21-
let mut tree =
22-
package_tree_captured(snapshot.root()).map_err(|error| snapshot.remap_error(error))?;
23-
super::authorization::remap_tree(&snapshot, &mut tree);
24-
Ok(tree)
20+
use crate::{Manifest, PackageDependencySpec, PackageSource};
21+
22+
fn package_identity(manifest: &Manifest) -> PackageIdentity {
23+
PackageIdentity {
24+
name: manifest.package.name.clone(),
25+
version: manifest.package.version.clone(),
26+
edition: manifest.package.edition.clone(),
27+
}
2528
}
2629

27-
pub(super) fn package_tree_captured(package_dir: &Path) -> Result<PackageTree, String> {
30+
/// Build a package tree from captured project inputs. Native Rust wrapper
31+
/// inspection stays outside this boundary and is injected by the legacy
32+
/// compatibility host.
33+
pub fn package_tree_captured(
34+
package_dir: &Path,
35+
native_rust_review: NativeRustReviewFn,
36+
) -> Result<PackageTree, String> {
2837
let graph = resolve_dependency_graph(package_dir, DependencyResolutionScope::Development)?;
2938
let root = package_tree_node(
3039
&graph,
3140
&graph.root,
3241
PackageDependencyKind::Root,
3342
None,
3443
&mut BTreeMap::new(),
44+
native_rust_review,
3545
)?;
3646
let mut summary = PackageTreeSummary::default();
3747
collect_package_tree_summary(&root, &mut summary, &mut BTreeSet::new());
3848
Ok(PackageTree { root, summary })
3949
}
4050

41-
pub(super) fn check_package_graph(package_dir: &Path) -> Result<PackageGraphCheck, String> {
51+
/// Validate graph-level review facts from captured project inputs.
52+
pub fn check_package_graph(
53+
package_dir: &Path,
54+
native_rust_review: NativeRustReviewFn,
55+
) -> Result<PackageGraphCheck, String> {
4256
let root_manifest = load_package_manifest(package_dir)?;
43-
let tree = package_tree_captured(package_dir)?;
57+
let tree = package_tree_captured(package_dir, native_rust_review)?;
4458
let mut packages_by_name: BTreeMap<String, BTreeSet<String>> = BTreeMap::new();
4559
collect_package_graph_identities(&tree.root, &mut packages_by_name);
4660

@@ -273,7 +287,10 @@ fn canonical_graph_source(source: &str) -> String {
273287
let Some(path) = source.strip_prefix("path+") else {
274288
return source.to_string();
275289
};
276-
format!("path+{}", super::canonical_path_label(Path::new(path)))
290+
format!(
291+
"path+{}",
292+
rsscript_project::canonical_project_path_label(Path::new(path))
293+
)
277294
}
278295

279296
fn package_tree_node(
@@ -282,6 +299,7 @@ fn package_tree_node(
282299
dependency_kind: PackageDependencyKind,
283300
incoming: Option<&ResolvedDependencyEdge>,
284301
cache: &mut BTreeMap<(String, PackageDependencyKind), PackageTreeNode>,
302+
native_rust_review: NativeRustReviewFn,
285303
) -> Result<PackageTreeNode, String> {
286304
let cache_key = (key.to_string(), dependency_kind);
287305
if let Some(cached) = cache.get(&cache_key) {
@@ -295,7 +313,11 @@ fn package_tree_node(
295313
let package_dir = &resolved.package_dir;
296314
let features = resolved.features.clone();
297315
let package = load_package_with_features(package_dir, Some(&features))?;
298-
let review = review_package_dir_captured_with_features(package_dir, Some(&features))?;
316+
let review = review_package_dir_captured_with_features(
317+
package_dir,
318+
Some(&features),
319+
native_rust_review,
320+
)?;
299321
let interface_effective_hash = effective_interface_hash(&package.sources, &features);
300322
let identity = package_identity(&package.manifest);
301323
let mut dependencies = Vec::new();
@@ -306,7 +328,14 @@ fn package_tree_node(
306328
edge.kind
307329
};
308330
dependencies.push(match &edge.target {
309-
Some(target) => package_tree_node(graph, target, child_kind, Some(edge), cache)?,
331+
Some(target) => package_tree_node(
332+
graph,
333+
target,
334+
child_kind,
335+
Some(edge),
336+
cache,
337+
native_rust_review,
338+
)?,
310339
None => {
311340
unresolved_dependency_node(edge.spec.clone(), child_kind, unresolved_reasons(edge))
312341
}
@@ -319,7 +348,7 @@ fn package_tree_node(
319348
name: identity.name,
320349
version: Some(identity.version),
321350
requirement: spec.and_then(|spec| spec.requirement.clone()),
322-
source: super::package_path_source(package_dir),
351+
source: rsscript_project::project_path_source(package_dir),
323352
risk: review.risk,
324353
features,
325354
native: review.native_rust.is_some(),
@@ -391,14 +420,12 @@ fn unresolved_dependency_node(
391420
}
392421
}
393422

394-
fn package_provider_implementations(
395-
manifest: &super::Manifest,
396-
) -> Vec<super::PackageProviderImplementation> {
423+
fn package_provider_implementations(manifest: &Manifest) -> Vec<PackageProviderImplementation> {
397424
manifest
398425
.implements
399426
.iter()
400427
.map(
401-
|(interface_package, implementation)| super::PackageProviderImplementation {
428+
|(interface_package, implementation)| PackageProviderImplementation {
402429
interface_package: interface_package.clone(),
403430
version: implementation.version.clone(),
404431
interface_features: implementation.interface_features.clone(),
@@ -408,23 +435,23 @@ fn package_provider_implementations(
408435
.collect()
409436
}
410437

411-
fn package_virtual(manifest: &super::Manifest) -> Option<super::PackageVirtual> {
438+
fn package_virtual(manifest: &Manifest) -> Option<PackageVirtual> {
412439
manifest
413440
.virtual_package
414441
.as_ref()
415-
.map(|virtual_package| super::PackageVirtual {
442+
.map(|virtual_package| PackageVirtual {
416443
has_default: virtual_package.has_default,
417444
provider: virtual_package.provider.clone(),
418445
})
419446
}
420447

421-
fn package_is_interface_only(sources: &[super::PackageSource]) -> bool {
448+
fn package_is_interface_only(sources: &[PackageSource]) -> bool {
422449
let has_interface = sources
423450
.iter()
424-
.any(|source| source.kind == super::PackageReviewFileKind::Interface);
451+
.any(|source| source.kind == PackageReviewFileKind::Interface);
425452
let has_source = sources
426453
.iter()
427-
.any(|source| source.kind == super::PackageReviewFileKind::Source);
454+
.any(|source| source.kind == PackageReviewFileKind::Source);
428455
has_interface && !has_source
429456
}
430457

crates/rsscript-package-review/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ mod contract;
1212
mod dependency;
1313
mod diff;
1414
mod execution_facts;
15+
mod graph;
1516
mod lock;
1617
mod policy;
1718
mod review;
@@ -25,6 +26,7 @@ pub use contract::*;
2526
pub use dependency::*;
2627
pub use diff::*;
2728
pub use execution_facts::*;
29+
pub use graph::*;
2830
pub use lock::*;
2931
pub use policy::*;
3032
pub use review::*;

crates/rsscript-sdk/tests/architecture.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2069,6 +2069,23 @@ fn native_package_dependency_model_is_not_owned_by_aot_lowering() {
20692069
&& lock.contains("rsscript_project::project_path_source"),
20702070
"package lock must own hashing/comparison and receive native path resolution explicitly"
20712071
);
2072+
let graph_path = root.join("crates/rsscript-package-review/src/graph.rs");
2073+
assert!(
2074+
graph_path.is_file()
2075+
&& !root
2076+
.join("crates/rsscript-compiler/src/package/graph.rs")
2077+
.exists(),
2078+
"package graph evidence must be physically owned by the package-review boundary"
2079+
);
2080+
let graph = read(&graph_path);
2081+
assert!(
2082+
graph.contains("pub fn package_tree_captured")
2083+
&& graph.contains("pub fn check_package_graph")
2084+
&& graph.contains("NativeRustReviewFn")
2085+
&& graph.contains("rsscript_project::project_path_source")
2086+
&& !graph.contains("authorization::"),
2087+
"package graph evaluation must consume captured review input and receive native inspection explicitly"
2088+
);
20722089
assert!(
20732090
dependency.contains("load_package_from_manifest_source")
20742091
&& !dependency.contains("load_package_with_features("),
@@ -2083,6 +2100,12 @@ fn native_package_dependency_model_is_not_owned_by_aot_lowering() {
20832100
&& !package_module.contains("fn open_regular_file_within_root"),
20842101
"compiler compatibility code must consume project-owned generic I/O primitives instead of reimplementing bounded traversal"
20852102
);
2103+
assert!(
2104+
package_module.contains("rsscript_package_review::package_tree_captured")
2105+
&& package_module.contains("snapshot_package_graph_inputs")
2106+
&& package_module.contains("remap_tree"),
2107+
"compiler package graph compatibility must only authorize captured input and remap public paths"
2108+
);
20862109
}
20872110

20882111
#[test]

docs/architecture/adr/0225-own-captured-package-review-inputs-outside-compiler.md

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,15 +60,21 @@ are review-owned as well. The one native-sensitive operation is an explicit
6060
rooted-path resolver callback; compiler compatibility retains only that adapter
6161
and snapshot remapping rather than lock semantics or hashing implementation.
6262

63+
Package graph construction and graph-level review validation are also
64+
review-owned. They consume the project-captured manifest graph and receive the
65+
legacy native Rust inspection only as the same explicit callback. Compiler
66+
compatibility now only authorizes the captured input and remaps snapshot paths
67+
for its legacy public result.
68+
6369
The compiler's opt-in `package` compatibility feature has a private forwarding
6470
module during the staged migration so existing authorization, native, lock, and
6571
review callers retain their established behavior. The reviewed compiler default
6672
closure remains unchanged and does not select this crate.
6773

6874
## Consequences
6975

70-
This is the first physical S05.3 migration step, not its completion. Review
71-
execution, risk/policy, lock/check/diff, and public compatibility composition
72-
must move next before the forwarding module can be removed. Architecture tests
73-
assert that the source-set file cannot return under `rsscript-compiler` and
74-
that its loader continues to consume project-owned bounded capture APIs.
76+
This is a staged physical S05.3 migration. The remaining package check and
77+
final public compatibility composition must move before the forwarding module
78+
can be removed. Architecture tests assert that captured review implementation
79+
files cannot return under `rsscript-compiler`, while compiler compatibility
80+
continues to consume project-owned bounded capture APIs.

0 commit comments

Comments
 (0)