Summary
CellScript has rich compiled-program metadata, but it does not expose a stable machine-readable package resolve graph or the actual build-unit plan.
Today:
- cellc metadata compiles an entry and emits CompileMetadata for the program/artifact;
- cellc info serializes the declared manifest;
- cellc lock reports only node and environment counts;
- Cell.lock stores resolved source nodes, but consumers must reverse-engineer selection, scopes, feature roots, environment decisions, workspace roots, and build units.
These are different questions. IDEs, CI policy, security review, cache diagnostics, workspace scheduling, update planning, and external builders need a read-only canonical answer to: what will this command resolve and build?
Code evidence
- metadata is compile metadata, not package metadata:
|
fn metadata(args: MetadataArgs) -> Result<()> { |
|
let input_path = args.input.unwrap_or_else(|| PathBuf::from(".")); |
|
let input = Utf8Path::from_path(&input_path) |
|
.ok_or_else(|| crate::error::CompileError::without_span(format!("path '{}' is not valid UTF-8", input_path.display())))?; |
|
let options = CompileOptions { |
|
edition: crate::CURRENT_EDITION, |
|
opt_level: 0, |
|
output: None, |
|
debug: false, |
|
target: args.target, |
|
target_profile: args.target_profile, |
|
primitive_compat: None, |
|
}; |
|
let result = match compile_path(input, options.clone()) { |
|
Ok(result) => result, |
|
Err(error) => return Err(diagnostics_to_error(&compile_failure_diagnostics(input, options, error))), |
|
}; |
|
let json = serde_json::to_string_pretty(&result.metadata) |
|
.map_err(|error| crate::error::CompileError::without_span(format!("failed to serialize metadata: {}", error)))?; |
|
|
|
if let Some(output_path) = args.output.as_ref() { |
|
if let Some(parent) = output_path.parent() { |
|
std::fs::create_dir_all(parent)?; |
|
} |
|
std::fs::write(output_path, json)?; |
|
println!("{}", "Metadata generated".green()); |
|
println!(" Output: {}", output_path.display()); |
|
} else { |
|
println!("{}", json); |
|
} |
|
Ok(()) |
- lock JSON reports counts, not the graph:
|
fn lock(args: PackageLockArgs) -> Result<()> { |
|
refresh_lockfile_from_manifest(std::path::Path::new("."))?; |
|
let lockfile = Lockfile::read_from_root(std::path::Path::new("."))?.expect("lockfile was just written"); |
|
CommandOutcome { |
|
machine: serde_json::json!({ |
|
"status": "ok", |
|
"lockfile": "Cell.lock", |
|
"schema": lockfile.schema, |
|
"dependency_nodes": lockfile.dependencies.len(), |
|
"environments": lockfile.environments.keys().collect::<Vec<_>>(), |
|
}), |
|
human_lines: vec![ |
|
"Dependency graph locked".green().to_string(), |
|
format!(" {} dependency node(s)", lockfile.dependencies.len()), |
|
], |
|
} |
|
.emit(args.json) |
- info reports declarations, not resolved nodes:
|
fn info(args: InfoArgs) -> Result<()> { |
|
let pm = PackageManager::new("."); |
|
let manifest = pm.read_manifest()?; |
|
let mut human_lines = vec![ |
|
"Package Info:".bold().to_string(), |
|
format!(" Name: {}", manifest.package.name), |
|
format!(" Version: {}", manifest.package.version), |
|
format!(" Description: {}", manifest.package.description), |
|
format!(" License: {}", manifest.package.license), |
|
format!(" Authors: {}", manifest.package.authors.join(", ")), |
|
format!(" Entry: {}", manifest.package.entry), |
|
" Dependencies:".to_string(), |
|
]; |
|
human_lines.extend(manifest.dependencies.iter().map(|(name, dep)| format!(" - {}: {:?}", name, dep))); |
|
CommandOutcome { |
|
machine: serde_json::json!({ |
|
"status": "ok", |
|
"manifest": "Cell.toml", |
|
"package": manifest.package, |
|
"dependencies": manifest.dependencies, |
|
"dev_dependencies": manifest.dev_dependencies, |
|
"features": manifest.features, |
|
"environments": manifest.environments, |
|
"dependency_overrides": manifest.dependency_overrides, |
|
"resolvers": manifest.resolvers, |
|
"build": manifest.build, |
|
"policy": manifest.policy, |
|
"deploy": manifest.deploy, |
|
"metadata": manifest.metadata, |
|
}), |
|
human_lines, |
|
} |
|
.emit(args.json) |
- the incremental cache is one whole-entry content key; its unit reasons are not exposed: https://github.com/CellScript-Labs/CellScript/blob/8ae6dc4a2749fa107ff42c166772600648c19bb7/src/lib.rs#L7079-L7103
Reference architecture
Cargo exposes a versioned metadata schema containing packages, workspace members, the resolved graph, feature activation, dependency kinds/targets, and output roots:
https://github.com/rust-lang/cargo/blob/75d17360928f57ff2a7d2f2da1c753f5fe1926d1/src/ops/cargo_metadata.rs#L15-L100
Cargo separately models and can serialize compilation units keyed by package, target, profile, platform, mode, and features:
https://github.com/rust-lang/cargo/blob/75d17360928f57ff2a7d2f2da1c753f5fe1926d1/src/compiler/unit_graph.rs#L13-L150
Move package-alt separates a rooted PackageGraph from BuildPlan compilation:
https://github.com/MystenLabs/sui/blob/5a9f37431c473fa2f6d49abecbcc6a6d7190f533/external-crates/move/crates/move-package-alt/src/graph/mod.rs#L40-L89
https://github.com/MystenLabs/sui/blob/5a9f37431c473fa2f6d49abecbcc6a6d7190f533/external-crates/move/crates/move-package-alt-compilation/src/build_plan.rs
CellScript should expose its own smaller, CKB-aware model.
Proposed schemas
Resolve graph
A versioned read-only schema should include:
- root package or workspace member set;
- canonical package node IDs;
- package name, namespace, version, edition, compiler requirement;
- exact source kind and immutable source identity;
- source and manifest digests;
- incoming/outgoing aliases and edge kinds;
- runtime/test/build-reserved scope;
- requested and effective feature roots;
- selected CKB environment identity and dependency-local mapping;
- lock status and whether each value came from manifest, lock, or explicit CLI selection;
- unreachable/stale nodes and policy warnings.
Build-unit plan
A separate versioned schema should include:
- selected entry/action/lock;
- package node;
- target and artifact format;
- target profile, VM/ABI/codec identities, compatibility profile;
- optimization/debug/primitive policy;
- direct unit dependencies;
- expected outputs and sidecars;
- cache key/fingerprint inputs;
- cached/up-to-date/stale status with a stable reason;
- production gate/policy requirements.
The graph must be deterministic and path-normalized, but local paths need not be hidden when the user explicitly requests local diagnostic output.
Command contract
Do not overload the existing compiled-program metadata schema. Add a distinct command family or explicit mode.
Read-only graph inspection must:
- never update Cell.lock;
- never invoke an external resolver in locked/frozen mode;
- clearly mark when an unlocked query would need network or mutable resolution;
- support JSON schema version negotiation;
- produce stable opaque node IDs;
- filter by package, environment, features, scope, target/profile, and entry without changing their meaning.
Dependencies and sequencing
The schema can be designed in parallel, but it must not stabilize the current inconsistent resolver behavior as a public API. Production implementation depends on #17 for chain-identity-safe environment selection and on #15/#16/#18 for canonical workspace, package-instance, and compiler-requirement semantics. The first stable fixture must be generated from those corrected semantics, not from a compatibility snapshot of today's member loop.
Acceptance criteria
Non-goals
- exposing internal Rust structs as an accidental public API;
- replacing typed semantic, ProofPlan, or verified artifact metadata;
- making an inspection command perform an update;
- promising Cargo-compatible JSON.
Summary
CellScript has rich compiled-program metadata, but it does not expose a stable machine-readable package resolve graph or the actual build-unit plan.
Today:
These are different questions. IDEs, CI policy, security review, cache diagnostics, workspace scheduling, update planning, and external builders need a read-only canonical answer to: what will this command resolve and build?
Code evidence
CellScript/src/cli/commands.rs
Lines 2024 to 2054 in 8ae6dc4
CellScript/src/cli/commands.rs
Lines 4461 to 4477 in 8ae6dc4
CellScript/src/cli/commands.rs
Lines 4480 to 4512 in 8ae6dc4
Reference architecture
Cargo exposes a versioned metadata schema containing packages, workspace members, the resolved graph, feature activation, dependency kinds/targets, and output roots:
https://github.com/rust-lang/cargo/blob/75d17360928f57ff2a7d2f2da1c753f5fe1926d1/src/ops/cargo_metadata.rs#L15-L100
Cargo separately models and can serialize compilation units keyed by package, target, profile, platform, mode, and features:
https://github.com/rust-lang/cargo/blob/75d17360928f57ff2a7d2f2da1c753f5fe1926d1/src/compiler/unit_graph.rs#L13-L150
Move package-alt separates a rooted PackageGraph from BuildPlan compilation:
https://github.com/MystenLabs/sui/blob/5a9f37431c473fa2f6d49abecbcc6a6d7190f533/external-crates/move/crates/move-package-alt/src/graph/mod.rs#L40-L89
https://github.com/MystenLabs/sui/blob/5a9f37431c473fa2f6d49abecbcc6a6d7190f533/external-crates/move/crates/move-package-alt-compilation/src/build_plan.rs
CellScript should expose its own smaller, CKB-aware model.
Proposed schemas
Resolve graph
A versioned read-only schema should include:
Build-unit plan
A separate versioned schema should include:
The graph must be deterministic and path-normalized, but local paths need not be hidden when the user explicitly requests local diagnostic output.
Command contract
Do not overload the existing compiled-program metadata schema. Add a distinct command family or explicit mode.
Read-only graph inspection must:
Dependencies and sequencing
The schema can be designed in parallel, but it must not stabilize the current inconsistent resolver behavior as a public API. Production implementation depends on #17 for chain-identity-safe environment selection and on #15/#16/#18 for canonical workspace, package-instance, and compiler-requirement semantics. The first stable fixture must be generated from those corrected semantics, not from a compatibility snapshot of today's member loop.
Acceptance criteria
Non-goals