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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,5 @@ jobs:
- run: cargo clippy --workspace --all-targets --all-features -- -D warnings
- run: cargo test --workspace --all-features --locked
- run: cargo build --workspace --release --locked
- run: scripts/ndf check --root tests/fixtures/markdown/project --format json
- run: scripts/ndf check --root tests/fixtures/cli-smoke/project --format json
- run: git diff --check
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@ All notable changes to NDF are documented here.
- JSON workflows for build, check, show, trace, coverage, dependency reports,
semantic diff, export, ID allocation, and safe fixes.
- Exact dependency locking and no-regression diagnostic baselines.
- Read-only graph dependencies plus `conforms-to` and expected-absent `tracks`
relationships for cross-project contracts.
- Stable PTO instruction identities with indexed encoding, semantics, legality,
fault, completion, architectural-state, and memory-ordering facets.
- Deterministic PTO release impact analysis and the `ndf impact pto-release`
command, including complete conformance/watch inventories and transitive
consumer impact.
- Cross-project diagnostics `NDF-XREF-001` through `NDF-XREF-004` and PTO
instruction identity collision diagnostic `NDF-ASL-005`.

### Changed

Expand All @@ -22,6 +31,9 @@ All notable changes to NDF are documented here.
`status`.
- SQLite is defined as derived, disposable state rather than an authored
authority.
- Dependency graphs remain separate namespaces and are never merged into a
consumer index; every graph dependency is loaded only from an exact, clean
Git revision.

### Compatibility

Expand All @@ -31,3 +43,5 @@ All notable changes to NDF are documented here.
PTO adapter.
- NDF has no supported Python package, import API, or PyO3 compatibility layer.
- Format, IR, JSON output, CLI, and plugin surfaces remain experimental.
- This release does not change format 0.2, canonical IR 0.1, JSON envelope
schema 0.1, SQLite schema v1, or the independently versioned plugin API.
54 changes: 54 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,58 @@ workflows. `ndf-cli` is the only supported automation interface. NDF ships no
Python package or Python bindings; downstream tools invoke the versioned JSON
CLI from an exact Git pin.

### PTO release impact

Compare two immutable PTO indexes against one consumer index with:

```bash
ndf impact pto-release \
--before pto-before.sqlite \
--after pto-after.sqlite \
--consumer-index davincioo.sqlite \
--format json
```

The two PTO indexes are authoritative release snapshots. The consumer index is
the authoritative inventory of `conforms-to` relationships and
`expected=absent` `tracks` watches. The command only reads these three indexes;
it does not edit sources, indexes, lock files, or dependency checkouts.

The result is `compatible` when changes do not affect a consumer contract,
`review-required` for moved or superseded targets and newly present watched
identities, and `breaking` when a conformance target is removed or semantically
modified. The report includes direct and transitively related consumers in a
deterministic order so CI and release tooling can compare its JSON byte for
byte.

### Cross-project conformance

Declare an external graph only when document relationships must resolve into
that dependency:

```yaml
dependencies:
pto-spec:
path: deps/pto-spec
graph: true
```

The matching `ndf.lock` entry must name the dependency project and its full
40-character Git commit. NDF verifies that the checkout is exact and clean,
loads its graph read-only, and keeps its nodes out of the consumer index.

Consumer Markdown can then use a stable external URI:

```markdown
<!-- ndf: kind=requirement modality=must refinement=L1 status=active conforms-to=ndf://pto-spec/PTO-INST-TILE-TLOAD -->
```

Use `tracks=ndf://... expected=absent` when a consumer intentionally watches
for an upstream identity that does not exist yet. `NDF-XREF-001` reports an
invalid watch contract, `NDF-XREF-002` a missing conformance target,
`NDF-XREF-003` an undeclared graph namespace, and `NDF-XREF-004` a watched
identity that has appeared.

## Documentation

