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
6 changes: 6 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,12 @@ request body or final report.
- Avoid ceremonial documentation. Update shelves because the contract changed,
not because a path changed.

## Durable Decision Discipline

Follow the canonical [durable decision policy](docs/topics/documentation/README.md#durable-decision-discipline).
Important decisions are incomplete until the owning repository document is
current in the same change; chat, memory, and PR prose do not replace that owner.

## RED/GREEN Testing Discipline

Edict uses RED/GREEN test-driven development for nontrivial changes. The shared
Expand Down
17 changes: 16 additions & 1 deletion ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ current branch; it is not a future package plan.

## Workspace Shape

The Rust workspace has five members:
The Rust workspace has six members:

```text
flyingrobots-edict -> edict-syntax
edict-cli -> edict-syntax
xtask -> edict-syntax
edict-provider-schema -> edict-syntax
Expand All @@ -26,6 +27,20 @@ runtime without exposing Wasmtime types through Edict contracts.

## Crates

### `flyingrobots-edict`

`flyingrobots-edict` exposes the Rust library name `edict`. It is the curated
public facade for source checking, stable diagnostic classifications, and
canonical Core, Target IR, and result-projection artifact identity operations.
The [public Rust API topic](docs/topics/public-rust-api/README.md) owns this
boundary. The facade deliberately does not re-export the implementation crate's
module tree.

The package remains `publish = false`. Its presence defines and tests the
intended public Rust boundary; it neither authorizes nor claims crates.io
publication. The CLI remains the complete application-build and JSONL process
boundary.

### `edict-syntax`

`edict-syntax` is the implementation crate for more than syntax. Its public name
Expand Down
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ versions still track specification maturity rather than a released product.

### Changed

- Reused release preparation's calendar validator in the policy structural
guard, rejecting impossible target dates in manually edited policy blocks.
- Completed the Rust facade value-model exports for Core, Target IR, result
projections, decoded canonical values, and diagnostic spans, allowing
consumers to construct artifact inputs and inspect operation results.
- Kept the Rust facade package version, exact implementation dependency, and
lockfile entry synchronized during release preparation, so the prepared
workspace resolves without repairing its lockfile.
- Parsed release-policy fields as TOML values so comments and string contents
cannot impersonate required scope/non-goal lists. Reconciliation and the
structural guard now consume the same parsed string arrays.
Expand Down Expand Up @@ -192,6 +200,13 @@ versions still track specification maturity rather than a released product.

### Added

- Added the non-publishing `flyingrobots-edict` package with the Rust library
name `edict`. The curated facade exposes source checking, stable diagnostic
classifications, and canonical Core, Target IR, and result-projection
artifact identity operations without making the implementation crate's
module tree part of the recommended public API. The package remains
`publish = false`; registry naming, publication, and release authorization
are explicitly outside this change.
- Added explicit imported `Nominal<T>` lawpack contracts. Nominal contracts
preserve exact and ranged bounded-byte storage representations in Core while
rejecting cross-assignment between distinct contract coordinates before
Expand Down
8 changes: 8 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
[workspace]
members = [
"crates/edict",
"crates/edict-cli",
"crates/edict-provider-host-wasmtime",
"crates/edict-provider-schema",
Expand Down
13 changes: 8 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -517,11 +517,12 @@ input. The full stream contract and JSON Schemas are in the

### Using the library

`edict-syntax` is the front end. The one-call entry point parses and
surface-validates a source string:
The curated `edict` facade is the recommended Rust entry point. Its one-call
check parses and surface-validates a source string without exposing the
implementation crate's module tree:

```rust
use edict_syntax::{check, CheckOutcome};
use edict::{check, CheckOutcome};

match check("package examples.hello@1;\n") {
CheckOutcome::Valid => println!("ok"),
Expand All @@ -530,8 +531,10 @@ match check("package examples.hello@1;\n") {
}
```

The underlying stages (`parse_module` then `validate_surface`) remain available
when you need the parsed module.
The implementation crate retains lower-level stages for repository-internal
consumers that need the parsed module. The curated facade remains
`publish = false`; the current release-engineering work does not authorize or
claim crates.io publication.

---

Expand Down
23 changes: 23 additions & 0 deletions crates/edict/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[package]
name = "flyingrobots-edict"
version = "0.11.0-alpha.1"
description = "Curated public Rust facade for the Edict compiler and canonical artifacts"
readme = "README.md"
edition.workspace = true
rust-version.workspace = true
license.workspace = true
repository.workspace = true
publish = false
include = ["src/**", "tests/**", "README.md"]

[lib]
name = "edict"

[dependencies]
edict-syntax = { path = "../edict-syntax", version = "=0.11.0-alpha.1" }
Comment thread
flyingrobots marked this conversation as resolved.

[dev-dependencies]
serde_json = "1"

[lints]
workspace = true
24 changes: 24 additions & 0 deletions crates/edict/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Edict for Rust

`flyingrobots-edict` exposes the Rust library name `edict`. It is the curated
facade for Edict source checking, stable diagnostic classifications, and
canonical semantic-artifact identity operations.

The complete application-build boundary remains the JSONL `edict` CLI. This
facade deliberately does not expose the implementation crate's full module
tree.

```rust
use edict::{check, CheckOutcome};

assert_eq!(
check("package examples.public_facade@1;\n"),
CheckOutcome::Valid
);
```

## Publication status

This package currently has `publish = false`. Its archive and dependency
closure are under release-engineering review. Nothing in this package grants
permission to publish it to crates.io.
53 changes: 53 additions & 0 deletions crates/edict/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//! Curated public Rust facade for Edict.
//!
//! The [`check`] entry point is the supported one-call source check. Stable
//! failure classifications are grouped under [`diagnostic`], while canonical
//! semantic-artifact identity operations are grouped under [`artifact`]. The
//! implementation crate's broad module tree is intentionally not re-exported.
//!
//! ```
//! use edict::{check, CheckOutcome};
//!
//! assert_eq!(
//! check("package examples.public_facade@1;\n"),
//! CheckOutcome::Valid
//! );
//! ```
//!
//! Implementation modules are not part of this facade:
//!
//! ```compile_fail
//! use edict::parser::parse_module;
//! ```

pub use edict_syntax::{check, CheckOutcome};

/// Stable machine-usable failure classifications exposed by the public
/// facade.
pub mod diagnostic {
pub use edict_syntax::{
CanonicalError, CanonicalErrorKind, CompilerError, CompilerErrorKind, CompilerStage,
ParseError, ParseErrorKind, ResultProjectionFailure, ResultProjectionFailureKind,
SemanticError, SemanticErrorKind, Span, TargetLoweringFailure, TargetLoweringFailureKind,
};
}

/// Canonical semantic-artifact values, encoders, and domain-framed identity
/// operations. Nested value models are exported so callers can construct
/// candidates and inspect results without importing implementation modules.
pub mod artifact {
pub use edict_syntax::{
decode_canonical_cbor, decode_result_projection, digest_core_module,
digest_result_projection, digest_target_ir_artifact, encode_core_module,
encode_result_projection, encode_target_ir_artifact, verify_result_projection,
Comment thread
flyingrobots marked this conversation as resolved.
CanonicalError, CanonicalErrorKind, CanonicalValue, CompareOp, CoreBlock, CoreBound,
CoreBudget, CoreDigest, CoreExpr, CoreExternalActionBudget, CoreImport, CoreImportKind,
CoreIntent, CoreModule, CoreNode, CoreObstructionArm, CoreObstructionReason, CorePredicate,
CoreRequireFailureArm, CoreType, CoreValue, InputConstraint, InputConstraintSource,
LocalRef, ResourceRef, ResultProjection, ResultProjectionArtifact, ResultProjectionExpr,
ResultProjectionFailure, ResultProjectionFailureKind, ResultProjectionSource,
TargetIrArtifact, TargetIrExternalActionRequest, TargetIrIntent, TargetIrPureBinding,
TargetIrRequireFailure, TargetIrRequirement, TargetIrSemanticClosure, TargetIrStep,
VerifiedResultProjection,
};
}
146 changes: 146 additions & 0 deletions crates/edict/tests/artifact_models.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
//! Separate consumer crate: every Edict name comes from the curated facade.
use std::collections::BTreeMap;

use edict::{
artifact::{
decode_canonical_cbor, decode_result_projection, digest_core_module,
digest_result_projection, digest_target_ir_artifact, encode_core_module,
encode_result_projection, encode_target_ir_artifact, verify_result_projection,
CanonicalValue, CoreBlock, CoreBudget, CoreExpr, CoreIntent, CoreModule, CoreType,
LocalRef, ResourceRef, ResultProjection, ResultProjectionExpr, ResultProjectionSource,
TargetIrArtifact, TargetIrIntent, TargetIrSemanticClosure, VerifiedResultProjection,
},
check,
diagnostic::Span,
CheckOutcome,
};

fn core_fixture() -> CoreModule {
let input = LocalRef {
id: "arg.0".to_owned(),
alpha_name: "input".to_owned(),
ty: "examples.facade@1.Input".to_owned(),
};
let result = CoreExpr::Local {
reference: input.clone(),
};
CoreModule {
api_version: "edict.core/v1".to_owned(),
coordinate: "examples.facade@1".to_owned(),
imports: Vec::new(),
types: BTreeMap::from([(
"Input".to_owned(),
CoreType::Record {
fields: BTreeMap::from([("ok".to_owned(), "Bool".to_owned())]),
},
)]),
intents: BTreeMap::from([(
"echo".to_owned(),
CoreIntent {
input: input.ty.clone(),
output: input.ty.clone(),
required_operation_profile: "continuum.profile.read-only/v1".to_owned(),
basis: Some(result.clone()),
input_constraints: Vec::new(),
core_evaluation_budget: CoreBudget {
max_steps: 8,
max_allocated_bytes: 256,
max_output_bytes: 64,
},
body: CoreBlock {
locals: vec![input],
nodes: Vec::new(),
result,
},
},
)]),
required_core_capabilities: Vec::new(),
}
}

fn target_fixture(core: &CoreModule) -> TargetIrArtifact {
let intent = &core.intents["echo"];
TargetIrArtifact {
domain: "echo.span-ir/v1".to_owned(),
target_profile: ResourceRef {
coordinate: "echo.dpo@1".to_owned(),
digest: Some(format!("sha256:{}", "1".repeat(64))),
},
source_core_coordinate: core.coordinate.clone(),
semantic_closure: Some(TargetIrSemanticClosure {
source_core: ResourceRef {
coordinate: core.coordinate.clone(),
digest: Some(
digest_core_module(core)
.expect("Core digest")
.to_review_string(),
),
},
lawpacks: Vec::new(),
capabilities: Vec::new(),
}),
intents: BTreeMap::from([(
"echo".to_owned(),
TargetIrIntent {
operation_profile: intent.required_operation_profile.clone(),
basis: intent.basis.clone(),
input_constraints: intent.input_constraints.clone(),
core_evaluation_budget: intent.core_evaluation_budget.clone(),
pure_bindings: Vec::new(),
requirements: Vec::new(),
steps: Vec::new(),
external_action_requests: Vec::new(),
result: intent.body.result.clone(),
},
)]),
}
}

#[test]
fn facade_consumer_constructs_and_verifies_artifacts() {
let core = core_fixture();
let target = target_fixture(&core);
let projection = ResultProjection {
api_version: "edict.result-projection/v1".to_owned(),
operation_coordinate: "examples.facade@1.echo".to_owned(),
output_type: "examples.facade@1.Input".to_owned(),
max_output_bytes: 64,
expression: ResultProjectionExpr::Source {
source: ResultProjectionSource::ApplicationInput,
path: Vec::new(),
},
};

let core_bytes = encode_core_module(&core).expect("encode consumer Core");
let target_bytes = encode_target_ir_artifact(&target).expect("encode consumer Target IR");
let projection_bytes = encode_result_projection(&projection).expect("encode projection");
let decoded: CanonicalValue = decode_canonical_cbor(&core_bytes).expect("decode Core value");
assert!(matches!(decoded, CanonicalValue::Map(_)));
assert!(matches!(
decode_canonical_cbor(&target_bytes).expect("decode Target IR value"),
CanonicalValue::Map(_)
));
assert_eq!(
decode_result_projection(&projection_bytes).expect("decode projection"),
projection
);
let projection_digest = digest_result_projection(&projection).expect("projection digest");
let verified: VerifiedResultProjection =
verify_result_projection(&core, &target, "echo", &projection_bytes, projection_digest)
.expect("independently verify consumer projection");
assert_eq!(verified.projection(), &projection);
assert_eq!(verified.digest(), projection_digest);
assert_ne!(
digest_core_module(&core).expect("Core identity"),
digest_target_ir_artifact(&target).expect("Target IR identity")
);
}

#[test]
fn facade_consumer_names_diagnostic_spans() {
let CheckOutcome::ParseFailed(error) = check("package ;") else {
panic!("malformed package must fail parsing");
};
let span: Span = error.span;
assert_eq!(span, Span { start: 8, end: 9 });
}
Loading