- [English specification](normative_language.md)
Expand All @@ -37,3 +89,5 @@ CLI from an exact Git pin.
Format 0.2, canonical IR 0.1, JSON output schema 0.1, CLI spelling, and the
plugin API are experimental. Downstream repositories consume NDF from an exact
Git commit recorded in `ndf.lock`; CI must not fetch an unpinned branch.
This cross-project release keeps format 0.2, canonical IR 0.1, JSON envelope
schema 0.1, SQLite schema v1, and the plugin API version unchanged.
21 changes: 21 additions & 0 deletions crates/ndf-cli/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ impl Arguments {
ReportCommand::Dependencies { .. } => "report dependencies",
},
Some(Command::Diff { .. }) => "diff",
Some(Command::Impact { command }) => match command {
ImpactCommand::PtoRelease { .. } => "impact pto-release",
},
Some(Command::Export { .. }) => "export",
Some(Command::Id { .. }) => "id allocate",
Some(Command::Fix { .. }) => "fix",
Expand Down Expand Up @@ -73,6 +76,10 @@ pub enum Command {
#[arg(long, value_enum, default_value_t = JsonFormat::Json)]
format: JsonFormat,
},
Impact {
#[command(subcommand)]
command: ImpactCommand,
},
Export {
#[arg(long)]
index: PathBuf,
Expand All @@ -94,6 +101,20 @@ pub enum Command {
},
}

#[derive(Debug, Subcommand)]
pub enum ImpactCommand {
PtoRelease {
#[arg(long)]
before: PathBuf,
#[arg(long)]
after: PathBuf,
#[arg(long)]
consumer_index: PathBuf,
#[arg(long, value_enum, default_value_t = JsonFormat::Json)]
format: JsonFormat,
},
}

#[derive(Debug, Subcommand)]
pub enum ReportCommand {
Coverage {
Expand Down
29 changes: 25 additions & 4 deletions crates/ndf-cli/src/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,23 @@ use std::process::Command as ProcessCommand;

use ndf_compiler::CompilerError;
use ndf_compiler::adapters::parse_project;
use ndf_compiler::dependencies::resolve_dependencies;
use ndf_compiler::dependencies::{load_dependency_graphs, resolve_dependencies};
use ndf_compiler::diff::semantic_diff;
use ndf_compiler::export::export_graph;
use ndf_compiler::fix::{FixMode, markdown_paths, safe_fix};
use ndf_compiler::id_allocator::allocate_id;
use ndf_compiler::index::{BuildProvenance, NdfIndex, build_index};
use ndf_compiler::manifest::{ProjectLock, ProjectManifest};
use ndf_compiler::pto_impact::pto_release_impact;
use ndf_compiler::query::trace;
use ndf_compiler::report::{CoveragePolicy, coverage};
use ndf_compiler::rules::{ValidationPolicy, validate_graph};
use ndf_compiler::rules::{ValidationPolicy, validate_project_graph};
use ndf_core::identity::NodeId;
use ndf_core::model::{Diagnostic, DiagnosticSeverity, Graph};
use ndf_core::version::{FORMAT_VERSION, IR_VERSION, TOOL_VERSION};
use serde_json::{Value, json};

use crate::args::{Arguments, Command, IdCommand, ReportCommand};
use crate::args::{Arguments, Command, IdCommand, ImpactCommand, ReportCommand};
use crate::envelope::Envelope;

pub fn run(arguments: Arguments) -> Result<Envelope, CompilerError> {
Expand Down Expand Up @@ -115,6 +116,24 @@ pub fn run(arguments: Arguments) -> Result<Envelope, CompilerError> {
Envelope::success("diff", semantic_diff(&before, &after)?, Vec::new())
.map_err(Into::into)
}
Some(Command::Impact { command }) => match command {
ImpactCommand::PtoRelease {
before,
after,
consumer_index,
format: _,
} => {
let before = NdfIndex::open(&before)?;
let after = NdfIndex::open(&after)?;
let consumer = NdfIndex::open(&consumer_index)?;
Envelope::success(
"impact pto-release",
pto_release_impact(&before, &after, &consumer)?,
Vec::new(),
)
.map_err(Into::into)
}
},
Some(Command::Export {
index,
output,
Expand Down Expand Up @@ -182,8 +201,10 @@ pub fn run(arguments: Arguments) -> Result<Envelope, CompilerError> {

fn project_graph(root: &Path) -> Result<(ProjectManifest, Graph), CompilerError> {
let manifest = ProjectManifest::load(&root.join("ndf.yaml"))?;
let lock = ProjectLock::load(&root.join("ndf.lock"))?;
let dependencies = load_dependency_graphs(&manifest, &lock, root)?;
let parsed = parse_project(&manifest)?;
let diagnostics = validate_graph(&parsed, &ValidationPolicy::default());
let diagnostics = validate_project_graph(&parsed, &dependencies, &ValidationPolicy::default());
Ok((
manifest,
Graph {
Expand Down
Loading
Loading